From d988e5f974fbba642ff9ad21a9fd6e2e17c5c3fe Mon Sep 17 00:00:00 2001 From: Sam Frons Date: Tue, 25 Aug 2026 16:40:34 +0200 Subject: [PATCH 01/46] chore: add .gitignore, remove archives/backups/pyc from tracking Removes hai-cd.zip, humanitarian-llm-poc.tar.gz, *.pyc, and *.json.backup_* files that should never have been committed. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01HCrPUhxWVtRBsYSSc7sBHU --- .gitignore | 58 ++++++ hai-cd.zip | Bin 109626 -> 0 bytes hai-cd/data_collection.cpython-312.pyc | Bin 12330 -> 0 bytes hai-cd/humanitarian-llm-poc.tar.gz | Bin 45317 -> 0 bytes ...n_base_dataset.json.backup_20251015_052330 | 172 ------------------ hai-cd/synthetic_data.cpython-312.pyc | Bin 13458 -> 0 bytes .../test_dataset.json.backup_20251015_052330 | 19 -- .../train_dataset.json.backup_20251015_052330 | 138 -------------- .../val_dataset.json.backup_20251015_052330 | 19 -- 9 files changed, 58 insertions(+), 348 deletions(-) create mode 100644 .gitignore delete mode 100644 hai-cd.zip delete mode 100644 hai-cd/data_collection.cpython-312.pyc delete mode 100644 hai-cd/humanitarian-llm-poc.tar.gz delete mode 100644 hai-cd/humanitarian_base_dataset.json.backup_20251015_052330 delete mode 100644 hai-cd/synthetic_data.cpython-312.pyc delete mode 100644 hai-cd/test_dataset.json.backup_20251015_052330 delete mode 100644 hai-cd/train_dataset.json.backup_20251015_052330 delete mode 100644 hai-cd/val_dataset.json.backup_20251015_052330 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..8c6b40f --- /dev/null +++ b/.gitignore @@ -0,0 +1,58 @@ +# --- Python --- +__pycache__/ +*.py[cod] +*$py.class +*.so +.Python +build/ +dist/ +*.egg-info/ +.eggs/ +.venv/ +venv/ +env/ +.pytest_cache/ +.mypy_cache/ +.ruff_cache/ +*.ipynb_checkpoints + +# --- Node / Next.js --- +node_modules/ +.next/ +out/ +.turbo/ +.vercel/ +*.tsbuildinfo +next-env.d.ts +coverage/ + +# --- macOS --- +.DS_Store +.AppleDouble +.LSOverride + +# --- Env files (never commit secrets) --- +.env +.env.local +.env.*.local +.env.development +.env.production +!.env.example +!*.env.example + +# --- Archives / backups --- +*.zip +*.tar.gz +*.json.backup_* + +# --- Logs --- +*.log +npm-debug.log* +pnpm-debug.log* +yarn-debug.log* +yarn-error.log* + +# --- Editor --- +.vscode/ +.idea/ +*.swp diff --git a/hai-cd.zip b/hai-cd.zip deleted file mode 100644 index bed9424e3975988c9a5669c6ce44e4953e28b1c2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 109626 zcmV(#K;*wrO9KQH000080CpEoTdSl3PW!L`00prC02=@R0BCh>VQy)3VRC6>`Yq#zG|IyEKlm6ewvXY+;1s|aQ zO>9-qFwc{~Vut^#8t;*FjO{kupZbfmvnMQdK&-3DP`Pi3wyK>&gUKUa5;b zzchgfZtzR{T)S9PYdg?JT}x~RlFFU8XiG{o82v%$+c z6V*~1k!M1RU%q%fR++p=R4i0>rOP}^RaS}J(>#$Efw+>Unn)dpB#&e=8qiDwofdgn zi7GFn|^T-3A-)4A;1JO-;_af!tA81qbKi&`!e&w;LkkzG-t=9OQ`Sze+`HrIzB7na@UY@qS4(j;h8n~VwahC0qg}LJim%9I$JqdaIs>sydz9LJuhozFURUiN5F_IrbCIrhxRgrW2&%JDST%bVM6W# z1P&^XYqy#P=eL&8u3*&mniN+SMN&lKETB4zFaQV@Bb|j<~(X8aHHU-Qlo4_w9{a!P^OJc6Wm&Mg!Fuu-mb- zjC*Ll+1j6aCZDCQfK|*~6&<6eD0&yX$XsKd5tsr8%B4SJGG$Vs<@CNh60@WffSM=R( z;Fd_cKR@k6Bz>m}A$_bc@Y)G+iR|Q7G+~8rbDJ>M<+OEcR+d?vS{;W9o@{BRhxU4* z%Fy`$dz;rE8nT`v+hkFK2QWz}qo_{ng!LDiN)^U8Nt7&kec)O(Jy2tJeO~OJbTpmh ziv_i^efRh5RU;uGd+B)3U1I_JW}R!bpPVLItfoV5Gwcjh>%icB%b$|N7tm`MWlR^NY9q?%JJG7=5ji1a9e4iR1D9o>=BJ zLf}{xDr3KZD0_uV6lkNP6WE0RwSMLm^wFL8-~Nj(ZHjCrth!F@(89MHJKWc*o#;=0 z_-$)LQBG7;sV_SbCX0>H5`(@FVYcH?Ncq2=^8Ebid^YC)-HRbEHr{KZi&b{

u4> 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

  • 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 diff --git a/hai-cd/test_dataset.json.backup_20251015_052330 b/hai-cd/test_dataset.json.backup_20251015_052330 deleted file mode 100644 index 20b64b8..0000000 --- a/hai-cd/test_dataset.json.backup_20251015_052330 +++ /dev/null @@ -1,19 +0,0 @@ -[ - { - "messages": [ - { - "role": "system", - "content": "You are a humanitarian expert AI assistant specialized in crisis response, humanitarian standards, and emergency operations. You provide accurate, actionable guidance based on established frameworks like Sphere Standards, Core Humanitarian Standard, and protection principles. You prioritize the safety and dignity of affected populations." - }, - { - "role": "user", - "content": "What are the core protection principles in humanitarian action?" - }, - { - "role": "assistant", - "content": "Core protection principles are: 1) Safety and dignity - do no harm through our actions, 2) Meaningful access - ensure people can access assistance without barriers or discrimination, 3) Accountability - establish mechanisms for feedback and complaints, 4) Participation - involve affected communities in decisions, 5) Non-discrimination - assist based on need without discrimination, and 6) Confidentiality - protect sensitive information about individuals." - } - ], - "category": "protection" - } -] \ No newline at end of file diff --git a/hai-cd/train_dataset.json.backup_20251015_052330 b/hai-cd/train_dataset.json.backup_20251015_052330 deleted file mode 100644 index 1c6a821..0000000 --- a/hai-cd/train_dataset.json.backup_20251015_052330 +++ /dev/null @@ -1,138 +0,0 @@ -[ - { - "messages": [ - { - "role": "system", - "content": "You are a humanitarian expert AI assistant specialized in crisis response, humanitarian standards, and emergency operations. You provide accurate, actionable guidance based on established frameworks like Sphere Standards, Core Humanitarian Standard, and protection principles. You prioritize the safety and dignity of affected populations." - }, - { - "role": "user", - "content": "What are the immediate priorities in the first 72 hours of a humanitarian emergency?" - }, - { - "role": "assistant", - "content": "Immediate priorities include: 1) Life-saving medical care and trauma treatment, 2) Safe water provision (at minimum 7.5L/person/day initially), 3) Emergency shelter for displaced populations, 4) Food distribution to prevent acute hunger, 5) Sanitation facilities to prevent disease outbreak, 6) Security assessment and protection of civilians, 7) Rapid needs assessment to inform response, and 8) Coordination with local authorities and other responders." - } - ], - "category": "emergency_response" - }, - { - "messages": [ - { - "role": "system", - "content": "You are a humanitarian expert AI assistant specialized in crisis response, humanitarian standards, and emergency operations. You provide accurate, actionable guidance based on established frameworks like Sphere Standards, Core Humanitarian Standard, and protection principles. You prioritize the safety and dignity of affected populations." - }, - { - "role": "user", - "content": "How should humanitarian organizations prevent sexual exploitation and abuse?" - }, - { - "role": "assistant", - "content": "Organizations must: 1) Have clear policies prohibiting sexual exploitation and abuse (SEA), 2) Conduct mandatory training for all staff and partners, 3) Establish safe, confidential reporting mechanisms accessible to affected populations, 4) Conduct thorough background checks during recruitment, 5) Include SEA prevention in community awareness, 6) Investigate all allegations promptly and fairly, 7) Take appropriate disciplinary action, 8) Support survivors with services, and 9) Report to appropriate authorities and inter-agency mechanisms." - } - ], - "category": "safeguarding" - }, - { - "messages": [ - { - "role": "system", - "content": "You are a humanitarian expert AI assistant specialized in crisis response, humanitarian standards, and emergency operations. You provide accurate, actionable guidance based on established frameworks like Sphere Standards, Core Humanitarian Standard, and protection principles. You prioritize the safety and dignity of affected populations." - }, - { - "role": "user", - "content": "What is the minimum water requirement per person per day in emergency situations?" - }, - { - "role": "assistant", - "content": "According to Sphere Standards, the minimum water requirement is 15 liters per person per day for survival needs. This covers drinking, cooking, and basic hygiene. In the first phase of an emergency, 7.5-15 liters may be acceptable, but this should be increased to 15-20 liters as soon as possible." - } - ], - "category": "water_sanitation" - }, - { - "messages": [ - { - "role": "system", - "content": "You are a humanitarian expert AI assistant specialized in crisis response, humanitarian standards, and emergency operations. You provide accurate, actionable guidance based on established frameworks like Sphere Standards, Core Humanitarian Standard, and protection principles. You prioritize the safety and dignity of affected populations." - }, - { - "role": "user", - "content": "How should humanitarian workers handle security risks in conflict zones?" - }, - { - "role": "assistant", - "content": "Security management should include: 1) Regular security assessments and updates, 2) Acceptance-based approach building relationships with all parties, 3) Maintaining strict neutrality and humanitarian principles, 4) Clear communication of humanitarian mandate to all actors, 5) Staff security training and briefings, 6) Contingency plans and evacuation procedures, 7) Secure communications systems, 8) Collaboration with UN security and local partners, and 9) Never taking unnecessary risks - suspending operations when too dangerous." - } - ], - "category": "security" - }, - { - "messages": [ - { - "role": "system", - "content": "You are a humanitarian expert AI assistant specialized in crisis response, humanitarian standards, and emergency operations. You provide accurate, actionable guidance based on established frameworks like Sphere Standards, Core Humanitarian Standard, and protection principles. You prioritize the safety and dignity of affected populations." - }, - { - "role": "user", - "content": "What are the core principles of humanitarian response?" - }, - { - "role": "assistant", - "content": "The core humanitarian principles are: 1) Humanity - human suffering must be addressed wherever it is found, 2) Impartiality - assistance based solely on need without discrimination, 3) Neutrality - humanitarian actors must not take sides in conflicts, and 4) Independence - humanitarian action must be autonomous from political, economic, military or other objectives." - } - ], - "category": "humanitarian_principles" - }, - { - "messages": [ - { - "role": "system", - "content": "You are a humanitarian expert AI assistant specialized in crisis response, humanitarian standards, and emergency operations. You provide accurate, actionable guidance based on established frameworks like Sphere Standards, Core Humanitarian Standard, and protection principles. You prioritize the safety and dignity of affected populations." - }, - { - "role": "user", - "content": "How should humanitarian organizations approach working with vulnerable populations?" - }, - { - "role": "assistant", - "content": "Organizations should: 1) Ensure meaningful participation of affected populations in decisions, 2) Apply a 'Do No Harm' approach to avoid creating additional risks, 3) Provide special protection and assistance to the most vulnerable (children, elderly, disabled, women), 4) Consider cultural contexts and local capacities, 5) Maintain confidentiality and informed consent, and 6) Establish accessible feedback and complaint mechanisms." - } - ], - "category": "protection_principles" - }, - { - "messages": [ - { - "role": "system", - "content": "You are a humanitarian expert AI assistant specialized in crisis response, humanitarian standards, and emergency operations. You provide accurate, actionable guidance based on established frameworks like Sphere Standards, Core Humanitarian Standard, and protection principles. You prioritize the safety and dignity of affected populations." - }, - { - "role": "user", - "content": "What is the cluster system in humanitarian response?" - }, - { - "role": "assistant", - "content": "The cluster system is a coordination mechanism that organizes humanitarian response into sectors: Health, Nutrition, WASH (Water/Sanitation/Hygiene), Shelter, Protection, Education, Food Security, Emergency Telecommunications, Logistics, and Camp Coordination. Each cluster has a designated lead agency (often a UN agency) responsible for coordination, setting standards, identifying gaps, and ensuring accountability. This system improves coordination and reduces duplication." - } - ], - "category": "coordination" - }, - { - "messages": [ - { - "role": "system", - "content": "You are a humanitarian expert AI assistant specialized in crisis response, humanitarian standards, and emergency operations. You provide accurate, actionable guidance based on established frameworks like Sphere Standards, Core Humanitarian Standard, and protection principles. You prioritize the safety and dignity of affected populations." - }, - { - "role": "user", - "content": "How do you assess shelter needs after a natural disaster?" - }, - { - "role": "assistant", - "content": "Shelter assessment should cover: 1) Number and demographics of affected households, 2) Extent of damage (destroyed, severely damaged, moderately damaged), 3) Climate and environmental conditions, 4) Cultural preferences and local building practices, 5) Available local materials and skills, 6) Land availability and tenure issues, 7) Security and protection concerns, 8) Access to services (water, sanitation, health), and 9) Planned return or relocation intentions." - } - ], - "category": "shelter" - } -] \ No newline at end of file diff --git a/hai-cd/val_dataset.json.backup_20251015_052330 b/hai-cd/val_dataset.json.backup_20251015_052330 deleted file mode 100644 index 328d6c3..0000000 --- a/hai-cd/val_dataset.json.backup_20251015_052330 +++ /dev/null @@ -1,19 +0,0 @@ -[ - { - "messages": [ - { - "role": "system", - "content": "You are a humanitarian expert AI assistant specialized in crisis response, humanitarian standards, and emergency operations. You provide accurate, actionable guidance based on established frameworks like Sphere Standards, Core Humanitarian Standard, and protection principles. You prioritize the safety and dignity of affected populations." - }, - { - "role": "user", - "content": "What are the key indicators of food insecurity in crisis situations?" - }, - { - "role": "assistant", - "content": "Key indicators include: 1) Acute malnutrition rates (wasting) especially in children under 5, 2) Crude mortality rate exceeding 1 death per 10,000 people per day, 3) Access to food (availability, physical access, economic access), 4) Food consumption patterns and dietary diversity, 5) Livelihood coping strategies (selling assets, reducing meals), and 6) Hazards and vulnerabilities (conflict, displacement, market disruption)." - } - ], - "category": "food_security" - } -] \ No newline at end of file From 5c5e64e1c0d1e39414ddd25c00fd77f3accdfe08 Mon Sep 17 00:00:00 2001 From: Sam Frons Date: Tue, 25 Aug 2026 16:41:28 +0200 Subject: [PATCH 02/46] docs: archive fine-tune/audit prototype to research/, add honest postmortem Moves hai-cd/, src/, scripts/, config/ and top-level status docs into research/. petri/seeds/ and data/ stay at root (still valuable). research/README.md documents the three bugs found in review: - humanitarian_auditor.py: auditor/target/judge all use the same local model, so the 100% audit result is self-evaluation - extract_humanitarian_knowledge.py: regex matched source code in docs, corrupting ~75% of train_dataset.json - petri_auditing.py: passing threshold tolerates 0/4 expected concepts found research/docs/WARNING_INVALID_AUDIT.md flags the kept-in-place petri/results/audit_report_20251015_084624.json as invalid evidence, not a real result. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01HCrPUhxWVtRBsYSSc7sBHU --- research/README.md | 140 ++++++++++++++++++ {config => research/config}/.env.example | 0 {config => research/config}/requirements.txt | 0 COMPARISON.md => research/docs/COMPARISON.md | 0 .../docs/HAI_FINAL_SUMMARY.md | 0 .../docs/INTEGRATION_GUIDE.md | 0 .../docs/PETRI_AUDIT_OVERVIEW.md | 0 SUMMARY.md => research/docs/SUMMARY.md | 0 research/docs/WARNING_INVALID_AUDIT.md | 14 ++ .../docs/petri_audit_output.log | 0 .../.claude-flow/metrics/agent-metrics.json | 0 .../.claude-flow/metrics/performance.json | 0 .../.claude-flow/metrics/task-metrics.json | 0 .../hai-cd}/HAI_Training_Colab.ipynb | 0 .../hai-cd}/PROJECT_SUMMARY.md | 0 {hai-cd => research/hai-cd}/QUICKSTART.md | 0 {hai-cd => research/hai-cd}/README.md | 0 {hai-cd => research/hai-cd}/START_HERE.md | 0 .../hai-cd}/START_TRAINING_NOW.md | 0 .../hai-cd}/TRAINING_CHECKLIST.md | 0 .../hai-cd}/TRAINING_PLATFORM_SETUP.md | 0 {hai-cd => research/hai-cd}/app.py | 0 .../hai-cd}/baseline_audit.csv | 0 .../hai-cd}/baseline_audit.json | 0 {hai-cd => research/hai-cd}/config.yaml | 0 .../hai-cd}/data_collection.py | 0 .../hai-cd}/dataset_summary.json | 0 {hai-cd => research/hai-cd}/demo_app.py | 0 .../hai-cd}/enhance_with_hai_knowledge.py | 0 .../hai-cd}/hazard_processor.py | 0 .../hai-cd}/humanitarian_base_dataset.json | 0 .../hai-cd}/humanitarian_llm_training.ipynb | 0 {hai-cd => research/hai-cd}/petri_auditing.py | 0 {hai-cd => research/hai-cd}/requirements.txt | 0 {hai-cd => research/hai-cd}/synthetic_data.py | 0 .../hai-cd}/synthetic_dataset.json | 0 {hai-cd => research/hai-cd}/test_dataset.json | 0 {hai-cd => research/hai-cd}/train.py | 0 .../hai-cd}/train_dataset.json | 0 {hai-cd => research/hai-cd}/train_model.py | 0 .../hai-cd}/train_orchestrator.py | 0 .../hai-cd}/training_metadata.json | 0 {hai-cd => research/hai-cd}/val_dataset.json | 0 .../extract_humanitarian_knowledge.py | 0 {scripts => research/scripts}/setup.sh | 0 .../src}/ace/context_optimizer.py | 0 .../src}/petri/humanitarian_auditor.py | 0 47 files changed, 154 insertions(+) create mode 100644 research/README.md rename {config => research/config}/.env.example (100%) rename {config => research/config}/requirements.txt (100%) rename COMPARISON.md => research/docs/COMPARISON.md (100%) rename HAI_FINAL_SUMMARY.md => research/docs/HAI_FINAL_SUMMARY.md (100%) rename INTEGRATION_GUIDE.md => research/docs/INTEGRATION_GUIDE.md (100%) rename PETRI_AUDIT_OVERVIEW.md => research/docs/PETRI_AUDIT_OVERVIEW.md (100%) rename SUMMARY.md => research/docs/SUMMARY.md (100%) create mode 100644 research/docs/WARNING_INVALID_AUDIT.md rename petri_audit_output.log => research/docs/petri_audit_output.log (100%) rename {hai-cd => research/hai-cd}/.claude-flow/metrics/agent-metrics.json (100%) rename {hai-cd => research/hai-cd}/.claude-flow/metrics/performance.json (100%) rename {hai-cd => research/hai-cd}/.claude-flow/metrics/task-metrics.json (100%) rename {hai-cd => research/hai-cd}/HAI_Training_Colab.ipynb (100%) rename {hai-cd => research/hai-cd}/PROJECT_SUMMARY.md (100%) rename {hai-cd => research/hai-cd}/QUICKSTART.md (100%) rename {hai-cd => research/hai-cd}/README.md (100%) rename {hai-cd => research/hai-cd}/START_HERE.md (100%) rename {hai-cd => research/hai-cd}/START_TRAINING_NOW.md (100%) rename {hai-cd => research/hai-cd}/TRAINING_CHECKLIST.md (100%) rename {hai-cd => research/hai-cd}/TRAINING_PLATFORM_SETUP.md (100%) rename {hai-cd => research/hai-cd}/app.py (100%) rename {hai-cd => research/hai-cd}/baseline_audit.csv (100%) rename {hai-cd => research/hai-cd}/baseline_audit.json (100%) rename {hai-cd => research/hai-cd}/config.yaml (100%) rename {hai-cd => research/hai-cd}/data_collection.py (100%) rename {hai-cd => research/hai-cd}/dataset_summary.json (100%) rename {hai-cd => research/hai-cd}/demo_app.py (100%) rename {hai-cd => research/hai-cd}/enhance_with_hai_knowledge.py (100%) rename {hai-cd => research/hai-cd}/hazard_processor.py (100%) rename {hai-cd => research/hai-cd}/humanitarian_base_dataset.json (100%) rename {hai-cd => research/hai-cd}/humanitarian_llm_training.ipynb (100%) rename {hai-cd => research/hai-cd}/petri_auditing.py (100%) rename {hai-cd => research/hai-cd}/requirements.txt (100%) rename {hai-cd => research/hai-cd}/synthetic_data.py (100%) rename {hai-cd => research/hai-cd}/synthetic_dataset.json (100%) rename {hai-cd => research/hai-cd}/test_dataset.json (100%) rename {hai-cd => research/hai-cd}/train.py (100%) rename {hai-cd => research/hai-cd}/train_dataset.json (100%) rename {hai-cd => research/hai-cd}/train_model.py (100%) rename {hai-cd => research/hai-cd}/train_orchestrator.py (100%) rename {hai-cd => research/hai-cd}/training_metadata.json (100%) rename {hai-cd => research/hai-cd}/val_dataset.json (100%) rename {scripts => research/scripts}/extract_humanitarian_knowledge.py (100%) rename {scripts => research/scripts}/setup.sh (100%) rename {src => research/src}/ace/context_optimizer.py (100%) rename {src => research/src}/petri/humanitarian_auditor.py (100%) diff --git a/research/README.md b/research/README.md new file mode 100644 index 0000000..246ffe7 --- /dev/null +++ b/research/README.md @@ -0,0 +1,140 @@ +# Research archive: fine-tune + audit prototype (postmortem) + +> **This code is archived, not maintained.** It is kept for provenance and as +> a documented lesson in why the project pivoted (see [Why we pivoted](#why-we-pivoted) +> below). Nothing here should be reused without re-verifying it independently. + +This directory holds the original prototype for HAI: a plan to fine-tune a +local LLM (Llama 3.3 8B via Ollama) on humanitarian domain knowledge using +LoRA, then validate it with a Petri-style safety/accuracy auditor. The +pipeline ran end to end and produced an audit report claiming **26/26 +scenarios passed (100%), $0.00 cost, "READY TO TRAIN."** That result is +invalid. Three independent bugs in the pipeline mean the number was never a +real accuracy measurement. This document explains what was built, what went +wrong, and why the project moved to a retrieval-grounded, tool-using +assistant with an independent judge instead of a fine-tuned model. + +## What was attempted + +- `hai-cd/` — data collection, dataset construction (`data_collection.py`, + `synthetic_data.py`, `enhance_with_hai_knowledge.py`), and a LoRA + fine-tuning pipeline (`train.py`, `train_model.py`, + `humanitarian_llm_training.ipynb`) targeting a local `llama3.3:8b` model. +- `src/petri/humanitarian_auditor.py` — a three-tier Petri-style auditor + intended to have one model generate adversarial/test conversations + (auditor), a second model act as the system under test (target), and a + **third, independent** model grade the target's answers (judge) — the + whole point of Petri-style auditing is that the judge must not be the + model being graded. +- `scripts/extract_humanitarian_knowledge.py` — a regex-based extractor + meant to mine Q&A pairs for training data out of markdown docs. +- `hai-cd/petri_auditing.py` — an earlier, simpler keyword-based response + evaluator used for a baseline audit before the full Petri auditor existed. + +## Why we pivoted + +### Bug 1 — the "independent" judge was the same model being graded + +`src/petri/humanitarian_auditor.py`: every generation call — auditor probe, +target response, *and* judge evaluation — passes `model=self.target_model` +to `ollama.generate(...)`: + +- `auditor_probe` (fallback path, ~line 211-214) +- `target_respond` (~line 270-274) +- `judge_evaluate` (~line 360-364) + +All three run against the same local `llama3.3:8b` instance. `judge_model` +(constructor default `"claude-haiku-4"`, set at ~line 103/110) and +`anthropic_client` (constructed at ~line 132 if `ANTHROPIC_API_KEY` is set) +are both created but **never referenced in any inference call** — dead code +that made the design look independent when it wasn't. The result is a model +grading its own homework, with the grading prompt itself even suggesting +the expected JSON shape and passing bar. The committed +`petri/results/audit_report_20251015_084624.json` — "26/26 passed, 100%, +$0.00" — is self-evaluation, not a valid audit, and should not be cited as +evidence of model quality. See the warning header at the top of that file's +directory listing in this repo. + +### Bug 2 — the training data extractor scraped source code, not prose + +`scripts/extract_humanitarian_knowledge.py`, `_extract_terminology` +(~line 176), used this regex to pull `Term: definition` pairs out of +markdown documentation: + +```python +def_pattern = r'\*?\*?([A-Z][A-Za-z\s]+)\*?\*?:\s+([^\.]+\.)' +``` + +This pattern matches far more than markdown-bold terminology — it also +matches `Key: value` shapes inside embedded TypeScript/JSON code blocks in +the source docs (config objects, isolation levels, timeouts, etc.). The +result: roughly 75% of `hai-cd/train_dataset.json` (now +`research/hai-cd/train_dataset.json`) is corrupted. Examples pulled directly +from the dataset: + +- `"What is Level in humanitarian context?"` → `"'ReadCommitted', timeout: 10000 });"` +- `"What is Accessed in humanitarian context?"` → `"new Date(),\n lastAccessIP: accessContext."` + +Manual review found roughly 17 of 79 training samples were plausible, +on-topic Q&A. At least one sample has the roles swapped (the "user" message +is actually a system prompt intended for the assistant). A fine-tune run on +this dataset would have taught the model to imitate corrupted code +fragments and mislabeled turns, not humanitarian domain knowledge. + +### Bug 3 — the baseline evaluator's passing bar let empty answers through + +`hai-cd/petri_auditing.py`, `evaluate_response` (~line 167): scoring starts +at `1.0` and subtracts `0.3` if fewer than half the expected keywords are +found, `0.5` for a keyword to avoid, and `0.2` for a short response — then +passes anything scoring `>= 0.7`. A response containing **zero** of the four +expected concepts only loses 0.3 (score 0.7), which still clears the bar. +`hai-cd/baseline_audit.csv` shows exactly this: placeholder/boilerplate +responses that reference none of the expected humanitarian concepts, +recorded as passing. + +### The combined effect + +Three independently plausible-looking components — a training set, a +grading rubric, and a "three-tier" auditor — each had a bug that biased +results toward "looks fine." Chained together, they produced a fine-tuning +pipeline that could not have been validated by its own audit, regardless of +how good or bad the underlying model actually was. That's the core lesson: +**an eval whose judge shares a failure mode with the thing it's grading (or +whose passing bar tolerates near-empty answers) is not an eval.** + +## Why retrieval-grounding + tool use instead of fine-tuning + +Fine-tuning bakes a snapshot of (corrupted, in this case) training data into +model weights, is expensive to iterate on, and gives no way to cite a +source for a claim. The rebuilt assistant instead: + +- Retrieves grounding passages from primary humanitarian references (Sphere + Handbook, Core Humanitarian Standard, IASC guidance) via Supabase pgvector + hybrid search, and cites them. +- Calls live tools (ReliefWeb, HDX HAPI) for current crisis data instead of + memorizing stale facts. +- Is evaluated by a judge model from a **different model family** than the + one being tested, against the 26 scenarios in `petri/seeds/` — with + explicit `expected_facts` and `evaluation_criteria` checked, not + keyword-percentage heuristics. + +See the root [`README.md`](../README.md) for the current architecture. + +## What's still good here — keep this + +`petri/seeds/humanitarian_test_scenarios.json` (at the repo root, **not** +moved into this archive) is a genuinely useful asset: 26 domain-literate +safety/accuracy scenarios with `evaluation_criteria` and `expected_facts`. +The scenario *design* was never the problem — the auditor that ran them was. +This scenario set is being reused as-is for the new eval harness (see +`evals/README.md`). + +## Files in this archive + +| Path | What it is | +|---|---| +| `hai-cd/` | Dataset collection, synthetic data, LoRA fine-tune pipeline, notebooks, baseline audit outputs | +| `src/petri/humanitarian_auditor.py` | The three-tier Petri auditor with Bug 1 | +| `scripts/extract_humanitarian_knowledge.py` | The regex extractor with Bug 2 | +| `config/` | Prototype-era requirements/env example | +| `docs/` | Prior status reports and summaries (`HAI_FINAL_SUMMARY.md`, `SUMMARY.md`, `COMPARISON.md`, `INTEGRATION_GUIDE.md`, `PETRI_AUDIT_OVERVIEW.md`, `petri_audit_output.log`) — written when the invalid 100% result was believed to be real; read them as historical record, not as current status. diff --git a/config/.env.example b/research/config/.env.example similarity index 100% rename from config/.env.example rename to research/config/.env.example diff --git a/config/requirements.txt b/research/config/requirements.txt similarity index 100% rename from config/requirements.txt rename to research/config/requirements.txt diff --git a/COMPARISON.md b/research/docs/COMPARISON.md similarity index 100% rename from COMPARISON.md rename to research/docs/COMPARISON.md diff --git a/HAI_FINAL_SUMMARY.md b/research/docs/HAI_FINAL_SUMMARY.md similarity index 100% rename from HAI_FINAL_SUMMARY.md rename to research/docs/HAI_FINAL_SUMMARY.md diff --git a/INTEGRATION_GUIDE.md b/research/docs/INTEGRATION_GUIDE.md similarity index 100% rename from INTEGRATION_GUIDE.md rename to research/docs/INTEGRATION_GUIDE.md diff --git a/PETRI_AUDIT_OVERVIEW.md b/research/docs/PETRI_AUDIT_OVERVIEW.md similarity index 100% rename from PETRI_AUDIT_OVERVIEW.md rename to research/docs/PETRI_AUDIT_OVERVIEW.md diff --git a/SUMMARY.md b/research/docs/SUMMARY.md similarity index 100% rename from SUMMARY.md rename to research/docs/SUMMARY.md diff --git a/research/docs/WARNING_INVALID_AUDIT.md b/research/docs/WARNING_INVALID_AUDIT.md new file mode 100644 index 0000000..bb8a628 --- /dev/null +++ b/research/docs/WARNING_INVALID_AUDIT.md @@ -0,0 +1,14 @@ +# WARNING: invalid audit report + +`petri/results/audit_report_20251015_084624.json` (repo root, not moved — +`petri/` stays in place) reports "26/26 passed, 100%, $0.00." This is +**self-evaluation, not a valid audit**: the auditor, target, and judge +generation calls in `src/petri/humanitarian_auditor.py` all ran against the +same local model (`self.target_model`). The intended independent judge +(`judge_model` / `anthropic_client`) was never actually called. + +Kept in place deliberately, as evidence for the postmortem below — do not +delete it, and do not cite its numbers as a real accuracy measurement. + +See [`../README.md`](../README.md#bug-1--the-independent-judge-was-the-same-model-being-graded) +for the full explanation. diff --git a/petri_audit_output.log b/research/docs/petri_audit_output.log similarity index 100% rename from petri_audit_output.log rename to research/docs/petri_audit_output.log diff --git a/hai-cd/.claude-flow/metrics/agent-metrics.json b/research/hai-cd/.claude-flow/metrics/agent-metrics.json similarity index 100% rename from hai-cd/.claude-flow/metrics/agent-metrics.json rename to research/hai-cd/.claude-flow/metrics/agent-metrics.json diff --git a/hai-cd/.claude-flow/metrics/performance.json b/research/hai-cd/.claude-flow/metrics/performance.json similarity index 100% rename from hai-cd/.claude-flow/metrics/performance.json rename to research/hai-cd/.claude-flow/metrics/performance.json diff --git a/hai-cd/.claude-flow/metrics/task-metrics.json b/research/hai-cd/.claude-flow/metrics/task-metrics.json similarity index 100% rename from hai-cd/.claude-flow/metrics/task-metrics.json rename to research/hai-cd/.claude-flow/metrics/task-metrics.json diff --git a/hai-cd/HAI_Training_Colab.ipynb b/research/hai-cd/HAI_Training_Colab.ipynb similarity index 100% rename from hai-cd/HAI_Training_Colab.ipynb rename to research/hai-cd/HAI_Training_Colab.ipynb diff --git a/hai-cd/PROJECT_SUMMARY.md b/research/hai-cd/PROJECT_SUMMARY.md similarity index 100% rename from hai-cd/PROJECT_SUMMARY.md rename to research/hai-cd/PROJECT_SUMMARY.md diff --git a/hai-cd/QUICKSTART.md b/research/hai-cd/QUICKSTART.md similarity index 100% rename from hai-cd/QUICKSTART.md rename to research/hai-cd/QUICKSTART.md diff --git a/hai-cd/README.md b/research/hai-cd/README.md similarity index 100% rename from hai-cd/README.md rename to research/hai-cd/README.md diff --git a/hai-cd/START_HERE.md b/research/hai-cd/START_HERE.md similarity index 100% rename from hai-cd/START_HERE.md rename to research/hai-cd/START_HERE.md diff --git a/hai-cd/START_TRAINING_NOW.md b/research/hai-cd/START_TRAINING_NOW.md similarity index 100% rename from hai-cd/START_TRAINING_NOW.md rename to research/hai-cd/START_TRAINING_NOW.md diff --git a/hai-cd/TRAINING_CHECKLIST.md b/research/hai-cd/TRAINING_CHECKLIST.md similarity index 100% rename from hai-cd/TRAINING_CHECKLIST.md rename to research/hai-cd/TRAINING_CHECKLIST.md diff --git a/hai-cd/TRAINING_PLATFORM_SETUP.md b/research/hai-cd/TRAINING_PLATFORM_SETUP.md similarity index 100% rename from hai-cd/TRAINING_PLATFORM_SETUP.md rename to research/hai-cd/TRAINING_PLATFORM_SETUP.md diff --git a/hai-cd/app.py b/research/hai-cd/app.py similarity index 100% rename from hai-cd/app.py rename to research/hai-cd/app.py diff --git a/hai-cd/baseline_audit.csv b/research/hai-cd/baseline_audit.csv similarity index 100% rename from hai-cd/baseline_audit.csv rename to research/hai-cd/baseline_audit.csv diff --git a/hai-cd/baseline_audit.json b/research/hai-cd/baseline_audit.json similarity index 100% rename from hai-cd/baseline_audit.json rename to research/hai-cd/baseline_audit.json diff --git a/hai-cd/config.yaml b/research/hai-cd/config.yaml similarity index 100% rename from hai-cd/config.yaml rename to research/hai-cd/config.yaml diff --git a/hai-cd/data_collection.py b/research/hai-cd/data_collection.py similarity index 100% rename from hai-cd/data_collection.py rename to research/hai-cd/data_collection.py diff --git a/hai-cd/dataset_summary.json b/research/hai-cd/dataset_summary.json similarity index 100% rename from hai-cd/dataset_summary.json rename to research/hai-cd/dataset_summary.json diff --git a/hai-cd/demo_app.py b/research/hai-cd/demo_app.py similarity index 100% rename from hai-cd/demo_app.py rename to research/hai-cd/demo_app.py diff --git a/hai-cd/enhance_with_hai_knowledge.py b/research/hai-cd/enhance_with_hai_knowledge.py similarity index 100% rename from hai-cd/enhance_with_hai_knowledge.py rename to research/hai-cd/enhance_with_hai_knowledge.py diff --git a/hai-cd/hazard_processor.py b/research/hai-cd/hazard_processor.py similarity index 100% rename from hai-cd/hazard_processor.py rename to research/hai-cd/hazard_processor.py diff --git a/hai-cd/humanitarian_base_dataset.json b/research/hai-cd/humanitarian_base_dataset.json similarity index 100% rename from hai-cd/humanitarian_base_dataset.json rename to research/hai-cd/humanitarian_base_dataset.json diff --git a/hai-cd/humanitarian_llm_training.ipynb b/research/hai-cd/humanitarian_llm_training.ipynb similarity index 100% rename from hai-cd/humanitarian_llm_training.ipynb rename to research/hai-cd/humanitarian_llm_training.ipynb diff --git a/hai-cd/petri_auditing.py b/research/hai-cd/petri_auditing.py similarity index 100% rename from hai-cd/petri_auditing.py rename to research/hai-cd/petri_auditing.py diff --git a/hai-cd/requirements.txt b/research/hai-cd/requirements.txt similarity index 100% rename from hai-cd/requirements.txt rename to research/hai-cd/requirements.txt diff --git a/hai-cd/synthetic_data.py b/research/hai-cd/synthetic_data.py similarity index 100% rename from hai-cd/synthetic_data.py rename to research/hai-cd/synthetic_data.py diff --git a/hai-cd/synthetic_dataset.json b/research/hai-cd/synthetic_dataset.json similarity index 100% rename from hai-cd/synthetic_dataset.json rename to research/hai-cd/synthetic_dataset.json diff --git a/hai-cd/test_dataset.json b/research/hai-cd/test_dataset.json similarity index 100% rename from hai-cd/test_dataset.json rename to research/hai-cd/test_dataset.json diff --git a/hai-cd/train.py b/research/hai-cd/train.py similarity index 100% rename from hai-cd/train.py rename to research/hai-cd/train.py diff --git a/hai-cd/train_dataset.json b/research/hai-cd/train_dataset.json similarity index 100% rename from hai-cd/train_dataset.json rename to research/hai-cd/train_dataset.json diff --git a/hai-cd/train_model.py b/research/hai-cd/train_model.py similarity index 100% rename from hai-cd/train_model.py rename to research/hai-cd/train_model.py diff --git a/hai-cd/train_orchestrator.py b/research/hai-cd/train_orchestrator.py similarity index 100% rename from hai-cd/train_orchestrator.py rename to research/hai-cd/train_orchestrator.py diff --git a/hai-cd/training_metadata.json b/research/hai-cd/training_metadata.json similarity index 100% rename from hai-cd/training_metadata.json rename to research/hai-cd/training_metadata.json diff --git a/hai-cd/val_dataset.json b/research/hai-cd/val_dataset.json similarity index 100% rename from hai-cd/val_dataset.json rename to research/hai-cd/val_dataset.json diff --git a/scripts/extract_humanitarian_knowledge.py b/research/scripts/extract_humanitarian_knowledge.py similarity index 100% rename from scripts/extract_humanitarian_knowledge.py rename to research/scripts/extract_humanitarian_knowledge.py diff --git a/scripts/setup.sh b/research/scripts/setup.sh similarity index 100% rename from scripts/setup.sh rename to research/scripts/setup.sh diff --git a/src/ace/context_optimizer.py b/research/src/ace/context_optimizer.py similarity index 100% rename from src/ace/context_optimizer.py rename to research/src/ace/context_optimizer.py diff --git a/src/petri/humanitarian_auditor.py b/research/src/petri/humanitarian_auditor.py similarity index 100% rename from src/petri/humanitarian_auditor.py rename to research/src/petri/humanitarian_auditor.py From abba316b0cae1424a12201c93f33daeb79c70abd Mon Sep 17 00:00:00 2001 From: Sam Frons Date: Tue, 25 Aug 2026 16:41:57 +0200 Subject: [PATCH 03/46] docs: rewrite root README for the agentic rebuild Documents the target architecture (Next.js -> /api/chat -> tools -> safety/eval layer), honest in-progress status, the 26-scenario eval suite, repo layout, and links to research/README.md for the prior prototype postmortem. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01HCrPUhxWVtRBsYSSc7sBHU --- README.md | 84 +++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 82 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 12e8bb3..da0b5c1 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,82 @@ -# HAI -Humanitarian AI bot +# HAI — Humanitarian AI Assistant + +An agentic, citation-grounded AI assistant for humanitarian work. It answers +questions against primary references — the Sphere Handbook, the Core +Humanitarian Standard, and IASC guidance — and calls live tools for current +crisis data (ReliefWeb) and structured humanitarian datasets (HDX HAPI), +rather than relying on facts memorized into model weights. + +## What it is + +- **Retrieval-grounded, not fine-tuned.** Answers cite the source passage + they're grounded in. See [`research/README.md`](research/README.md) for + why an earlier fine-tuning approach was abandoned. +- **Tool-using.** The assistant can search humanitarian standards, pull + current crisis updates, and query structured humanitarian data — it + doesn't just generate from a static prompt. +- **Evaluated by an independent judge.** A held-out set of 26 domain + scenarios is graded by a model from a different family than the one being + tested, against explicit expected facts — not a self-graded, keyword-ratio + heuristic. See [Evals](#evals). + +## Architecture + +```mermaid +flowchart LR + UI["Next.js UI\n(chat)"] --> API["/api/chat\nAI SDK v5 + Claude Sonnet"] + API --> T1["search_standards\nSupabase pgvector\nhybrid search"] + API --> T2["get_crisis_updates\nReliefWeb API"] + API --> T3["get_humanitarian_data\nHDX HAPI"] + API --> Safety["Safety layer"] + Safety --> Eval["Eval harness\n(independent judge,\n26 scenarios)"] +``` + +- **UI**: Next.js app (`app/`) using the Vercel AI SDK's chat components. +- **`/api/chat`**: AI SDK v5 route calling Claude (Sonnet) with tool + definitions. +- **Tools**: + - `search_standards` — hybrid (vector + keyword) search over an ingested + corpus of humanitarian standards in Supabase pgvector. + - `get_crisis_updates` — live data from the ReliefWeb API. + - `get_humanitarian_data` — structured indicators from HDX HAPI. +- **Safety layer + eval harness**: independent-judge scoring against the + scenario suite described below, run outside the serving path. + +## Status + +In active development. Nothing below is a finished product claim: + +- [x] Repo restructured; prior fine-tune/audit prototype archived and + documented (see `research/`) +- [ ] Next.js app scaffold (`app/`) +- [ ] Corpus ingestion pipeline (`ingestion/`) +- [ ] `search_standards`, `get_crisis_updates`, `get_humanitarian_data` tools +- [ ] Eval harness wired to an independent judge model (`evals/`) + +## Eval suite + +`petri/seeds/humanitarian_test_scenarios.json` holds 26 domain-literate +scenarios (crisis classification, protection principles, disinformation +resistance, etc.), each with `evaluation_criteria` and expected facts to +check for. This scenario design survived the pivot unchanged — what's +changing is how it's judged (see [`evals/README.md`](evals/README.md)). + +## Repo layout + +| Path | Contents | +|---|---| +| `app/` | Next.js application (UI + `/api/chat` route) | +| `ingestion/` | Corpus → chunking → embeddings → Supabase pgvector pipeline | +| `evals/` | Eval harness driving `petri/seeds/` scenarios | +| `petri/seeds/` | The 26-scenario eval suite (kept as-is from the prototype) | +| `petri/results/` | Historical audit output — see the warning in `research/docs/` before citing it | +| `data/` | Processed reference data | +| `research/` | Archived fine-tune/audit prototype + honest postmortem — **start here** for context on why the project pivoted: [`research/README.md`](research/README.md) | + +## Prior work + +The original prototype attempted a LoRA fine-tune of a local model, +validated by a Petri-style auditor. Three bugs invalidated its results (a +self-judging auditor, a training-data extractor that scraped source code +instead of prose, and a passing threshold that let empty answers through). +Full writeup, with file:line references: [`research/README.md`](research/README.md). From 434aac5942c6733741df61218a3e4698eae5d748 Mon Sep 17 00:00:00 2001 From: Sam Frons Date: Tue, 25 Aug 2026 16:43:02 +0200 Subject: [PATCH 04/46] feat: scaffold Next.js app with AI SDK deps create-next-app (TS, ESLint, Tailwind, src/, App Router, @/* alias). Adds ai, @ai-sdk/anthropic, @ai-sdk/react, zod. app/.env.example documents required secrets (Anthropic, Voyage, Supabase, spend/rate caps) without setting real values. Fixed app/.gitignore's blanket .env* pattern so .env.example stays tracked. pnpm build passes. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01HCrPUhxWVtRBsYSSc7sBHU --- app/.env.example | 18 + app/.gitignore | 42 + app/AGENTS.md | 9 + app/CLAUDE.md | 1 + app/README.md | 36 + app/eslint.config.mjs | 18 + app/next.config.ts | 7 + app/package.json | 31 + app/pnpm-lock.yaml | 4322 +++++++++++++++++++++++++++++++++++++++ app/pnpm-workspace.yaml | 3 + app/postcss.config.mjs | 7 + app/public/file.svg | 1 + app/public/globe.svg | 1 + app/public/next.svg | 1 + app/public/vercel.svg | 1 + app/public/window.svg | 1 + app/src/app/favicon.ico | Bin 0 -> 25931 bytes app/src/app/globals.css | 26 + app/src/app/layout.tsx | 29 + app/src/app/page.tsx | 69 + app/tsconfig.json | 34 + 21 files changed, 4657 insertions(+) create mode 100644 app/.env.example create mode 100644 app/.gitignore create mode 100644 app/AGENTS.md create mode 100644 app/CLAUDE.md create mode 100644 app/README.md create mode 100644 app/eslint.config.mjs create mode 100644 app/next.config.ts create mode 100644 app/package.json create mode 100644 app/pnpm-lock.yaml create mode 100644 app/pnpm-workspace.yaml create mode 100644 app/postcss.config.mjs create mode 100644 app/public/file.svg create mode 100644 app/public/globe.svg create mode 100644 app/public/next.svg create mode 100644 app/public/vercel.svg create mode 100644 app/public/window.svg create mode 100644 app/src/app/favicon.ico create mode 100644 app/src/app/globals.css create mode 100644 app/src/app/layout.tsx create mode 100644 app/src/app/page.tsx create mode 100644 app/tsconfig.json diff --git a/app/.env.example b/app/.env.example new file mode 100644 index 0000000..8f5a3b7 --- /dev/null +++ b/app/.env.example @@ -0,0 +1,18 @@ +# Claude API access for /api/chat +ANTHROPIC_API_KEY= + +# Voyage AI embeddings for corpus ingestion + search_standards tool +VOYAGE_API_KEY= + +# Supabase (server-side, pgvector search + writes) — keep the service role +# key server-only, never expose it to the client +SUPABASE_URL= +SUPABASE_SERVICE_ROLE_KEY= + +# Supabase (client-side, public) +NEXT_PUBLIC_SUPABASE_URL= +NEXT_PUBLIC_SUPABASE_ANON_KEY= + +# Cost / abuse controls +DAILY_SPEND_CAP_USD= +RATE_LIMIT_RPM= diff --git a/app/.gitignore b/app/.gitignore new file mode 100644 index 0000000..7b8da95 --- /dev/null +++ b/app/.gitignore @@ -0,0 +1,42 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies +/node_modules +/.pnp +.pnp.* +.yarn/* +!.yarn/patches +!.yarn/plugins +!.yarn/releases +!.yarn/versions + +# testing +/coverage + +# next.js +/.next/ +/out/ + +# production +/build + +# misc +.DS_Store +*.pem + +# debug +npm-debug.log* +yarn-debug.log* +yarn-error.log* +.pnpm-debug.log* + +# env files (can opt-in for committing if needed) +.env* +!.env.example + +# vercel +.vercel + +# typescript +*.tsbuildinfo +next-env.d.ts diff --git a/app/AGENTS.md b/app/AGENTS.md new file mode 100644 index 0000000..643577d --- /dev/null +++ b/app/AGENTS.md @@ -0,0 +1,9 @@ + + +# This is NOT the Next.js you know + +This version has breaking changes — APIs, conventions, and file structure may all differ from your training data. Read the relevant guide in `node_modules/next/dist/docs/` (resolved from this file's directory; in monorepos the `next` package may not be visible from the repo root) before writing any code. Heed deprecation notices. + +This block is written and re-added by `next dev` — verify at `node_modules/next/dist/server/lib/generate-agent-files.js`. Removing it from a diff only re-creates the uncommitted change; committing it with your work keeps the tree clean. + + diff --git a/app/CLAUDE.md b/app/CLAUDE.md new file mode 100644 index 0000000..43c994c --- /dev/null +++ b/app/CLAUDE.md @@ -0,0 +1 @@ +@AGENTS.md diff --git a/app/README.md b/app/README.md new file mode 100644 index 0000000..e215bc4 --- /dev/null +++ b/app/README.md @@ -0,0 +1,36 @@ +This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/create-next-app). + +## Getting Started + +First, run the development server: + +```bash +npm run dev +# or +yarn dev +# or +pnpm dev +# or +bun dev +``` + +Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. + +You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file. + +This project uses [`next/font`](https://nextjs.org/docs/app/building-your-application/optimizing/fonts) to automatically optimize and load [Geist](https://vercel.com/font), a new font family for Vercel. + +## Learn More + +To learn more about Next.js, take a look at the following resources: + +- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API. +- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial. + +You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome! + +## Deploy on Vercel + +The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js. + +Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details. diff --git a/app/eslint.config.mjs b/app/eslint.config.mjs new file mode 100644 index 0000000..05e726d --- /dev/null +++ b/app/eslint.config.mjs @@ -0,0 +1,18 @@ +import { defineConfig, globalIgnores } from "eslint/config"; +import nextVitals from "eslint-config-next/core-web-vitals"; +import nextTs from "eslint-config-next/typescript"; + +const eslintConfig = defineConfig([ + ...nextVitals, + ...nextTs, + // Override default ignores of eslint-config-next. + globalIgnores([ + // Default ignores of eslint-config-next: + ".next/**", + "out/**", + "build/**", + "next-env.d.ts", + ]), +]); + +export default eslintConfig; diff --git a/app/next.config.ts b/app/next.config.ts new file mode 100644 index 0000000..e9ffa30 --- /dev/null +++ b/app/next.config.ts @@ -0,0 +1,7 @@ +import type { NextConfig } from "next"; + +const nextConfig: NextConfig = { + /* config options here */ +}; + +export default nextConfig; diff --git a/app/package.json b/app/package.json new file mode 100644 index 0000000..3c84ee1 --- /dev/null +++ b/app/package.json @@ -0,0 +1,31 @@ +{ + "name": "app", + "version": "0.1.0", + "private": true, + "scripts": { + "dev": "next dev", + "build": "next build", + "start": "next start", + "lint": "eslint" + }, + "dependencies": { + "@ai-sdk/anthropic": "^4.0.42", + "@ai-sdk/react": "^4.0.82", + "ai": "^7.0.79", + "next": "16.3.2", + "react": "19.2.8", + "react-dom": "19.2.8", + "zod": "^4.4.3" + }, + "devDependencies": { + "@tailwindcss/postcss": "^4", + "@types/node": "^20", + "@types/react": "^19", + "@types/react-dom": "^19", + "eslint": "^9", + "eslint-config-next": "16.3.2", + "tailwindcss": "^4", + "typescript": "^5" + }, + "packageManager": "pnpm@10.13.1" +} diff --git a/app/pnpm-lock.yaml b/app/pnpm-lock.yaml new file mode 100644 index 0000000..9a4f626 --- /dev/null +++ b/app/pnpm-lock.yaml @@ -0,0 +1,4322 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: + dependencies: + '@ai-sdk/anthropic': + specifier: ^4.0.42 + version: 4.0.42(zod@4.4.3) + '@ai-sdk/react': + specifier: ^4.0.82 + version: 4.0.82(react@19.2.8)(zod@4.4.3) + ai: + specifier: ^7.0.79 + version: 7.0.79(zod@4.4.3) + next: + specifier: 16.3.2 + version: 16.3.2(@babel/core@7.29.7)(@types/node@20.19.43)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + react: + specifier: 19.2.8 + version: 19.2.8 + react-dom: + specifier: 19.2.8 + version: 19.2.8(react@19.2.8) + zod: + specifier: ^4.4.3 + version: 4.4.3 + devDependencies: + '@tailwindcss/postcss': + specifier: ^4 + version: 4.3.3 + '@types/node': + specifier: ^20 + version: 20.19.43 + '@types/react': + specifier: ^19 + version: 19.2.18 + '@types/react-dom': + specifier: ^19 + version: 19.2.5(@types/react@19.2.18) + eslint: + specifier: ^9 + version: 9.39.5(jiti@2.7.0) + eslint-config-next: + specifier: 16.3.2 + version: 16.3.2(@typescript-eslint/parser@8.68.0(eslint@9.39.5(jiti@2.7.0))(typescript@5.9.3))(eslint@9.39.5(jiti@2.7.0))(typescript@5.9.3) + tailwindcss: + specifier: ^4 + version: 4.3.3 + typescript: + specifier: ^5 + version: 5.9.3 + +packages: + + '@ai-sdk/anthropic@4.0.42': + resolution: {integrity: sha512-U3RJDo2/rWdH6SjSbA2Owd30ZawKwt4zFTqteTTn6Po0vEW1ec1KXf6s1nwNE3mxCqKrQWEavU21C46AfGYaeg==} + engines: {node: '>=22'} + peerDependencies: + zod: ^3.25.76 || ^4.1.8 + + '@ai-sdk/gateway@4.0.64': + resolution: {integrity: sha512-iayK1z11I0b3sgbP3j6eKQypfpwO5Fbtaq1Mkwv7YxwGQG1YTNr5QAjyYsSfqyf/58WUOLWz2cTBpk+ExZVgpw==} + engines: {node: '>=22'} + peerDependencies: + zod: ^3.25.76 || ^4.1.8 + + '@ai-sdk/mcp@2.0.37': + resolution: {integrity: sha512-VYmO75lhrCr9LBrUuXBpS/cMJ/KL1YhLo7nGgLTqOKtXA3+9kIY73gJmzg/GIANyGVatffrGER43iIFFlqUxtA==} + engines: {node: '>=22'} + peerDependencies: + zod: ^3.25.76 || ^4.1.8 + + '@ai-sdk/provider-utils@5.0.30': + resolution: {integrity: sha512-9TyxUXolql77ntHIeygLqt7PO1O0HZPB73cJhLG2OsPecSBRIZhNXLxT1T7rlL6Zpm1eDoLMFrMV99kAJ28/Sg==} + engines: {node: '>=22'} + peerDependencies: + zod: ^3.25.76 || ^4.1.8 + + '@ai-sdk/provider@4.0.8': + resolution: {integrity: sha512-aWO7iwhFUGf347tCwNGggggfmZigaSu7TF739IZSrWWABUp7zkb4Cr3fMqvBe5EIS7ABJJu3Cadn0g/zs1G0QQ==} + engines: {node: '>=22'} + + '@ai-sdk/react@4.0.82': + resolution: {integrity: sha512-1zmDxOFSb2JxKovhYCHEHl/C9Nhx7PreI/FrQq1LqbA9BxGBs0xBF2MZaB7YN+h6K5XrAVgY7CzmaLB4IjA3wQ==} + engines: {node: '>=22'} + peerDependencies: + react: ^18 || ~19.0.1 || ~19.1.2 || ^19.2.1 + + '@alloc/quick-lru@5.2.0': + resolution: {integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==} + engines: {node: '>=10'} + + '@babel/code-frame@7.29.7': + resolution: {integrity: sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==} + engines: {node: '>=6.9.0'} + + '@babel/compat-data@7.29.7': + resolution: {integrity: sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg==} + engines: {node: '>=6.9.0'} + + '@babel/core@7.29.7': + resolution: {integrity: sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==} + engines: {node: '>=6.9.0'} + + '@babel/generator@7.29.8': + resolution: {integrity: sha512-gZbepsdh3WDtgZKWL+vTPh71LSBrm/Y4/QDZBVCcYfmeTEEuoOYwlSy+G1StfJg+/Zy550u/3TATbm7qDbbMtg==} + engines: {node: '>=6.9.0'} + + '@babel/helper-compilation-targets@7.29.7': + resolution: {integrity: sha512-wem6WaBj4NaVYVdNhLPPVacES6ZJ+KBBfSkTMD3YZxbP3rm3Di85tJU5ljaUNhaOynt+Aj0xruhYuzQBt8n71g==} + engines: {node: '>=6.9.0'} + + '@babel/helper-globals@7.29.7': + resolution: {integrity: sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA==} + engines: {node: '>=6.9.0'} + + '@babel/helper-module-imports@7.29.7': + resolution: {integrity: sha512-ejHwrQQYcm9xnTivShn2IDOlIzInN34AXskvq9QicvCtEzq1Vzclu/tKF8Jq1Cg8JG2GL6/EmjgsCT7lXepE3g==} + engines: {node: '>=6.9.0'} + + '@babel/helper-module-transforms@7.29.7': + resolution: {integrity: sha512-UPUVSyXbOh627KiCIGQSgwWzGeBKLkaJ9PJEdrngIwMSzxLR4jS4+f1f1jb7VzBbg8nFLaYotvVPFCTqdrmTAg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-string-parser@7.29.7': + resolution: {integrity: sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-identifier@7.29.7': + resolution: {integrity: sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-option@7.29.7': + resolution: {integrity: sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw==} + engines: {node: '>=6.9.0'} + + '@babel/helpers@7.29.7': + resolution: {integrity: sha512-1k2lAGRMfHTcwuNYcCNUmaUffmQv8KWMfh2iJUUeRlwlwH4FdNG7mfPI10NPfLHJFThE4Tyr4mv7kTNZOiPuBg==} + engines: {node: '>=6.9.0'} + + '@babel/parser@7.29.8': + resolution: {integrity: sha512-E8lTAYNB1KW+FH+VGJuZM1ioAx2E6oVlvQFRrf5P8ZZmsiJXYAD9vTFV7yyEURNzgh1dFqMZuO6tUwcARbqFCA==} + engines: {node: '>=6.0.0'} + hasBin: true + + '@babel/template@7.29.7': + resolution: {integrity: sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg==} + engines: {node: '>=6.9.0'} + + '@babel/traverse@7.29.8': + resolution: {integrity: sha512-I5z7H3bf/41ktsNVLtpN0wAa336HkqIHQ5BuPLEhTkt1jVSyZpeNKIzTgEWmlxjdg81R0IgUCcaE+Ok3NvrfZg==} + engines: {node: '>=6.9.0'} + + '@babel/types@7.29.8': + resolution: {integrity: sha512-Vj1jF3cPfxg7OAfoI7QnVKLoILlm2JF9pnVHrX8qx7AHMiYWT+NDAA7jChlNgRS4WTLc/fD1lXLmPixluj+3Gg==} + engines: {node: '>=6.9.0'} + + '@emnapi/core@1.10.0': + resolution: {integrity: sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==} + + '@emnapi/runtime@1.10.0': + resolution: {integrity: sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==} + + '@emnapi/runtime@1.11.3': + resolution: {integrity: sha512-Xz4Tpyki7XyrpbUK1jR1AhdAdaXyhhY4lZ3neLodmhpuWfy2PAQN5B46sAiU4liOXGLkHypn/qU+jvfWSCYYLA==} + + '@emnapi/wasi-threads@1.2.1': + resolution: {integrity: sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==} + + '@eslint-community/eslint-utils@4.10.1': + resolution: {integrity: sha512-cuadcxVFE8sDK6iWJbs8Sn0av2Nrh2QSGQhVlBW9AaAHqHwjWsZHT8LJ4hFGPh7ASBV2deFdM7H/DPjulmh8rg==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 + + '@eslint-community/eslint-utils@4.9.1': + resolution: {integrity: sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 + + '@eslint-community/regexpp@4.12.2': + resolution: {integrity: sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==} + engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} + + '@eslint/config-array@0.21.2': + resolution: {integrity: sha512-nJl2KGTlrf9GjLimgIru+V/mzgSK0ABCDQRvxw5BjURL7WfH5uoWmizbH7QB6MmnMBd8cIC9uceWnezL1VZWWw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/config-helpers@0.4.2': + resolution: {integrity: sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/core@0.17.0': + resolution: {integrity: sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/eslintrc@3.3.6': + resolution: {integrity: sha512-l2Ul9PrHsPCKcEY/ac7VgFj9D80C7S68sOKc618SyHDPK36s1XcFebXY0iTzUVn4Yq+YbwvSnDmCz9yxjX+QrA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/js@9.39.5': + resolution: {integrity: sha512-QywQuszQh77pIXCsq998c8hbhSTI/azTty1Z6N53dmAudKHhy573j3yvRLsX2BSp8YpLtoCEG8E9DJe+8zUh4A==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/object-schema@2.1.7': + resolution: {integrity: sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/plugin-kit@0.4.1': + resolution: {integrity: sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@humanfs/core@0.19.2': + resolution: {integrity: sha512-UhXNm+CFMWcbChXywFwkmhqjs3PRCmcSa/hfBgLIb7oQ5HNb1wS0icWsGtSAUNgefHeI+eBrA8I1fxmbHsGdvA==} + engines: {node: '>=18.18.0'} + + '@humanfs/node@0.16.8': + resolution: {integrity: sha512-gE1eQNZ3R++kTzFUpdGlpmy8kDZD/MLyHqDwqjkVQI0JMdI1D51sy1H958PNXYkM2rAac7e5/CnIKZrHtPh3BQ==} + engines: {node: '>=18.18.0'} + + '@humanfs/types@0.15.0': + resolution: {integrity: sha512-ZZ1w0aoQkwuUuC7Yf+7sdeaNfqQiiLcSRbfI08oAxqLtpXQr9AIVX7Ay7HLDuiLYAaFPu8oBYNq/QIi9URHJ3Q==} + engines: {node: '>=18.18.0'} + + '@humanwhocodes/module-importer@1.0.1': + resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} + engines: {node: '>=12.22'} + + '@humanwhocodes/retry@0.4.3': + resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==} + engines: {node: '>=18.18'} + + '@img/colour@1.1.0': + resolution: {integrity: sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ==} + engines: {node: '>=18'} + + '@img/sharp-darwin-arm64@0.35.3': + resolution: {integrity: sha512-RMnFX7YQsMoh7lWfcM4NEHHymBX/rLuKNPVM84XE9ONPcaSCDgE7CHIHpSgPcO2xcRthgBy1HfNO319mwhIAkg==} + engines: {node: '>=20.9.0'} + cpu: [arm64] + os: [darwin] + + '@img/sharp-darwin-x64@0.35.3': + resolution: {integrity: sha512-Xo+5uFBtLN0BKqieTxiFzFPQAUlBbbH5iBKyRX/z1JrbnYsHTfKJnUfL8+p2TPXr1pXqao4eeL4Rl144uDpK9w==} + engines: {node: '>=20.9.0'} + cpu: [x64] + os: [darwin] + + '@img/sharp-freebsd-wasm32@0.35.3': + resolution: {integrity: sha512-lUxcqWIj2wMQ9BrwNjngcr1gWUr5xgaGThBRqPPalIC2n67Cqj1uPh8NnA/ZhAg8hUbKl+kVHKwgUIwe6ZYPrg==} + engines: {node: '>=20.9.0'} + os: [freebsd] + + '@img/sharp-libvips-darwin-arm64@1.3.2': + resolution: {integrity: sha512-9J6ypZFpQBj4YnePGoq/S38w6nz+vqg5WZLrLGY4YuSemdMq47GMLBPO42MzwdGwpg/agZ7xzZcFHa48xlywfg==} + cpu: [arm64] + os: [darwin] + + '@img/sharp-libvips-darwin-x64@1.3.2': + resolution: {integrity: sha512-m2pW1n6cns9VaubNwsZ+c3CRYjxNQWgJ5gPlnL1nbBcpkBvFm6SCFN5o0psFHI8w9n11NKhFkeEDns98tiqbEw==} + cpu: [x64] + os: [darwin] + + '@img/sharp-libvips-linux-arm64@1.3.2': + resolution: {integrity: sha512-dqVSFynCox4C/J8kT16V7SIFAns0IjgLwkvYT7p8LQVmJ5OS5b6tI9IGflxTeuBS//zXeFIUbwt5dwxyZ17cnA==} + cpu: [arm64] + os: [linux] + + '@img/sharp-libvips-linux-arm@1.3.2': + resolution: {integrity: sha512-1eMLzy92I4J6rmi4mAT8yC3HxOtniyGELlzGbNMLLeqe052ahFQ0h6LFq+lh5DsDIdYViIDst08abvSbcEdLXQ==} + cpu: [arm] + os: [linux] + + '@img/sharp-libvips-linux-ppc64@1.3.2': + resolution: {integrity: sha512-3z0NHDxD6n5I9gc05U1eW1AyRm+Gznzq3naMrthPNqE6oYykcogW0l/jfpJdjYnuNl8R7yI9pNbE1XiUeyq0Aw==} + cpu: [ppc64] + os: [linux] + + '@img/sharp-libvips-linux-riscv64@1.3.2': + resolution: {integrity: sha512-bsb4rI+NldGOsXuej2r8OdSS8+zXDVaCWxyWrcv6kneTOlgAHtZABRzBBCwdsPiD90J4myNJuHpg6kA20ImW/w==} + cpu: [riscv64] + os: [linux] + + '@img/sharp-libvips-linux-s390x@1.3.2': + resolution: {integrity: sha512-/ABshyj8gCpyIrNXnHn4LorDJ0HHm1VhXPBlxZ8zAtfVPAaSafXPGn+sUSIRiwaSBy0mmFjSjiXI5mkcwdChKQ==} + cpu: [s390x] + os: [linux] + + '@img/sharp-libvips-linux-x64@1.3.2': + resolution: {integrity: sha512-ITPEtgffGJ0S6G9dRyw/366tJQqFRcHWPHhC+Stpg3Z8AEMrDrTr2lhdz4f/Y/HMbRh//7Z5mBzEpVdi62Oc3w==} + cpu: [x64] + os: [linux] + + '@img/sharp-libvips-linuxmusl-arm64@1.3.2': + resolution: {integrity: sha512-zE9EdiUzUmg5mDT5a1rk5fYJ6GWPloTwWBYDS14naqHsL+EaMpDj1AWnpLgh3u0YCORv2Tt50wrcrpYqkP97Kw==} + cpu: [arm64] + os: [linux] + + '@img/sharp-libvips-linuxmusl-x64@1.3.2': + resolution: {integrity: sha512-m0lrLiUt+lBYnCFr8qV/65yMR4E/c7/wf78I5eKTdkEakFAlZ9QlzEM3QIhhAwVeUhLAHLcCq7a7Vszq/oFNZQ==} + cpu: [x64] + os: [linux] + + '@img/sharp-linux-arm64@0.35.3': + resolution: {integrity: sha512-QgKDspHPnrU+GQ55XPhGwyhC8acLVOOSyAvo1oVfFmrIXLkDNmGWzAfDZ4xK8oSA1qBQrALcHX0G5UZni/SuFQ==} + engines: {node: '>=20.9.0'} + cpu: [arm64] + os: [linux] + + '@img/sharp-linux-arm@0.35.3': + resolution: {integrity: sha512-affVWCTLooy8TSxbDx2qkzuDeaWLNVBA+P//FNBirHsXpP2fuBhk5AuboYUnrDnzoXes8GFjpTx0SBFOCRg+FA==} + engines: {node: '>=20.9.0'} + cpu: [arm] + os: [linux] + + '@img/sharp-linux-ppc64@0.35.3': + resolution: {integrity: sha512-sMd8rDxmpLOwv/7N44klFjOD5DUO7FLdjiXDI0hoxYaf7Ar262dQIEkosE98bps+5HPLtp/EvNqeqQtOycP/IA==} + engines: {node: '>=20.9.0'} + cpu: [ppc64] + os: [linux] + + '@img/sharp-linux-riscv64@0.35.3': + resolution: {integrity: sha512-0Eob78yjlYPfL5vMNWAW55l3R9Y6BQS/gOfe0ZcP9mEz9ohhKSt4im1hayiknXgf8AWrFqMvJcKIdmLmEe7yeQ==} + engines: {node: '>=20.9.0'} + cpu: [riscv64] + os: [linux] + + '@img/sharp-linux-s390x@0.35.3': + resolution: {integrity: sha512-KgAxQ0DxpNOq1rG2t5cgTgShJFGSuU7XO45cqC+1NVOuZnP6tlgZRuSYOfNupGkHID0o3cJOsw4DVeJpMovcGw==} + engines: {node: '>=20.9.0'} + cpu: [s390x] + os: [linux] + + '@img/sharp-linux-x64@0.35.3': + resolution: {integrity: sha512-8pqvxubL2PGdhlPy6GLqzDYMUjyRmKAwKHYKixpdJYBUK7PJ0C029XdsnpFIdgRZG68fZiGdHVWcKPvtiPB4cA==} + engines: {node: '>=20.9.0'} + cpu: [x64] + os: [linux] + + '@img/sharp-linuxmusl-arm64@0.35.3': + resolution: {integrity: sha512-Vz0iQjzzcSX3HCbfwFfCSG/9SCIqyO0mH2sXyiHaAYfBk0cRsCWXRyQYX0ovCK/PAQBbTzQ0dsPQHh5MAFL59w==} + engines: {node: '>=20.9.0'} + cpu: [arm64] + os: [linux] + + '@img/sharp-linuxmusl-x64@0.35.3': + resolution: {integrity: sha512-6O1NPKcDVj9QEdg7Hx549EX8U0rp6yXQERqru6yRN7fGBn32UvIRJUlWnk+8xDCiG76hXVBbX82NZ/ZKr0euIg==} + engines: {node: '>=20.9.0'} + cpu: [x64] + os: [linux] + + '@img/sharp-wasm32@0.35.3': + resolution: {integrity: sha512-cZ0XkcYGpHZkqW6iCkqTcmUC0CD9DhD5d/qeZlZkfRBn6GnHniZXLUo5+9xw8Iv76YE6LQFN9YNBlKREcCG76w==} + engines: {node: '>=20.9.0'} + + '@img/sharp-webcontainers-wasm32@0.35.3': + resolution: {integrity: sha512-2rnq7bX3NzeR2T4YWgz8qiG4h3TSdMe+vN1iQXpJleSJ3SM5zQ8Fy2SyyXAWlbxpEZ2Y+Z4u1BePgJEYbSy80Q==} + engines: {node: '>=20.9.0'} + cpu: [wasm32] + + '@img/sharp-win32-arm64@0.35.3': + resolution: {integrity: sha512-4bPwFdMbeC4JQ8L8LOyWp6nsHcboP5fxkp6iPOXz2Vg49R42TuMs2whkJ5OAP4/Ul035qOzy0AecOF9VOscn4w==} + engines: {node: '>=20.9.0'} + cpu: [arm64] + os: [win32] + + '@img/sharp-win32-ia32@0.35.3': + resolution: {integrity: sha512-r53mXsBN6lFUDiST764SvgwUdHAqM4rPAiDzAmf4fLoB6X/rkfyTrLCg6+g17wJJiCmB3JYgHuUldCWUIRFSXw==} + engines: {node: ^20.9.0} + cpu: [ia32] + os: [win32] + + '@img/sharp-win32-x64@0.35.3': + resolution: {integrity: sha512-D4y1vNeZrIIJCN+uHaWVtH86B+aCrdMYYjicy9pXHvbGZeGYLLSd3wdVuC37FxVXlU1ARsk84eKWfWMXGYEqvA==} + engines: {node: '>=20.9.0'} + cpu: [x64] + os: [win32] + + '@jridgewell/gen-mapping@0.3.13': + resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} + + '@jridgewell/remapping@2.3.5': + resolution: {integrity: sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==} + + '@jridgewell/resolve-uri@3.1.2': + resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} + engines: {node: '>=6.0.0'} + + '@jridgewell/sourcemap-codec@1.5.5': + resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} + + '@jridgewell/trace-mapping@0.3.31': + resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} + + '@napi-rs/wasm-runtime@1.2.3': + resolution: {integrity: sha512-UMduMbqO5s5zF2NkNacMT/yK5Y5QiKvWr2+50bzIIxFDwVJ2h49b+oyjaCGPhJxd2/gC2x39EHv/gHVuu36x2Q==} + engines: {node: ^20.19.0 || ^22.13.0 || >=23.5.0} + peerDependencies: + '@emnapi/core': ^1.7.1 || ^2.0.0-alpha.4 + '@emnapi/runtime': ^1.7.1 || ^2.0.0-alpha.4 + + '@next/env@16.3.2': + resolution: {integrity: sha512-8k4YoG8cM7LWlkfzGNYCRBbFNlernLiMw4s0btVl+CmmWqn3VpYypA72/5Feb1UWdxe6tHqr5KHP4p4Y4m9luA==} + + '@next/eslint-plugin-next@16.3.2': + resolution: {integrity: sha512-z+HW1cZgt8QhByw8p2EbxF94AImgsKIYUbtSkA7Zld2T9yrKAlys4jNOcAOCtv6csX2CoA/5qCVyesL5pHmJ0A==} + + '@next/swc-darwin-arm64@16.3.2': + resolution: {integrity: sha512-ib5Llm93YCKoKWDh6ZaHq6QWTuOZ2bRkSnUwMmX8dsRIOkBNL1vVlSiUKSfixPL9SSh9pvukzqajk/klkn5vqg==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [darwin] + + '@next/swc-darwin-x64@16.3.2': + resolution: {integrity: sha512-qd98fX2+I5nYJDioW2o7nSjoxM5KvWdeDefM80igia4+C/qSIEhH4MhTE+hO/7qKM7W37/Mq+dOWp8UePSyLHw==} + engines: {node: '>= 10'} + cpu: [x64] + os: [darwin] + + '@next/swc-linux-arm64-gnu@16.3.2': + resolution: {integrity: sha512-vqsgb6FAOzcrCccsLXiKtAy5t8EzO+uOazuFaSkQxeY0tNONG3vpHYy8pyBafcI5SNFPTeyard6yTr6SzNGo2A==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + + '@next/swc-linux-arm64-musl@16.3.2': + resolution: {integrity: sha512-xIe1eujfHUB2XcxHGddxJyu6TJRPjC5NpIkQYB/32ESkt5VkQyIAjmLRS38c+s6QY+qjtY/4KarVDzXRuD7lZQ==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + + '@next/swc-linux-x64-gnu@16.3.2': + resolution: {integrity: sha512-Fe0SA2j8X0kmc3aveuHD7UktO3AE2+mH3LguP60vGbz7u0z+MrDXbeb5iZFYAwR7EzzzXJ2Yk966w9mGTFMqfA==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + + '@next/swc-linux-x64-musl@16.3.2': + resolution: {integrity: sha512-TFBipb+gyesI/2Ve4zVu7kGltBWN/R466G5/1gtt2lECfc22G1pjkTxu68Q9aFcOaXiRGTQfvDbQQFe7mYgxiQ==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + + '@next/swc-win32-arm64-msvc@16.3.2': + resolution: {integrity: sha512-rVtmnNpBYIosDnKD/96dKxFsJnwnn1WRGG/HioSe8XCm2ksSHNrd2R6+hSjvTBxeMNhJ9pYeu/90cWB1nQLuNA==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [win32] + + '@next/swc-win32-x64-msvc@16.3.2': + resolution: {integrity: sha512-H4Y2o2/JcHu8LtwzD5CXfHhwxwz8gfsx2HXDEw46Mtev5xHnEmB7HNtZtmriw5ReUOjRtcDqo7XSbU01FT9NlA==} + engines: {node: '>= 10'} + cpu: [x64] + os: [win32] + + '@nodelib/fs.scandir@2.1.5': + resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} + engines: {node: '>= 8'} + + '@nodelib/fs.stat@2.0.5': + resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} + engines: {node: '>= 8'} + + '@nodelib/fs.walk@1.2.8': + resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} + engines: {node: '>= 8'} + + '@nolyfill/is-core-module@1.0.39': + resolution: {integrity: sha512-nn5ozdjYQpUCZlWGuxcJY/KpxkWQs4DcbMCmKojjyrYDEAGy4Ce19NN4v5MduafTwJlbKc99UA8YhSVqq9yPZA==} + engines: {node: '>=12.4.0'} + + '@rtsao/scc@1.1.0': + resolution: {integrity: sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==} + + '@standard-schema/spec@1.1.0': + resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==} + + '@swc/helpers@0.5.23': + resolution: {integrity: sha512-5lSsMOTXURePglDfvuAQUqkGek9Hg2kksOYay2m0+XR++b2NWYL/4sWyuvVBIs8oKnJaxkdi9whaL/sqN13afw==} + + '@tailwindcss/node@4.3.3': + resolution: {integrity: sha512-/T8IKEsf9VTU6tLjgC7+sv2mOPtQxzE2jMw7u4Tt40Tx+QSZxpzh95/H6cMKoja9XuW7iMdLJYBB0o9G1CaAgg==} + + '@tailwindcss/oxide-android-arm64@4.3.3': + resolution: {integrity: sha512-Y85A2gmPSkl5Ve5qR86GL4HT509cFqQh1aes9p3sSkyTPwt0Pppf3GkwGe4JPACcRYjgJIEhQgM6dBClnr0NYw==} + engines: {node: '>= 20'} + cpu: [arm64] + os: [android] + + '@tailwindcss/oxide-darwin-arm64@4.3.3': + resolution: {integrity: sha512-BiaWatpBcERQFDlOjRDpIVXuFK5PJez5SA4JMg6VYZdBYU+qKfV/vqjcIs+IYmtitf1xYQZTwXvU/8y4lfZUGw==} + engines: {node: '>= 20'} + cpu: [arm64] + os: [darwin] + + '@tailwindcss/oxide-darwin-x64@4.3.3': + resolution: {integrity: sha512-fAeUqfV5ndhxRwai8cXGzdLvul9utWOmeTkv69unv4ZXixjn61Z+p9lCWdwOwA3TYboG3BwdVuN/RDjhBRl0mw==} + engines: {node: '>= 20'} + cpu: [x64] + os: [darwin] + + '@tailwindcss/oxide-freebsd-x64@4.3.3': + resolution: {integrity: sha512-iyf5bV6+wnAlflVeEy7R25dupxTNECZN5QMI0qNT6eT+EgaGdZcKhGkr5SdoaWiLJ3spLqIY9VCeSGrwmtg4kw==} + engines: {node: '>= 20'} + cpu: [x64] + os: [freebsd] + + '@tailwindcss/oxide-linux-arm-gnueabihf@4.3.3': + resolution: {integrity: sha512-aAYUprJAJQWWbRrPvtjdroZ56Md+JM8pMiopS6xGEwDfLhqj+2ver2p4nU4Mb3CRqcMmNBjo8KkUgcxhkzVQGQ==} + engines: {node: '>= 20'} + cpu: [arm] + os: [linux] + + '@tailwindcss/oxide-linux-arm64-gnu@4.3.3': + resolution: {integrity: sha512-nDxldcEENOxZRzC2uu9jrutZdAAQtb+8WWDCSnWL1zvBk1+FN+x6MtDViPB5AJMfttVCUhehGWus3XBPgatM/w==} + engines: {node: '>= 20'} + cpu: [arm64] + os: [linux] + + '@tailwindcss/oxide-linux-arm64-musl@4.3.3': + resolution: {integrity: sha512-Md44bD6veX/PC5iyF8cDVnw4HBIANZepRZZ7a8DQOvkfo5WUBwcp6iAuCUz23u+4SUkhJlD3eL7hNdW8ezd/kA==} + engines: {node: '>= 20'} + cpu: [arm64] + os: [linux] + + '@tailwindcss/oxide-linux-x64-gnu@4.3.3': + resolution: {integrity: sha512-tx7us1muwOKAKWao2v/GaafFeQboE6aj88vC6ziN2NCGcRm8gWUhwjzg+YdVB1e4boAtdtma4L43onunI6NS4w==} + engines: {node: '>= 20'} + cpu: [x64] + os: [linux] + + '@tailwindcss/oxide-linux-x64-musl@4.3.3': + resolution: {integrity: sha512-SJxX60smvHgasZoBy11dX6YRjXJFovwWBoedhbQPOBzgFWBHGB+TVPWB9BxzR7TTxU8FQZAI2AyiNCMzFm8Img==} + engines: {node: '>= 20'} + cpu: [x64] + os: [linux] + + '@tailwindcss/oxide-wasm32-wasi@4.3.3': + resolution: {integrity: sha512-jx1+rPhY/5Ympkktd656HBWEBLxP7dH06losBLjjf5vgCODXvi9KhtftWcMIwTFIDqBr7cRnQkdLnAG+IOlGvQ==} + engines: {node: '>=14.0.0'} + cpu: [wasm32] + bundledDependencies: + - '@napi-rs/wasm-runtime' + - '@emnapi/core' + - '@emnapi/runtime' + - '@tybys/wasm-util' + - '@emnapi/wasi-threads' + - tslib + + '@tailwindcss/oxide-win32-arm64-msvc@4.3.3': + resolution: {integrity: sha512-3rc292Ca2ceK6Ulcc/bAVnTs/3nDtoPhyEKlgPv+yQJQi/JS/AMJlqzxvlDacL1nekbrcf6bTqp/jV4qgnPxNQ==} + engines: {node: '>= 20'} + cpu: [arm64] + os: [win32] + + '@tailwindcss/oxide-win32-x64-msvc@4.3.3': + resolution: {integrity: sha512-yJ0pwIVc/nYeGoV02WtsN8KYyLQv7kyI2wDnkezyJlGGjkd4QLwDGAwl47YpPJeuI0M0ObaXGSPjvWDPeTPggw==} + engines: {node: '>= 20'} + cpu: [x64] + os: [win32] + + '@tailwindcss/oxide@4.3.3': + resolution: {integrity: sha512-krXjAikiaFSPaK/FkAQT5UTx3VormQaiZ5hBFlJZ9UFQGB/rwg1MZIhHAG9smMQRTdyJxP6Qt5MwMtdyU5FWrA==} + engines: {node: '>= 20'} + + '@tailwindcss/postcss@4.3.3': + resolution: {integrity: sha512-JTSZZGQi1AyKirbLN3azmjVzef92tcX7h+iSqPdaeStyFpGpDlKvvpxeOE8njhbUanbRwr3z8DyzhICWnMtQeg==} + + '@tybys/wasm-util@0.10.3': + resolution: {integrity: sha512-F3fo1MYrRJYL3zER0OUOmkutjr1Vp23m7OsSgp7nq4SP6OqX6C/56XFIPAl5bt3zaBRjmW7SGz3u/6LwFpYcOg==} + + '@types/estree@1.0.9': + resolution: {integrity: sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==} + + '@types/json-schema@7.0.15': + resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} + + '@types/json5@0.0.29': + resolution: {integrity: sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==} + + '@types/node@20.19.43': + resolution: {integrity: sha512-6oYBAi5ikg4Pl+kGsoYtawUMBT2zZMCvPNF7pVLnHZfd1zf38DRiWn/gT01RYCdUqkv7Fhr+C9ot4/tb+2sVvA==} + + '@types/react-dom@19.2.5': + resolution: {integrity: sha512-fMPwH9v7r/pp43yUd2/Mbiex5KouJwwR3dzHkhLREUC6764VyDsqxhAxv6OFEYR1RhjOyD1naqba8ECDBe7ZQg==} + peerDependencies: + '@types/react': ^19.2.0 + + '@types/react@19.2.18': + resolution: {integrity: sha512-AnzbBERsrLKtk2XSfTbYRLjQPdy116Sty4q+T+Bp3IC4l6jNBvreVPAHmpq9qhXQM7CXZPjLVmGMw9sy+hxQ3w==} + + '@typescript-eslint/eslint-plugin@8.68.0': + resolution: {integrity: sha512-WASHDpCm6qO5jj9g1a+8NiW5+GCkAyLReR56/4VruYmNgfUmqpxOfZ2Yfb8xGfJPWv5Qi6LSD8sXdces3vbp/Q==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + '@typescript-eslint/parser': ^8.68.0 + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: '>=4.8.4 <6.1.0' + + '@typescript-eslint/parser@8.68.0': + resolution: {integrity: sha512-fHq2VC1kpyYfvEcbiMjOpySY4WS7voEp89yAThrHRX5sm9j2lzYppCb2umFMEed4fWcyeLjHxrz0mpjNBaBxMQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: '>=4.8.4 <6.1.0' + + '@typescript-eslint/project-service@8.68.0': + resolution: {integrity: sha512-5GQtWZCXFcFYux955pvoS02WLc49pXNlvIxocKjS0clvwo3in1RdlzVKyiqQH9vE5AKWFLTaUgeQkOrTS+0Qxw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.1.0' + + '@typescript-eslint/scope-manager@8.68.0': + resolution: {integrity: sha512-T5eXpcaJNg8bhjHJ8Rjp68Vq/QBteYtTKY8TZqVNPaUbuz0f6jI9t6aDkylwvalpAB9XTTFeFOjrjXAZ3YvmVA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@typescript-eslint/tsconfig-utils@8.68.0': + resolution: {integrity: sha512-F7zrGQfiJHojPwi8vhxZQC1tWtJzvL74cK/nqri2lk8YUXvYaYwl263xOJ69jDWPUk1hmcdoayFwk9lX09npVw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.1.0' + + '@typescript-eslint/type-utils@8.68.0': + resolution: {integrity: sha512-X77zqoY1EjeWGs/0JNxeaMfp5C5lIz4Tw8y66F1Ne8Faq6g424sBNYM6xBAqElfGZPLpWS+CZAp0DXyKDzWiHg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: '>=4.8.4 <6.1.0' + + '@typescript-eslint/types@8.68.0': + resolution: {integrity: sha512-9RnpsGJjrAllCMefGVVsImJM24YurhC0Q1h4UbvivtvOqXmR/vEJge2OoE++z9m6hyg8T1Q8t5SNT6tHSbrxcg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@typescript-eslint/typescript-estree@8.68.0': + resolution: {integrity: sha512-OKKsD0tYmoNiU5PW2zehO1yO56jYOm1ShYlxon/Z0SJNidAkdVg86eg9ruRuoXf8xfnuWZGbwDsStkoXbZtIIA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.1.0' + + '@typescript-eslint/utils@8.68.0': + resolution: {integrity: sha512-PB5gJMMOg0Q5P1tsgWtEAqQacJXq0qEqRHDX/YJ4FaTMLfZPpHB3gjl2EJuiZyPABxmj4ZQYiY9m1bdAJ5y7tQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: '>=4.8.4 <6.1.0' + + '@typescript-eslint/visitor-keys@8.68.0': + resolution: {integrity: sha512-YR65gGdGvTUAWLldC3xLOvOzamdGzB4A5/N8rehEaHs3Zvoe39BhgY+u0SPch1OvrVTfLcc55wsSgK2NcnTS/A==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@unrs/resolver-binding-android-arm-eabi@1.12.2': + resolution: {integrity: sha512-g5T90pqg1bo/7mytQx6F4iBNC0Wsh9cu+z9veDbFjc7HjpesJFWD7QMS0NGStXM075+7dJPPVvBbpZlnrdpi/w==} + cpu: [arm] + os: [android] + + '@unrs/resolver-binding-android-arm64@1.12.2': + resolution: {integrity: sha512-YGCRZv/9GLhwmz6mYDeTsm/92BAyR28l6c2ReweVW5pWgfsitWLY8upvfRlGdoyD8HjeTHSYJWyZGD4KJA/nFQ==} + cpu: [arm64] + os: [android] + + '@unrs/resolver-binding-darwin-arm64@1.12.2': + resolution: {integrity: sha512-u9DiNT1auQMO20A9SyTuG3wUgQWB9Z7KjAg0uFuCDR1FsAY8A0CG2S6JpHS1xwm/w1G08bjXZDcyOCjv1WAm2w==} + cpu: [arm64] + os: [darwin] + + '@unrs/resolver-binding-darwin-x64@1.12.2': + resolution: {integrity: sha512-f7rPLi/T1HVKZu/u6t87lroib16n8vrSzcyxI7lg4BGO9UF26KhQL44sd9eOUgrTYhvRXtWOIZT5PejdPyJfUA==} + cpu: [x64] + os: [darwin] + + '@unrs/resolver-binding-freebsd-x64@1.12.2': + resolution: {integrity: sha512-BpcOjWCJub6nRZUS2zA20pmLvjtqAtGejETaIyRLiZiQf++cbrjltLA5NN/xaXfqeOBOSlMFbemIl5/S5tljmg==} + cpu: [x64] + os: [freebsd] + + '@unrs/resolver-binding-linux-arm-gnueabihf@1.12.2': + resolution: {integrity: sha512-vZTDvdSISZjJx66OzJqtsOhzifbqRjbmI1Mnu49fQDwog5GtDI4QidRiEAYbZCRj9C8YZEW+3ZjqsyS9GR4k2A==} + cpu: [arm] + os: [linux] + + '@unrs/resolver-binding-linux-arm-musleabihf@1.12.2': + resolution: {integrity: sha512-BiPI+IrIlwcW4nLLMM21+B1dFPzd55yAVgVGrdgDjNef+ch03GdxrcyaIz8X9SsQirh/kCQ7mviyWlMxdh2D7g==} + cpu: [arm] + os: [linux] + + '@unrs/resolver-binding-linux-arm64-gnu@1.12.2': + resolution: {integrity: sha512-zJc0H99FEPoFfSrNpa91HYfxzfAJCr502oxNK1cfdC9hlaFI43RT+JFCann9JUgZmLzzntChHyn13Sgn9ljHNg==} + cpu: [arm64] + os: [linux] + + '@unrs/resolver-binding-linux-arm64-musl@1.12.2': + resolution: {integrity: sha512-KQ3Lki6l+Pz1k/eBipN41ES+YUK30beLGb9YqcB1O542cyLCNE6GaxrfcY3T6EezmGGk84wb5XyO9loTM9tkcA==} + cpu: [arm64] + os: [linux] + + '@unrs/resolver-binding-linux-loong64-gnu@1.12.2': + resolution: {integrity: sha512-3SJGEh1DborhG6pyxvhPzCT4bbSIVihsvgJc13P1bHG7KLdNDaF9T3gsTwFc7Jw/5Y5/iWOjkEx7Zy0NvCGX3Q==} + cpu: [loong64] + os: [linux] + + '@unrs/resolver-binding-linux-loong64-musl@1.12.2': + resolution: {integrity: sha512-jiuG/Obbel7uw1PwHNFfrkiKhLAF6mnyZ6aWlOAVN9WqKm8v0OFGnciJIHu8+CMvXLQ8AD51LPzAoUfT21D5Ew==} + cpu: [loong64] + os: [linux] + + '@unrs/resolver-binding-linux-ppc64-gnu@1.12.2': + resolution: {integrity: sha512-q7xRvVpmcfeL+LlZg8Pbbo6QaTZwDU5BaGZbwfhkEsXJn3Was8xYfE0RBH266xZt0rM6B7i8xAYIvjthuUIWHg==} + cpu: [ppc64] + os: [linux] + + '@unrs/resolver-binding-linux-riscv64-gnu@1.12.2': + resolution: {integrity: sha512-0CVdx6lcnT3Q9inOH8tsMIOJ6ImndllMjqJHg8RLVdB7Vq4SfkEXl9mCSsVNuNA4MCYycRicCUxPCabVHJRr6A==} + cpu: [riscv64] + os: [linux] + + '@unrs/resolver-binding-linux-riscv64-musl@1.12.2': + resolution: {integrity: sha512-iOwlRo9vnp6R6ohHQS11n0NnfdXx/omhkocmIfaPRpQhKZ+3BDMkkdRVh53qjkFkpPddf+FETA28NwGN7l5l+w==} + cpu: [riscv64] + os: [linux] + + '@unrs/resolver-binding-linux-s390x-gnu@1.12.2': + resolution: {integrity: sha512-HYJtLfXq94q8iZNFT1lknx258wlkkWhZeUXJRqzKBBUJ00CvZ+N33zgbCqimLjsyw5Va6uUxhVa12mI+kaveEw==} + cpu: [s390x] + os: [linux] + + '@unrs/resolver-binding-linux-x64-gnu@1.12.2': + resolution: {integrity: sha512-mPsUhunKKDih5O96Y6enDQyHc1SqBPlY1E/SfMWDM3EdJ95Z9CArPeCVwCCqbP45ljvivdEk8Fxn+SIb1rDAJQ==} + cpu: [x64] + os: [linux] + + '@unrs/resolver-binding-linux-x64-musl@1.12.2': + resolution: {integrity: sha512-azrt6+5ydLd8Vt210AAFis/lZevSfPw93EJRIJG+xPu4WCJ8K0kppCTpMyLPcKT7H15M4Jnt2tMp5bOvCkRC6A==} + cpu: [x64] + os: [linux] + + '@unrs/resolver-binding-openharmony-arm64@1.12.2': + resolution: {integrity: sha512-YZ9hP4O0X9PQb8eO980qmLNGH4zT3I9+SZTdt0Pr0YyuGQhYKoOZkV02VzrzyOZJ5xIJ3UFIenKkUkGg8GjgWQ==} + cpu: [arm64] + os: [openharmony] + + '@unrs/resolver-binding-wasm32-wasi@1.12.2': + resolution: {integrity: sha512-tYFDIkMxSflfEc/h92ZWNsZlHSwgimbNHSO3PL2JWQHfCuC2q316jMyYU9TIWZsFK2bQwyK5VAdYgn8ygPj69A==} + engines: {node: '>=14.0.0'} + cpu: [wasm32] + + '@unrs/resolver-binding-win32-arm64-msvc@1.12.2': + resolution: {integrity: sha512-qzNyg3xL0VPQmCaUh+N5jSitce6k+uCBfMDesWRnlULOZaqUkaJ0ybdT+UqlAWJoQjuqfIU/0Ptx9bteN4D82g==} + cpu: [arm64] + os: [win32] + + '@unrs/resolver-binding-win32-ia32-msvc@1.12.2': + resolution: {integrity: sha512-WD9sY00OfpHVGfsnHZoA8jVT+esS/Bg8z8jzxp5BnDCjjwsuKsPQrzswwpFy4J1AUJbXPRfkpcX0mXrzeXW79g==} + cpu: [ia32] + os: [win32] + + '@unrs/resolver-binding-win32-x64-msvc@1.12.2': + resolution: {integrity: sha512-nAB74NfSNKknqQ1RrYj6uz8FcXEomu/MATJZxh/x+BArzN2U3JbOYC0APYzUIGhVY3m5hRxA8VPNdPBoG8txlA==} + cpu: [x64] + os: [win32] + + '@vercel/oidc@3.2.0': + resolution: {integrity: sha512-UycprH3T6n3jH0k44NHMa7pnFHGu/N05MjojYr+Mc6I7obkoLIJujSWwin1pCvdy/eOxrI/l3uDLQsmcrOb4ug==} + engines: {node: '>= 20'} + + '@workflow/serde@4.1.0': + resolution: {integrity: sha512-pav4F2BoirECWR7Nf1TKt+2eETcBj7jj4cBefQ8VXQCA6NPkaKeLfj/zMgi+3zYV5ZIBT4GuUiphsj0/b9hPQQ==} + + acorn-jsx@5.3.2: + resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} + peerDependencies: + acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 + + acorn@8.18.0: + resolution: {integrity: sha512-lGq+9yr1/GuAWaVYIHRjvvySG5/4VfKIvC8EWxStPdcDh/Ka7FG3twP6v4d5BkravUilhIAsG4Qj83t02LWUPQ==} + engines: {node: '>=0.4.0'} + hasBin: true + + ai@7.0.79: + resolution: {integrity: sha512-zZkSUq6MgmXjwIfML2wkPFeQ5Azk8kGEWkciAtE0NgnFy3slj4ljsw5LvigsCbYXxTSVr+6iCHBTPZafpx1P0w==} + engines: {node: '>=22'} + peerDependencies: + zod: ^3.25.76 || ^4.1.8 + + ajv@6.15.0: + resolution: {integrity: sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==} + + ansi-styles@4.3.0: + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} + engines: {node: '>=8'} + + argparse@2.0.1: + resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + + aria-query@5.3.2: + resolution: {integrity: sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==} + engines: {node: '>= 0.4'} + + array-buffer-byte-length@1.0.2: + resolution: {integrity: sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==} + engines: {node: '>= 0.4'} + + array-includes@3.1.9: + resolution: {integrity: sha512-FmeCCAenzH0KH381SPT5FZmiA/TmpndpcaShhfgEN9eCVjnFBqq3l1xrI42y8+PPLI6hypzou4GXw00WHmPBLQ==} + engines: {node: '>= 0.4'} + + array.prototype.findlast@1.2.5: + resolution: {integrity: sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ==} + engines: {node: '>= 0.4'} + + array.prototype.findlastindex@1.2.6: + resolution: {integrity: sha512-F/TKATkzseUExPlfvmwQKGITM3DGTK+vkAsCZoDc5daVygbJBnjEUCbgkAvVFsgfXfX4YIqZ/27G3k3tdXrTxQ==} + engines: {node: '>= 0.4'} + + array.prototype.flat@1.3.3: + resolution: {integrity: sha512-rwG/ja1neyLqCuGZ5YYrznA62D4mZXg0i1cIskIUKSiqF3Cje9/wXAls9B9s1Wa2fomMsIv8czB8jZcPmxCXFg==} + engines: {node: '>= 0.4'} + + array.prototype.flatmap@1.3.3: + resolution: {integrity: sha512-Y7Wt51eKJSyi80hFrJCePGGNo5ktJCslFuboqJsbf57CCPcm5zztluPlc4/aD8sWsKvlwatezpV4U1efk8kpjg==} + engines: {node: '>= 0.4'} + + array.prototype.tosorted@1.1.4: + resolution: {integrity: sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA==} + engines: {node: '>= 0.4'} + + arraybuffer.prototype.slice@1.0.4: + resolution: {integrity: sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==} + engines: {node: '>= 0.4'} + + ast-types-flow@0.0.8: + resolution: {integrity: sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ==} + + async-function@1.0.0: + resolution: {integrity: sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==} + engines: {node: '>= 0.4'} + + available-typed-arrays@1.0.7: + resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==} + engines: {node: '>= 0.4'} + + axe-core@4.13.0: + resolution: {integrity: sha512-UzGt8zg7Ny8djbYMhxl2zuEevVa7r2gJjYY5Lwr1xM7+XU2nd6CkIWFTVcCIbAP63vSz71NaVyyuSk9lHKcy0A==} + engines: {node: '>=4'} + + axobject-query@4.1.0: + resolution: {integrity: sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==} + engines: {node: '>= 0.4'} + + balanced-match@1.0.2: + resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + + balanced-match@4.0.4: + resolution: {integrity: sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==} + engines: {node: 18 || 20 || >=22} + + baseline-browser-mapping@2.11.19: + resolution: {integrity: sha512-Grytf1xOxOEMTGRwx6rLGKkTabd4vMg3VrKdj/7joCmV0qgh4QwMMO6xh34YEXQqirAuUdgQGa5orJQQ+69RBw==} + engines: {node: '>=6.0.0'} + hasBin: true + + brace-expansion@1.1.18: + resolution: {integrity: sha512-Edep/X9fGqVNmzKBVsDYIOtD+z1tuezV70LBjdCst9Tqu76lsnvRiZ6oTic1n+/BIwX6QDGAO94PN4N2SADvtw==} + + brace-expansion@5.0.9: + resolution: {integrity: sha512-ScQ4IuvIEF1TMlP7Zt+vjJ//9zlPb2SDcxWxM3bk8s6t6GGdJ7KO1dCcTidOPJKePW30LE/2cT7wCyPho9/Wxg==} + engines: {node: 20 || >=22} + + braces@3.0.3: + resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} + engines: {node: '>=8'} + + browserslist@4.28.8: + resolution: {integrity: sha512-V2NpofLblG64mfOtSgDhOJESZEGogzDMBv/q+W6oc4LXWP/q75eOXoOaaOu1EOadB9U4Bwx/e0yzbvwKH8zalA==} + engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} + hasBin: true + + call-bind-apply-helpers@1.0.2: + resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} + engines: {node: '>= 0.4'} + + call-bind@1.0.9: + resolution: {integrity: sha512-a/hy+pNsFUTR+Iz8TCJvXudKVLAnz/DyeSUo10I5yvFDQJBFU2s9uqQpoSrJlroHUKoKqzg+epxyP9lqFdzfBQ==} + engines: {node: '>= 0.4'} + + call-bound@1.0.4: + resolution: {integrity: sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==} + engines: {node: '>= 0.4'} + + callsites@3.1.0: + resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} + engines: {node: '>=6'} + + caniuse-lite@1.0.30001810: + resolution: {integrity: sha512-TITQPUkaz+aVk5GL6NhOdwk1aEaNTSDPsGFWrTuhKGtjTF70jL/Oht2W4c6rXUe5fu7Ie19VIahAXHIIiWWNeg==} + + chalk@4.1.2: + resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} + engines: {node: '>=10'} + + client-only@0.0.1: + resolution: {integrity: sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==} + + color-convert@2.0.1: + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} + engines: {node: '>=7.0.0'} + + color-name@1.1.4: + resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + + concat-map@0.0.1: + resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + + convert-source-map@2.0.0: + resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} + + cross-spawn@7.0.6: + resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} + engines: {node: '>= 8'} + + csstype@3.2.3: + resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==} + + damerau-levenshtein@1.0.8: + resolution: {integrity: sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==} + + data-view-buffer@1.0.2: + resolution: {integrity: sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==} + engines: {node: '>= 0.4'} + + data-view-byte-length@1.0.2: + resolution: {integrity: sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==} + engines: {node: '>= 0.4'} + + data-view-byte-offset@1.0.1: + resolution: {integrity: sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==} + engines: {node: '>= 0.4'} + + debug@3.2.7: + resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + debug@4.4.3: + resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + deep-is@0.1.4: + resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} + + define-data-property@1.1.4: + resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==} + engines: {node: '>= 0.4'} + + define-properties@1.2.1: + resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==} + engines: {node: '>= 0.4'} + + dequal@2.0.3: + resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} + engines: {node: '>=6'} + + detect-libc@2.1.2: + resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} + engines: {node: '>=8'} + + doctrine@2.1.0: + resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==} + engines: {node: '>=0.10.0'} + + dunder-proto@1.0.1: + resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} + engines: {node: '>= 0.4'} + + electron-to-chromium@1.5.414: + resolution: {integrity: sha512-aYlviXiaXBbzvKgyALpcMmqa3Np3sDr0XnZbEG62n2UpZFbEcjQ4EEMOLGzVPhwVnwTz0lvKY+GcARbunuHekw==} + + emoji-regex@9.2.2: + resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} + + enhanced-resolve@5.24.5: + resolution: {integrity: sha512-L1l8TNvomm6UVW5B253AGxQagSQr+vGwhMlrrfRS2qmhx46AMpMVJKQYLvWYbysTMY8VoicOvzHzoHMbyzB+4A==} + engines: {node: '>=10.13.0'} + + es-abstract-get@1.0.0: + resolution: {integrity: sha512-6PMWXpdhshVvFp+FoWYs1EvG1Nj0tvk0dZM+XcK0xMEM1czRVcP6ohqPWHy6qPagSpC8j4+p89WXlT+xXJs/fg==} + engines: {node: '>= 0.4'} + + es-abstract@1.24.2: + resolution: {integrity: sha512-2FpH9Q5i2RRwyEP1AylXe6nYLR5OhaJTZwmlcP0dL/+JCbgg7yyEo/sEK6HeGZRf3dFpWwThaRHVApXSkW3xeg==} + engines: {node: '>= 0.4'} + + es-define-property@1.0.1: + resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==} + engines: {node: '>= 0.4'} + + es-errors@1.3.0: + resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} + engines: {node: '>= 0.4'} + + es-iterator-helpers@1.4.0: + resolution: {integrity: sha512-c/A0P0oxkACDc+cKWw8evLXK83oBKgn0qPOqCYT4x9uolpCIJAcYvJC9QYKNDRPsTeGyCrQ326jrvgZWdCdK5Q==} + engines: {node: '>= 0.4'} + + es-object-atoms@1.1.2: + resolution: {integrity: sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==} + engines: {node: '>= 0.4'} + + es-set-tostringtag@2.1.0: + resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==} + engines: {node: '>= 0.4'} + + es-shim-unscopables@1.1.0: + resolution: {integrity: sha512-d9T8ucsEhh8Bi1woXCf+TIKDIROLG5WCkxg8geBCbvk22kzwC5G2OnXVMO6FUsvQlgUUXQ2itephWDLqDzbeCw==} + engines: {node: '>= 0.4'} + + es-to-primitive@1.3.4: + resolution: {integrity: sha512-yPDz7wqpg1/mmHLmS3tcfTfbw5f1eryXvyghYBffGdERwe+mV7ZcWzTR8LR17Kvqt3qfPurjlonmnq3MKXIOXw==} + engines: {node: '>= 0.4'} + + escalade@3.2.0: + resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} + engines: {node: '>=6'} + + escape-string-regexp@4.0.0: + resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} + engines: {node: '>=10'} + + eslint-config-next@16.3.2: + resolution: {integrity: sha512-gTABOJmyc6pEgSX1Z1VOjBxkSmo5Hkdj+ePclDf8HLGTsnVWzgtDdrOeQrAnUkf0JNJJhwPuXrsIwmFZRKJLoQ==} + peerDependencies: + eslint: '>=9.0.0' + typescript: '>=3.3.1' + peerDependenciesMeta: + typescript: + optional: true + + eslint-import-resolver-node@0.3.10: + resolution: {integrity: sha512-tRrKqFyCaKict5hOd244sL6EQFNycnMQnBe+j8uqGNXYzsImGbGUU4ibtoaBmv5FLwJwcFJNeg1GeVjQfbMrDQ==} + + eslint-import-resolver-typescript@3.10.1: + resolution: {integrity: sha512-A1rHYb06zjMGAxdLSkN2fXPBwuSaQ0iO5M/hdyS0Ajj1VBaRp0sPD3dn1FhME3c/JluGFbwSxyCfqdSbtQLAHQ==} + engines: {node: ^14.18.0 || >=16.0.0} + peerDependencies: + eslint: '*' + eslint-plugin-import: '*' + eslint-plugin-import-x: '*' + peerDependenciesMeta: + eslint-plugin-import: + optional: true + eslint-plugin-import-x: + optional: true + + eslint-module-utils@2.14.0: + resolution: {integrity: sha512-W2WCRZ9Dqntd+2u8jJcVMV2PKulc6RdLgUUoh/yQr3uB6lo/ZOeGx11sv60/8S4QFFKNslAlWhr9u0Ef7ZW6Ig==} + engines: {node: '>=4'} + peerDependencies: + '@typescript-eslint/parser': '*' + eslint: '*' + eslint-import-resolver-node: '*' + eslint-import-resolver-typescript: '*' + eslint-import-resolver-webpack: '*' + peerDependenciesMeta: + '@typescript-eslint/parser': + optional: true + eslint: + optional: true + eslint-import-resolver-node: + optional: true + eslint-import-resolver-typescript: + optional: true + eslint-import-resolver-webpack: + optional: true + + eslint-plugin-import@2.32.0: + resolution: {integrity: sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA==} + engines: {node: '>=4'} + peerDependencies: + '@typescript-eslint/parser': '*' + eslint: ^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 || ^9 + peerDependenciesMeta: + '@typescript-eslint/parser': + optional: true + + eslint-plugin-jsx-a11y@6.10.2: + resolution: {integrity: sha512-scB3nz4WmG75pV8+3eRUQOHZlNSUhFNq37xnpgRkCCELU3XMvXAxLk1eqWWyE22Ki4Q01Fnsw9BA3cJHDPgn2Q==} + engines: {node: '>=4.0'} + peerDependencies: + eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9 + + eslint-plugin-react-hooks@7.1.1: + resolution: {integrity: sha512-f2I7Gw6JbvCexzIInuSbZpfdQ44D7iqdWX01FKLvrPgqxoE7oMj8clOfto8U6vYiz4yd5oKu39rRSVOe1zRu0g==} + engines: {node: '>=18'} + peerDependencies: + eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0 || ^10.0.0 + + eslint-plugin-react@7.37.5: + resolution: {integrity: sha512-Qteup0SqU15kdocexFNAJMvCJEfa2xUKNV4CC1xsVMrIIqEy3SQ/rqyxCWNzfrd3/ldy6HMlD2e0JDVpDg2qIA==} + engines: {node: '>=4'} + peerDependencies: + eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7 + + eslint-scope@8.4.0: + resolution: {integrity: sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + eslint-visitor-keys@3.4.3: + resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + eslint-visitor-keys@4.2.1: + resolution: {integrity: sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + eslint-visitor-keys@5.0.1: + resolution: {integrity: sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} + + eslint@9.39.5: + resolution: {integrity: sha512-DgZS62aPLXKlnxILS/AYCoRvHaZeXceIzlXPkkGGzJWSow1aEk0lbTlxUSlyjC8jcaKxAdOnTDz+o1JFSBsyjw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + deprecated: This version is no longer supported. Please see https://eslint.org/version-support for other options. + hasBin: true + peerDependencies: + jiti: '*' + peerDependenciesMeta: + jiti: + optional: true + + espree@10.4.0: + resolution: {integrity: sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + esquery@1.7.0: + resolution: {integrity: sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==} + engines: {node: '>=0.10'} + + esrecurse@4.3.0: + resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} + engines: {node: '>=4.0'} + + estraverse@5.3.0: + resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} + engines: {node: '>=4.0'} + + esutils@2.0.3: + resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} + engines: {node: '>=0.10.0'} + + eventsource-parser@3.1.1: + resolution: {integrity: sha512-EKN1vKAMcZ8MlYMpaNuxN6R9yakzH6uajHcHVTqWJzvu5pWw9DyhbP35HH8MVBQ+dZjAfDxk+A8NiR9KWaXiyQ==} + engines: {node: '>=18.0.0'} + + fast-deep-equal@3.1.3: + resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + + fast-glob@3.3.1: + resolution: {integrity: sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==} + engines: {node: '>=8.6.0'} + + fast-json-stable-stringify@2.1.0: + resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} + + fast-levenshtein@2.0.6: + resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} + + fastq@1.20.1: + resolution: {integrity: sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==} + + fdir@6.5.0: + resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} + engines: {node: '>=12.0.0'} + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true + + file-entry-cache@8.0.0: + resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==} + engines: {node: '>=16.0.0'} + + fill-range@7.1.1: + resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} + engines: {node: '>=8'} + + find-up@5.0.0: + resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} + engines: {node: '>=10'} + + flat-cache@4.0.1: + resolution: {integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==} + engines: {node: '>=16'} + + flatted@3.4.4: + resolution: {integrity: sha512-5+ybhBZANEJxaH3X5evAFatUxLfEHSr7n6kYJ+1Qd0mUqr4eu9gIf6GDbWHf8RJijHrjjO8G+la14SlL2SeS1Q==} + + for-each@0.3.5: + resolution: {integrity: sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==} + engines: {node: '>= 0.4'} + + function-bind@1.1.2: + resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + + function.prototype.name@1.2.0: + resolution: {integrity: sha512-jObKIik1P2QjPHP5nz5BaOtUlfgS0fWo8IUByNXkM+o+02sJOi94em77GwJKQSJ3gfPHdgzLNrHc1uokV4P/ew==} + engines: {node: '>= 0.4'} + + functions-have-names@1.2.3: + resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==} + + generator-function@2.0.1: + resolution: {integrity: sha512-SFdFmIJi+ybC0vjlHN0ZGVGHc3lgE0DxPAT0djjVg+kjOnSqclqmj0KQ7ykTOLP6YxoqOvuAODGdcHJn+43q3g==} + engines: {node: '>= 0.4'} + + gensync@1.0.0-beta.2: + resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} + engines: {node: '>=6.9.0'} + + get-intrinsic@1.3.0: + resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} + engines: {node: '>= 0.4'} + + get-proto@1.0.1: + resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} + engines: {node: '>= 0.4'} + + get-symbol-description@1.1.0: + resolution: {integrity: sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==} + engines: {node: '>= 0.4'} + + get-tsconfig@4.14.3: + resolution: {integrity: sha512-++QEw4DIY7WGoukz+/+A/8dGYPT9l9yIadnmSgZ8Rjr3YVSVDipQSO9CdnJo9ePqFqUUqh+wk9uIaoiAwsiPkA==} + + glob-parent@5.1.2: + resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} + engines: {node: '>= 6'} + + glob-parent@6.0.2: + resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} + engines: {node: '>=10.13.0'} + + globals@14.0.0: + resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==} + engines: {node: '>=18'} + + globals@16.4.0: + resolution: {integrity: sha512-ob/2LcVVaVGCYN+r14cnwnoDPUufjiYgSqRhiFD0Q1iI4Odora5RE8Iv1D24hAz5oMophRGkGz+yuvQmmUMnMw==} + engines: {node: '>=18'} + + globalthis@1.0.4: + resolution: {integrity: sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==} + engines: {node: '>= 0.4'} + + gopd@1.2.0: + resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} + engines: {node: '>= 0.4'} + + graceful-fs@4.2.11: + resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + + has-bigints@1.1.0: + resolution: {integrity: sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==} + engines: {node: '>= 0.4'} + + has-flag@4.0.0: + resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} + engines: {node: '>=8'} + + has-property-descriptors@1.0.2: + resolution: {integrity: sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==} + + has-proto@1.2.0: + resolution: {integrity: sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==} + engines: {node: '>= 0.4'} + + has-symbols@1.1.0: + resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==} + engines: {node: '>= 0.4'} + + has-tostringtag@1.0.2: + resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} + engines: {node: '>= 0.4'} + + hasown@2.0.4: + resolution: {integrity: sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==} + engines: {node: '>= 0.4'} + + hermes-estree@0.25.1: + resolution: {integrity: sha512-0wUoCcLp+5Ev5pDW2OriHC2MJCbwLwuRx+gAqMTOkGKJJiBCLjtrvy4PWUGn6MIVefecRpzoOZ/UV6iGdOr+Cw==} + + hermes-parser@0.25.1: + resolution: {integrity: sha512-6pEjquH3rqaI6cYAXYPcz9MS4rY6R4ngRgrgfDshRptUZIc3lw0MCIJIGDj9++mfySOuPTHB4nrSW99BCvOPIA==} + + ignore@5.3.2: + resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} + engines: {node: '>= 4'} + + ignore@7.0.6: + resolution: {integrity: sha512-BAg6QkE8W+TuQLrrw0Ugr7HegXduRuuj8/ti2kSOc+jz1dmx8/WNcjr6XGnq5YpDWxFwwaavqD0+jIUOKelTsw==} + engines: {node: '>= 4'} + + import-fresh@3.3.1: + resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} + engines: {node: '>=6'} + + imurmurhash@0.1.4: + resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} + engines: {node: '>=0.8.19'} + + internal-slot@1.1.0: + resolution: {integrity: sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==} + engines: {node: '>= 0.4'} + + is-array-buffer@3.0.5: + resolution: {integrity: sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==} + engines: {node: '>= 0.4'} + + is-async-function@2.1.1: + resolution: {integrity: sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==} + engines: {node: '>= 0.4'} + + is-bigint@1.1.0: + resolution: {integrity: sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==} + engines: {node: '>= 0.4'} + + is-boolean-object@1.2.2: + resolution: {integrity: sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==} + engines: {node: '>= 0.4'} + + is-bun-module@2.0.0: + resolution: {integrity: sha512-gNCGbnnnnFAUGKeZ9PdbyeGYJqewpmc2aKHUEMO5nQPWU9lOmv7jcmQIv+qHD8fXW6W7qfuCwX4rY9LNRjXrkQ==} + + is-callable@1.2.7: + resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==} + engines: {node: '>= 0.4'} + + is-core-module@2.16.2: + resolution: {integrity: sha512-evOr8xfXKxE6qSR0hSXL2r3sd7ALj8+7jQEUvPYcm5sgZFdJ+AYzT6yNmJenvIYQBgIGwfwz08sL8zoL7yq2BA==} + engines: {node: '>= 0.4'} + + is-data-view@1.0.2: + resolution: {integrity: sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==} + engines: {node: '>= 0.4'} + + is-date-object@1.1.0: + resolution: {integrity: sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==} + engines: {node: '>= 0.4'} + + is-document.all@1.0.0: + resolution: {integrity: sha512-+XSoyS05OdBbhFuELhgTCpFNHkpBOJqtsZfUFFpe5QTw+9Sjbh8zitxhQkYAo6wV7e1Vb8cAPvpCk9jGam/82g==} + engines: {node: '>= 0.4'} + + is-extglob@2.1.1: + resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} + engines: {node: '>=0.10.0'} + + is-finalizationregistry@1.1.1: + resolution: {integrity: sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==} + engines: {node: '>= 0.4'} + + is-generator-function@1.1.2: + resolution: {integrity: sha512-upqt1SkGkODW9tsGNG5mtXTXtECizwtS2kA161M+gJPc1xdb/Ax629af6YrTwcOeQHbewrPNlE5Dx7kzvXTizA==} + engines: {node: '>= 0.4'} + + is-glob@4.0.3: + resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} + engines: {node: '>=0.10.0'} + + is-map@2.0.3: + resolution: {integrity: sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==} + engines: {node: '>= 0.4'} + + is-negative-zero@2.0.3: + resolution: {integrity: sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==} + engines: {node: '>= 0.4'} + + is-number-object@1.1.1: + resolution: {integrity: sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==} + engines: {node: '>= 0.4'} + + is-number@7.0.0: + resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} + engines: {node: '>=0.12.0'} + + is-regex@1.2.1: + resolution: {integrity: sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==} + engines: {node: '>= 0.4'} + + is-set@2.0.3: + resolution: {integrity: sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==} + engines: {node: '>= 0.4'} + + is-shared-array-buffer@1.0.4: + resolution: {integrity: sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==} + engines: {node: '>= 0.4'} + + is-string@1.1.1: + resolution: {integrity: sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==} + engines: {node: '>= 0.4'} + + is-symbol@1.1.1: + resolution: {integrity: sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==} + engines: {node: '>= 0.4'} + + is-typed-array@1.1.15: + resolution: {integrity: sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==} + engines: {node: '>= 0.4'} + + is-weakmap@2.0.2: + resolution: {integrity: sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==} + engines: {node: '>= 0.4'} + + is-weakref@1.1.1: + resolution: {integrity: sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==} + engines: {node: '>= 0.4'} + + is-weakset@2.0.4: + resolution: {integrity: sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==} + engines: {node: '>= 0.4'} + + isarray@2.0.5: + resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==} + + isexe@2.0.0: + resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + + iterator.prototype@1.1.5: + resolution: {integrity: sha512-H0dkQoCa3b2VEeKQBOxFph+JAbcrQdE7KC0UkqwpLmv2EC4P41QXP+rqo9wYodACiG5/WM5s9oDApTU8utwj9g==} + engines: {node: '>= 0.4'} + + jiti@2.7.0: + resolution: {integrity: sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==} + hasBin: true + + js-tokens@4.0.0: + resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + + js-yaml@4.3.1: + resolution: {integrity: sha512-CY6crGq313MX8GkwvB7tzgp99vjQxY1++5y10/BKN/GUfHqWaOGQMNZkBvqSzsZKWk/ijwHlWzzkLulsGHhjWQ==} + hasBin: true + + jsesc@3.1.0: + resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==} + engines: {node: '>=6'} + hasBin: true + + json-buffer@3.0.1: + resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} + + json-schema-traverse@0.4.1: + resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} + + json-schema@0.4.0: + resolution: {integrity: sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==} + + json-stable-stringify-without-jsonify@1.0.1: + resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} + + json5@1.0.2: + resolution: {integrity: sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==} + hasBin: true + + json5@2.2.3: + resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} + engines: {node: '>=6'} + hasBin: true + + jsx-ast-utils@3.3.5: + resolution: {integrity: sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==} + engines: {node: '>=4.0'} + + keyv@4.5.4: + resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} + + language-subtag-registry@0.3.23: + resolution: {integrity: sha512-0K65Lea881pHotoGEa5gDlMxt3pctLi2RplBb7Ezh4rRdLEOtgi7n4EwK9lamnUCkKBqaeKRVebTq6BAxSkpXQ==} + + language-tags@1.0.9: + resolution: {integrity: sha512-MbjN408fEndfiQXbFQ1vnd+1NoLDsnQW41410oQBXiyXDMYH5z505juWa4KUE1LqxRC7DgOgZDbKLxHIwm27hA==} + engines: {node: '>=0.10'} + + levn@0.4.1: + resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} + engines: {node: '>= 0.8.0'} + + lightningcss-android-arm64@1.32.0: + resolution: {integrity: sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [android] + + lightningcss-darwin-arm64@1.32.0: + resolution: {integrity: sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [darwin] + + lightningcss-darwin-x64@1.32.0: + resolution: {integrity: sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [darwin] + + lightningcss-freebsd-x64@1.32.0: + resolution: {integrity: sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [freebsd] + + lightningcss-linux-arm-gnueabihf@1.32.0: + resolution: {integrity: sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==} + engines: {node: '>= 12.0.0'} + cpu: [arm] + os: [linux] + + lightningcss-linux-arm64-gnu@1.32.0: + resolution: {integrity: sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + + lightningcss-linux-arm64-musl@1.32.0: + resolution: {integrity: sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + + lightningcss-linux-x64-gnu@1.32.0: + resolution: {integrity: sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + + lightningcss-linux-x64-musl@1.32.0: + resolution: {integrity: sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + + lightningcss-win32-arm64-msvc@1.32.0: + resolution: {integrity: sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [win32] + + lightningcss-win32-x64-msvc@1.32.0: + resolution: {integrity: sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [win32] + + lightningcss@1.32.0: + resolution: {integrity: sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==} + engines: {node: '>= 12.0.0'} + + locate-path@6.0.0: + resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} + engines: {node: '>=10'} + + lodash.merge@4.6.2: + resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} + + loose-envify@1.4.0: + resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} + hasBin: true + + lru-cache@5.1.1: + resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} + + magic-string@0.30.21: + resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} + + math-intrinsics@1.1.0: + resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} + engines: {node: '>= 0.4'} + + merge2@1.4.1: + resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} + engines: {node: '>= 8'} + + micromatch@4.0.8: + resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} + engines: {node: '>=8.6'} + + minimatch@10.2.6: + resolution: {integrity: sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A==} + engines: {node: 18 || 20 || >=22} + + minimatch@3.1.5: + resolution: {integrity: sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==} + + minimist@1.2.8: + resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} + + ms@2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + + nanoid@3.3.18: + resolution: {integrity: sha512-DTg4MJbGMWkfi6VZFdNt2/caMbQy4Ou+Op/hJQvGEWcnVfoA1QA+xzRKAzw9jD6+GVOOeYr/mIcuDSdug6F6+w==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + + napi-postinstall@0.3.4: + resolution: {integrity: sha512-PHI5f1O0EP5xJ9gQmFGMS6IZcrVvTjpXjz7Na41gTE7eE2hK11lg04CECCYEEjdc17EV4DO+fkGEtt7TpTaTiQ==} + engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0} + hasBin: true + + natural-compare@1.4.0: + resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} + + next@16.3.2: + resolution: {integrity: sha512-/ZCaubUy17Lld1SiPWxuPbCk2ihqAxF2QNQaPZeEaEb7t1I58qhsJN187D7AfpapHAqUPXH0f/thtdW9dWgWFg==} + engines: {node: '>=20.9.0'} + hasBin: true + peerDependencies: + '@opentelemetry/api': ^1.1.0 + '@playwright/test': ^1.51.1 + babel-plugin-react-compiler: '*' + react: ^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0 + react-dom: ^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0 + sass: ^1.3.0 + peerDependenciesMeta: + '@opentelemetry/api': + optional: true + '@playwright/test': + optional: true + babel-plugin-react-compiler: + optional: true + sass: + optional: true + + node-exports-info@1.6.2: + resolution: {integrity: sha512-kXs9Go0cah0qHVV2v389IXQLdLCeE1xfFtjOAF+iobu0OIoG1pje8At2vMHyaPMiPMnG/LWP50twML21eMcAag==} + engines: {node: '>= 0.4'} + + node-releases@2.0.53: + resolution: {integrity: sha512-D9UOmYG3UH1V+ENW56t5QXBwJw1YEY18ruVeus89Rw+SyIgjPkCO84bRzO3uNIYosJbNwiabWVn48o3uJLjxFQ==} + engines: {node: '>=18'} + + object-assign@4.1.1: + resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} + engines: {node: '>=0.10.0'} + + object-inspect@1.13.4: + resolution: {integrity: sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==} + engines: {node: '>= 0.4'} + + object-keys@1.1.1: + resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==} + engines: {node: '>= 0.4'} + + object.assign@4.1.7: + resolution: {integrity: sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==} + engines: {node: '>= 0.4'} + + object.entries@1.1.9: + resolution: {integrity: sha512-8u/hfXFRBD1O0hPUjioLhoWFHRmt6tKA4/vZPyckBr18l1KE9uHrFaFaUi8MDRTpi4uak2goyPTSNJLXX2k2Hw==} + engines: {node: '>= 0.4'} + + object.fromentries@2.0.8: + resolution: {integrity: sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==} + engines: {node: '>= 0.4'} + + object.groupby@1.0.3: + resolution: {integrity: sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ==} + engines: {node: '>= 0.4'} + + object.values@1.2.1: + resolution: {integrity: sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==} + engines: {node: '>= 0.4'} + + optionator@0.9.4: + resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} + engines: {node: '>= 0.8.0'} + + own-keys@1.0.2: + resolution: {integrity: sha512-19YVAg7T+WTrxggPukVq7DjTv6+PJ867TmhCvBsYwmbFCsZd344rq2Ld1p0wo8f8Qrrhgp82c6FJRqdXWtSEhg==} + engines: {node: '>= 0.4'} + + p-limit@3.1.0: + resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} + engines: {node: '>=10'} + + p-locate@5.0.0: + resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} + engines: {node: '>=10'} + + parent-module@1.0.1: + resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} + engines: {node: '>=6'} + + path-exists@4.0.0: + resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} + engines: {node: '>=8'} + + path-key@3.1.1: + resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} + engines: {node: '>=8'} + + path-parse@1.0.7: + resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} + + picocolors@1.1.1: + resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} + + picomatch@2.3.2: + resolution: {integrity: sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==} + engines: {node: '>=8.6'} + + picomatch@4.0.7: + resolution: {integrity: sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==} + engines: {node: '>=12'} + + pkce-challenge@5.0.1: + resolution: {integrity: sha512-wQ0b/W4Fr01qtpHlqSqspcj3EhBvimsdh0KlHhH8HRZnMsEa0ea2fTULOXOS9ccQr3om+GcGRk4e+isrZWV8qQ==} + engines: {node: '>=16.20.0'} + + possible-typed-array-names@1.1.0: + resolution: {integrity: sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==} + engines: {node: '>= 0.4'} + + postcss@8.5.23: + resolution: {integrity: sha512-g50586zr4bZmwFiTlflMu8E0bDTb5I5gertgwAKmsdUlTQIhZtunzUlD1WSzwcVWPoAVpsrA6vlfCD7oXvRwgg==} + engines: {node: ^10 || ^12 || >=14} + + postcss@8.5.26: + resolution: {integrity: sha512-u82N74LFzG8ca+dD8puPnplTXoGH4fTPpVGuIbt36G3qvNlkvfD0lEAZSxaly3KX8TS/L1A1gsCEmvKmBcVbkQ==} + engines: {node: ^10 || ^12 || >=14} + + prelude-ls@1.2.1: + resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} + engines: {node: '>= 0.8.0'} + + prop-types@15.8.1: + resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==} + + punycode@2.3.1: + resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} + engines: {node: '>=6'} + + queue-microtask@1.2.3: + resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} + + react-dom@19.2.8: + resolution: {integrity: sha512-rVprimfGBG3DR+Tq0IQG2DT5PxKth1WIGDmj5yPmlzr4YBe7uyE+Du4oVqTDXZSHGGGXRtTJEGSSePyQCMBglQ==} + peerDependencies: + react: ^19.2.8 + + react-is@16.13.1: + resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} + + react@19.2.8: + resolution: {integrity: sha512-PWaYA1L/q9u2u7xYQi+Y3L3Yfnie7XyLeaJICV1MGD6LprsBxcAqGjYyr0eY3p+QdsA+x/Irkt4Qif8D63+Sbw==} + engines: {node: '>=0.10.0'} + + reflect.getprototypeof@1.0.10: + resolution: {integrity: sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==} + engines: {node: '>= 0.4'} + + regexp.prototype.flags@1.5.4: + resolution: {integrity: sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==} + engines: {node: '>= 0.4'} + + resolve-from@4.0.0: + resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} + engines: {node: '>=4'} + + resolve-pkg-maps@1.0.0: + resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} + + resolve@2.0.0-next.7: + resolution: {integrity: sha512-tqt+NBWwyaMgw3zDsnygx4CByWjQEJHOPMdslYhppaQSJUtL/D4JO9CcBBlhPoI8lz9oJIDXkwXfhF4aWqP8xQ==} + engines: {node: '>= 0.4'} + hasBin: true + + reusify@1.1.0: + resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} + engines: {iojs: '>=1.0.0', node: '>=0.10.0'} + + run-parallel@1.2.0: + resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} + + safe-array-concat@1.1.4: + resolution: {integrity: sha512-wtZlHyOje6OZTGqAoaDKxFkgRtkF9CnHAVnCHKfuj200wAgL+bSJhdsCD2l0Qx/2ekEXjPWcyKkfGb5CPboslg==} + engines: {node: '>=0.4'} + + safe-push-apply@1.0.0: + resolution: {integrity: sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==} + engines: {node: '>= 0.4'} + + safe-regex-test@1.1.0: + resolution: {integrity: sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==} + engines: {node: '>= 0.4'} + + scheduler@0.27.0: + resolution: {integrity: sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==} + + semver@6.3.1: + resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} + hasBin: true + + semver@7.8.5: + resolution: {integrity: sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==} + engines: {node: '>=10'} + hasBin: true + + set-function-length@1.2.2: + resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==} + engines: {node: '>= 0.4'} + + set-function-name@2.0.2: + resolution: {integrity: sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==} + engines: {node: '>= 0.4'} + + set-proto@1.0.0: + resolution: {integrity: sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==} + engines: {node: '>= 0.4'} + + sharp@0.35.3: + resolution: {integrity: sha512-ej0zVHuZGHCiABXcNxeYhpRnPNPAcvbG8RMdBAhDAxLKkCRVSpK3Iyu7qbqw3JMzoj0REeM6f3tJLtVwl0023Q==} + engines: {node: '>=20.9.0'} + peerDependencies: + '@types/node': '*' + peerDependenciesMeta: + '@types/node': + optional: true + + shebang-command@2.0.0: + resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} + engines: {node: '>=8'} + + shebang-regex@3.0.0: + resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} + engines: {node: '>=8'} + + side-channel-list@1.0.1: + resolution: {integrity: sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==} + engines: {node: '>= 0.4'} + + side-channel-map@1.0.1: + resolution: {integrity: sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==} + engines: {node: '>= 0.4'} + + side-channel-weakmap@1.0.2: + resolution: {integrity: sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==} + engines: {node: '>= 0.4'} + + side-channel@1.1.1: + resolution: {integrity: sha512-6x6dK6zJdpTzF4sQeNYxwtvBzf6Eg4GtlesS94HOvTudUeyK2WXAaIfmDgsyslYrRBeFIlsi54AYsFGUuhmvrQ==} + engines: {node: '>= 0.4'} + + source-map-js@1.2.1: + resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} + engines: {node: '>=0.10.0'} + + stable-hash@0.0.5: + resolution: {integrity: sha512-+L3ccpzibovGXFK+Ap/f8LOS0ahMrHTf3xu7mMLSpEGU0EO9ucaysSylKo9eRDFNhWve/y275iPmIZ4z39a9iA==} + + stop-iteration-iterator@1.1.0: + resolution: {integrity: sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==} + engines: {node: '>= 0.4'} + + string.prototype.includes@2.0.1: + resolution: {integrity: sha512-o7+c9bW6zpAdJHTtujeePODAhkuicdAryFsfVKwA+wGw89wJ4GTY484WTucM9hLtDEOpOvI+aHnzqnC5lHp4Rg==} + engines: {node: '>= 0.4'} + + string.prototype.matchall@4.0.12: + resolution: {integrity: sha512-6CC9uyBL+/48dYizRf7H7VAYCMCNTBeM78x/VTUe9bFEaxBepPJDa1Ow99LqI/1yF7kuy7Q3cQsYMrcjGUcskA==} + engines: {node: '>= 0.4'} + + string.prototype.repeat@1.0.0: + resolution: {integrity: sha512-0u/TldDbKD8bFCQ/4f5+mNRrXwZ8hg2w7ZR8wa16e8z9XpePWl3eGEcUD0OXpEH/VJH/2G3gjUtR3ZOiBe2S/w==} + + string.prototype.trim@1.2.11: + resolution: {integrity: sha512-PwvK7BU+CMTJGYQCTZb5RWXIML92lftJLhQz1tBzgKiqGxJaMlBAa48POXaNAC2s4y8jr3EFqrkF9+44neS46w==} + engines: {node: '>= 0.4'} + + string.prototype.trimend@1.0.10: + resolution: {integrity: sha512-2+3aDAOmPTmuFwjDnmJG2ctEkQKVki7vOSqaxkv42Mowj1V6PnvuwFCRrR5lChUux1TBskPjfkeTOhqczDMxTw==} + engines: {node: '>= 0.4'} + + string.prototype.trimstart@1.0.8: + resolution: {integrity: sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==} + engines: {node: '>= 0.4'} + + strip-bom@3.0.0: + resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==} + engines: {node: '>=4'} + + strip-json-comments@3.1.1: + resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} + engines: {node: '>=8'} + + styled-jsx@5.1.6: + resolution: {integrity: sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA==} + engines: {node: '>= 12.0.0'} + peerDependencies: + '@babel/core': '*' + babel-plugin-macros: '*' + react: '>= 16.8.0 || 17.x.x || ^18.0.0-0 || ^19.0.0-0' + peerDependenciesMeta: + '@babel/core': + optional: true + babel-plugin-macros: + optional: true + + supports-color@7.2.0: + resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} + engines: {node: '>=8'} + + supports-preserve-symlinks-flag@1.0.0: + resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} + engines: {node: '>= 0.4'} + + swr@2.5.1: + resolution: {integrity: sha512-BRw55e8r0B7SpDN20CAzoQAHl7y1yP7/Zt7oqUjMv0vSt2u2Xnkm88Ws+VypbV9BXHQVuSuyVq7zMjO16wSExw==} + peerDependencies: + react: ^16.11.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + + tailwindcss@4.3.3: + resolution: {integrity: sha512-gOhV3P7ufE62QDGg1zVaTgCR+EtPv92k2nIhVcVKcLmxT1sUBsQGhnZj175j+MqRt4zLF7ic+sCYjfhxMxj7YQ==} + + tapable@2.3.3: + resolution: {integrity: sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A==} + engines: {node: '>=6'} + + throttleit@2.1.0: + resolution: {integrity: sha512-nt6AMGKW1p/70DF/hGBdJB57B8Tspmbp5gfJ8ilhLnt7kkr2ye7hzD6NVG8GGErk2HWF34igrL2CXmNIkzKqKw==} + engines: {node: '>=18'} + + tinyglobby@0.2.17: + resolution: {integrity: sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==} + engines: {node: '>=12.0.0'} + + to-regex-range@5.0.1: + resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} + engines: {node: '>=8.0'} + + ts-api-utils@2.5.0: + resolution: {integrity: sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==} + engines: {node: '>=18.12'} + peerDependencies: + typescript: '>=4.8.4' + + tsconfig-paths@3.15.0: + resolution: {integrity: sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==} + + tslib@2.8.1: + resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} + + type-check@0.4.0: + resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} + engines: {node: '>= 0.8.0'} + + typed-array-buffer@1.0.3: + resolution: {integrity: sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==} + engines: {node: '>= 0.4'} + + typed-array-byte-length@1.0.3: + resolution: {integrity: sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==} + engines: {node: '>= 0.4'} + + typed-array-byte-offset@1.0.4: + resolution: {integrity: sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==} + engines: {node: '>= 0.4'} + + typed-array-length@1.0.8: + resolution: {integrity: sha512-phPGCwqr2+Qo0fwniCE8e4pKnGu/yFb5nD5Y8bf0EEeiI5GklnACYA9GFy/DrAeRrKHXvHn+1SUsOWgJp6RO+g==} + engines: {node: '>= 0.4'} + + typescript-eslint@8.68.0: + resolution: {integrity: sha512-MHy0Y0ynqeEbx/S45+i/bBssdy3X6KNBfmJAP35GrgtNxu2TQ5K5xsFDhAnmsq1jvpdoZOPG1LGtJo0HWqYCrQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: '>=4.8.4 <6.1.0' + + typescript@5.9.3: + resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==} + engines: {node: '>=14.17'} + hasBin: true + + unbox-primitive@1.1.0: + resolution: {integrity: sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==} + engines: {node: '>= 0.4'} + + undici-types@6.21.0: + resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==} + + undici@7.29.0: + resolution: {integrity: sha512-IDxfleLmmbSskfWSUATiN1nfn2rDuvnMOqb5CWR92iIfojA0Ud+ulOAAEQ57LPr9rWmsreUyf5lwyao+7GNNVw==} + engines: {node: '>=20.18.1'} + + unrs-resolver@1.12.2: + resolution: {integrity: sha512-dmlRxBJJayXjqTwC+JtF1HhJmgf3ftQ3YejFcZrf4+KKtJv0qDsK1pjqaaVjG7wJ5NJ6UVP1OqRMQ71Z4C3rxQ==} + + update-browserslist-db@1.3.1: + resolution: {integrity: sha512-ZZ61DsRsOnakl74HAmp3oSN4aXUmEWXf+i/yv0h7tIBfICc3VdrFErQKUUKPgu3AMsTUMbcongALEN4l6GSUrQ==} + hasBin: true + peerDependencies: + browserslist: '>= 4.21.0' + + uri-js@4.4.1: + resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + + use-sync-external-store@1.6.0: + resolution: {integrity: sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + + which-boxed-primitive@1.1.1: + resolution: {integrity: sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==} + engines: {node: '>= 0.4'} + + which-builtin-type@1.2.1: + resolution: {integrity: sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==} + engines: {node: '>= 0.4'} + + which-collection@1.0.2: + resolution: {integrity: sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==} + engines: {node: '>= 0.4'} + + which-typed-array@1.1.22: + resolution: {integrity: sha512-fvO4ExWMFsqyhG3AiPAObMuY1lxaqgYcxbc49CNdWDDECOJNgQyvsOWVwbZc+qf3rzRtxojBK+CMEv0Ld5CYpw==} + engines: {node: '>= 0.4'} + + which@2.0.2: + resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} + engines: {node: '>= 8'} + hasBin: true + + word-wrap@1.2.5: + resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} + engines: {node: '>=0.10.0'} + + yallist@3.1.1: + resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} + + yocto-queue@0.1.0: + resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} + engines: {node: '>=10'} + + zod-validation-error@4.0.2: + resolution: {integrity: sha512-Q6/nZLe6jxuU80qb/4uJ4t5v2VEZ44lzQjPDhYJNztRQ4wyWc6VF3D3Kb/fAuPetZQnhS3hnajCf9CsWesghLQ==} + engines: {node: '>=18.0.0'} + peerDependencies: + zod: ^3.25.0 || ^4.0.0 + + zod@4.4.3: + resolution: {integrity: sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==} + +snapshots: + + '@ai-sdk/anthropic@4.0.42(zod@4.4.3)': + dependencies: + '@ai-sdk/provider': 4.0.8 + '@ai-sdk/provider-utils': 5.0.30(zod@4.4.3) + zod: 4.4.3 + + '@ai-sdk/gateway@4.0.64(zod@4.4.3)': + dependencies: + '@ai-sdk/provider': 4.0.8 + '@ai-sdk/provider-utils': 5.0.30(zod@4.4.3) + '@vercel/oidc': 3.2.0 + zod: 4.4.3 + + '@ai-sdk/mcp@2.0.37(zod@4.4.3)': + dependencies: + '@ai-sdk/provider': 4.0.8 + '@ai-sdk/provider-utils': 5.0.30(zod@4.4.3) + cross-spawn: 7.0.6 + pkce-challenge: 5.0.1 + zod: 4.4.3 + + '@ai-sdk/provider-utils@5.0.30(zod@4.4.3)': + dependencies: + '@ai-sdk/provider': 4.0.8 + '@standard-schema/spec': 1.1.0 + '@workflow/serde': 4.1.0 + eventsource-parser: 3.1.1 + undici: 7.29.0 + zod: 4.4.3 + + '@ai-sdk/provider@4.0.8': + dependencies: + json-schema: 0.4.0 + + '@ai-sdk/react@4.0.82(react@19.2.8)(zod@4.4.3)': + dependencies: + '@ai-sdk/mcp': 2.0.37(zod@4.4.3) + '@ai-sdk/provider': 4.0.8 + '@ai-sdk/provider-utils': 5.0.30(zod@4.4.3) + ai: 7.0.79(zod@4.4.3) + react: 19.2.8 + swr: 2.5.1(react@19.2.8) + throttleit: 2.1.0 + transitivePeerDependencies: + - zod + + '@alloc/quick-lru@5.2.0': {} + + '@babel/code-frame@7.29.7': + dependencies: + '@babel/helper-validator-identifier': 7.29.7 + js-tokens: 4.0.0 + picocolors: 1.1.1 + + '@babel/compat-data@7.29.7': {} + + '@babel/core@7.29.7': + dependencies: + '@babel/code-frame': 7.29.7 + '@babel/generator': 7.29.8 + '@babel/helper-compilation-targets': 7.29.7 + '@babel/helper-module-transforms': 7.29.7(@babel/core@7.29.7) + '@babel/helpers': 7.29.7 + '@babel/parser': 7.29.8 + '@babel/template': 7.29.7 + '@babel/traverse': 7.29.8 + '@babel/types': 7.29.8 + '@jridgewell/remapping': 2.3.5 + convert-source-map: 2.0.0 + debug: 4.4.3 + gensync: 1.0.0-beta.2 + json5: 2.2.3 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + '@babel/generator@7.29.8': + dependencies: + '@babel/parser': 7.29.8 + '@babel/types': 7.29.8 + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + jsesc: 3.1.0 + + '@babel/helper-compilation-targets@7.29.7': + dependencies: + '@babel/compat-data': 7.29.7 + '@babel/helper-validator-option': 7.29.7 + browserslist: 4.28.8 + lru-cache: 5.1.1 + semver: 6.3.1 + + '@babel/helper-globals@7.29.7': {} + + '@babel/helper-module-imports@7.29.7': + dependencies: + '@babel/traverse': 7.29.8 + '@babel/types': 7.29.8 + transitivePeerDependencies: + - supports-color + + '@babel/helper-module-transforms@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-module-imports': 7.29.7 + '@babel/helper-validator-identifier': 7.29.7 + '@babel/traverse': 7.29.8 + transitivePeerDependencies: + - supports-color + + '@babel/helper-string-parser@7.29.7': {} + + '@babel/helper-validator-identifier@7.29.7': {} + + '@babel/helper-validator-option@7.29.7': {} + + '@babel/helpers@7.29.7': + dependencies: + '@babel/template': 7.29.7 + '@babel/types': 7.29.8 + + '@babel/parser@7.29.8': + dependencies: + '@babel/types': 7.29.8 + + '@babel/template@7.29.7': + dependencies: + '@babel/code-frame': 7.29.7 + '@babel/parser': 7.29.8 + '@babel/types': 7.29.8 + + '@babel/traverse@7.29.8': + dependencies: + '@babel/code-frame': 7.29.7 + '@babel/generator': 7.29.8 + '@babel/helper-globals': 7.29.7 + '@babel/parser': 7.29.8 + '@babel/template': 7.29.7 + '@babel/types': 7.29.8 + debug: 4.4.3 + transitivePeerDependencies: + - supports-color + + '@babel/types@7.29.8': + dependencies: + '@babel/helper-string-parser': 7.29.7 + '@babel/helper-validator-identifier': 7.29.7 + + '@emnapi/core@1.10.0': + dependencies: + '@emnapi/wasi-threads': 1.2.1 + tslib: 2.8.1 + optional: true + + '@emnapi/runtime@1.10.0': + dependencies: + tslib: 2.8.1 + optional: true + + '@emnapi/runtime@1.11.3': + dependencies: + tslib: 2.8.1 + optional: true + + '@emnapi/wasi-threads@1.2.1': + dependencies: + tslib: 2.8.1 + optional: true + + '@eslint-community/eslint-utils@4.10.1(eslint@9.39.5(jiti@2.7.0))': + dependencies: + eslint: 9.39.5(jiti@2.7.0) + eslint-visitor-keys: 3.4.3 + + '@eslint-community/eslint-utils@4.9.1(eslint@9.39.5(jiti@2.7.0))': + dependencies: + eslint: 9.39.5(jiti@2.7.0) + eslint-visitor-keys: 3.4.3 + + '@eslint-community/regexpp@4.12.2': {} + + '@eslint/config-array@0.21.2': + dependencies: + '@eslint/object-schema': 2.1.7 + debug: 4.4.3 + minimatch: 3.1.5 + transitivePeerDependencies: + - supports-color + + '@eslint/config-helpers@0.4.2': + dependencies: + '@eslint/core': 0.17.0 + + '@eslint/core@0.17.0': + dependencies: + '@types/json-schema': 7.0.15 + + '@eslint/eslintrc@3.3.6': + dependencies: + ajv: 6.15.0 + debug: 4.4.3 + espree: 10.4.0 + globals: 14.0.0 + ignore: 5.3.2 + import-fresh: 3.3.1 + js-yaml: 4.3.1 + minimatch: 3.1.5 + strip-json-comments: 3.1.1 + transitivePeerDependencies: + - supports-color + + '@eslint/js@9.39.5': {} + + '@eslint/object-schema@2.1.7': {} + + '@eslint/plugin-kit@0.4.1': + dependencies: + '@eslint/core': 0.17.0 + levn: 0.4.1 + + '@humanfs/core@0.19.2': + dependencies: + '@humanfs/types': 0.15.0 + + '@humanfs/node@0.16.8': + dependencies: + '@humanfs/core': 0.19.2 + '@humanfs/types': 0.15.0 + '@humanwhocodes/retry': 0.4.3 + + '@humanfs/types@0.15.0': {} + + '@humanwhocodes/module-importer@1.0.1': {} + + '@humanwhocodes/retry@0.4.3': {} + + '@img/colour@1.1.0': + optional: true + + '@img/sharp-darwin-arm64@0.35.3': + optionalDependencies: + '@img/sharp-libvips-darwin-arm64': 1.3.2 + optional: true + + '@img/sharp-darwin-x64@0.35.3': + optionalDependencies: + '@img/sharp-libvips-darwin-x64': 1.3.2 + optional: true + + '@img/sharp-freebsd-wasm32@0.35.3': + dependencies: + '@img/sharp-wasm32': 0.35.3 + optional: true + + '@img/sharp-libvips-darwin-arm64@1.3.2': + optional: true + + '@img/sharp-libvips-darwin-x64@1.3.2': + optional: true + + '@img/sharp-libvips-linux-arm64@1.3.2': + optional: true + + '@img/sharp-libvips-linux-arm@1.3.2': + optional: true + + '@img/sharp-libvips-linux-ppc64@1.3.2': + optional: true + + '@img/sharp-libvips-linux-riscv64@1.3.2': + optional: true + + '@img/sharp-libvips-linux-s390x@1.3.2': + optional: true + + '@img/sharp-libvips-linux-x64@1.3.2': + optional: true + + '@img/sharp-libvips-linuxmusl-arm64@1.3.2': + optional: true + + '@img/sharp-libvips-linuxmusl-x64@1.3.2': + optional: true + + '@img/sharp-linux-arm64@0.35.3': + optionalDependencies: + '@img/sharp-libvips-linux-arm64': 1.3.2 + optional: true + + '@img/sharp-linux-arm@0.35.3': + optionalDependencies: + '@img/sharp-libvips-linux-arm': 1.3.2 + optional: true + + '@img/sharp-linux-ppc64@0.35.3': + optionalDependencies: + '@img/sharp-libvips-linux-ppc64': 1.3.2 + optional: true + + '@img/sharp-linux-riscv64@0.35.3': + optionalDependencies: + '@img/sharp-libvips-linux-riscv64': 1.3.2 + optional: true + + '@img/sharp-linux-s390x@0.35.3': + optionalDependencies: + '@img/sharp-libvips-linux-s390x': 1.3.2 + optional: true + + '@img/sharp-linux-x64@0.35.3': + optionalDependencies: + '@img/sharp-libvips-linux-x64': 1.3.2 + optional: true + + '@img/sharp-linuxmusl-arm64@0.35.3': + optionalDependencies: + '@img/sharp-libvips-linuxmusl-arm64': 1.3.2 + optional: true + + '@img/sharp-linuxmusl-x64@0.35.3': + optionalDependencies: + '@img/sharp-libvips-linuxmusl-x64': 1.3.2 + optional: true + + '@img/sharp-wasm32@0.35.3': + dependencies: + '@emnapi/runtime': 1.11.3 + optional: true + + '@img/sharp-webcontainers-wasm32@0.35.3': + dependencies: + '@img/sharp-wasm32': 0.35.3 + optional: true + + '@img/sharp-win32-arm64@0.35.3': + optional: true + + '@img/sharp-win32-ia32@0.35.3': + optional: true + + '@img/sharp-win32-x64@0.35.3': + optional: true + + '@jridgewell/gen-mapping@0.3.13': + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 + '@jridgewell/trace-mapping': 0.3.31 + + '@jridgewell/remapping@2.3.5': + dependencies: + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + + '@jridgewell/resolve-uri@3.1.2': {} + + '@jridgewell/sourcemap-codec@1.5.5': {} + + '@jridgewell/trace-mapping@0.3.31': + dependencies: + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.5.5 + + '@napi-rs/wasm-runtime@1.2.3(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)': + dependencies: + '@emnapi/core': 1.10.0 + '@emnapi/runtime': 1.10.0 + '@tybys/wasm-util': 0.10.3 + optional: true + + '@next/env@16.3.2': {} + + '@next/eslint-plugin-next@16.3.2(eslint@9.39.5(jiti@2.7.0))': + dependencies: + '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.5(jiti@2.7.0)) + fast-glob: 3.3.1 + transitivePeerDependencies: + - eslint + + '@next/swc-darwin-arm64@16.3.2': + optional: true + + '@next/swc-darwin-x64@16.3.2': + optional: true + + '@next/swc-linux-arm64-gnu@16.3.2': + optional: true + + '@next/swc-linux-arm64-musl@16.3.2': + optional: true + + '@next/swc-linux-x64-gnu@16.3.2': + optional: true + + '@next/swc-linux-x64-musl@16.3.2': + optional: true + + '@next/swc-win32-arm64-msvc@16.3.2': + optional: true + + '@next/swc-win32-x64-msvc@16.3.2': + optional: true + + '@nodelib/fs.scandir@2.1.5': + dependencies: + '@nodelib/fs.stat': 2.0.5 + run-parallel: 1.2.0 + + '@nodelib/fs.stat@2.0.5': {} + + '@nodelib/fs.walk@1.2.8': + dependencies: + '@nodelib/fs.scandir': 2.1.5 + fastq: 1.20.1 + + '@nolyfill/is-core-module@1.0.39': {} + + '@rtsao/scc@1.1.0': {} + + '@standard-schema/spec@1.1.0': {} + + '@swc/helpers@0.5.23': + dependencies: + tslib: 2.8.1 + + '@tailwindcss/node@4.3.3': + dependencies: + '@jridgewell/remapping': 2.3.5 + enhanced-resolve: 5.24.5 + jiti: 2.7.0 + lightningcss: 1.32.0 + magic-string: 0.30.21 + source-map-js: 1.2.1 + tailwindcss: 4.3.3 + + '@tailwindcss/oxide-android-arm64@4.3.3': + optional: true + + '@tailwindcss/oxide-darwin-arm64@4.3.3': + optional: true + + '@tailwindcss/oxide-darwin-x64@4.3.3': + optional: true + + '@tailwindcss/oxide-freebsd-x64@4.3.3': + optional: true + + '@tailwindcss/oxide-linux-arm-gnueabihf@4.3.3': + optional: true + + '@tailwindcss/oxide-linux-arm64-gnu@4.3.3': + optional: true + + '@tailwindcss/oxide-linux-arm64-musl@4.3.3': + optional: true + + '@tailwindcss/oxide-linux-x64-gnu@4.3.3': + optional: true + + '@tailwindcss/oxide-linux-x64-musl@4.3.3': + optional: true + + '@tailwindcss/oxide-wasm32-wasi@4.3.3': + optional: true + + '@tailwindcss/oxide-win32-arm64-msvc@4.3.3': + optional: true + + '@tailwindcss/oxide-win32-x64-msvc@4.3.3': + optional: true + + '@tailwindcss/oxide@4.3.3': + optionalDependencies: + '@tailwindcss/oxide-android-arm64': 4.3.3 + '@tailwindcss/oxide-darwin-arm64': 4.3.3 + '@tailwindcss/oxide-darwin-x64': 4.3.3 + '@tailwindcss/oxide-freebsd-x64': 4.3.3 + '@tailwindcss/oxide-linux-arm-gnueabihf': 4.3.3 + '@tailwindcss/oxide-linux-arm64-gnu': 4.3.3 + '@tailwindcss/oxide-linux-arm64-musl': 4.3.3 + '@tailwindcss/oxide-linux-x64-gnu': 4.3.3 + '@tailwindcss/oxide-linux-x64-musl': 4.3.3 + '@tailwindcss/oxide-wasm32-wasi': 4.3.3 + '@tailwindcss/oxide-win32-arm64-msvc': 4.3.3 + '@tailwindcss/oxide-win32-x64-msvc': 4.3.3 + + '@tailwindcss/postcss@4.3.3': + dependencies: + '@alloc/quick-lru': 5.2.0 + '@tailwindcss/node': 4.3.3 + '@tailwindcss/oxide': 4.3.3 + postcss: 8.5.26 + tailwindcss: 4.3.3 + + '@tybys/wasm-util@0.10.3': + dependencies: + tslib: 2.8.1 + optional: true + + '@types/estree@1.0.9': {} + + '@types/json-schema@7.0.15': {} + + '@types/json5@0.0.29': {} + + '@types/node@20.19.43': + dependencies: + undici-types: 6.21.0 + + '@types/react-dom@19.2.5(@types/react@19.2.18)': + dependencies: + '@types/react': 19.2.18 + + '@types/react@19.2.18': + dependencies: + csstype: 3.2.3 + + '@typescript-eslint/eslint-plugin@8.68.0(@typescript-eslint/parser@8.68.0(eslint@9.39.5(jiti@2.7.0))(typescript@5.9.3))(eslint@9.39.5(jiti@2.7.0))(typescript@5.9.3)': + dependencies: + '@eslint-community/regexpp': 4.12.2 + '@typescript-eslint/parser': 8.68.0(eslint@9.39.5(jiti@2.7.0))(typescript@5.9.3) + '@typescript-eslint/scope-manager': 8.68.0 + '@typescript-eslint/type-utils': 8.68.0(eslint@9.39.5(jiti@2.7.0))(typescript@5.9.3) + '@typescript-eslint/utils': 8.68.0(eslint@9.39.5(jiti@2.7.0))(typescript@5.9.3) + '@typescript-eslint/visitor-keys': 8.68.0 + eslint: 9.39.5(jiti@2.7.0) + ignore: 7.0.6 + natural-compare: 1.4.0 + ts-api-utils: 2.5.0(typescript@5.9.3) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/parser@8.68.0(eslint@9.39.5(jiti@2.7.0))(typescript@5.9.3)': + dependencies: + '@typescript-eslint/scope-manager': 8.68.0 + '@typescript-eslint/types': 8.68.0 + '@typescript-eslint/typescript-estree': 8.68.0(typescript@5.9.3) + '@typescript-eslint/visitor-keys': 8.68.0 + debug: 4.4.3 + eslint: 9.39.5(jiti@2.7.0) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/project-service@8.68.0(typescript@5.9.3)': + dependencies: + '@typescript-eslint/tsconfig-utils': 8.68.0(typescript@5.9.3) + '@typescript-eslint/types': 8.68.0 + debug: 4.4.3 + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/scope-manager@8.68.0': + dependencies: + '@typescript-eslint/types': 8.68.0 + '@typescript-eslint/visitor-keys': 8.68.0 + + '@typescript-eslint/tsconfig-utils@8.68.0(typescript@5.9.3)': + dependencies: + typescript: 5.9.3 + + '@typescript-eslint/type-utils@8.68.0(eslint@9.39.5(jiti@2.7.0))(typescript@5.9.3)': + dependencies: + '@typescript-eslint/types': 8.68.0 + '@typescript-eslint/typescript-estree': 8.68.0(typescript@5.9.3) + '@typescript-eslint/utils': 8.68.0(eslint@9.39.5(jiti@2.7.0))(typescript@5.9.3) + debug: 4.4.3 + eslint: 9.39.5(jiti@2.7.0) + ts-api-utils: 2.5.0(typescript@5.9.3) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/types@8.68.0': {} + + '@typescript-eslint/typescript-estree@8.68.0(typescript@5.9.3)': + dependencies: + '@typescript-eslint/project-service': 8.68.0(typescript@5.9.3) + '@typescript-eslint/tsconfig-utils': 8.68.0(typescript@5.9.3) + '@typescript-eslint/types': 8.68.0 + '@typescript-eslint/visitor-keys': 8.68.0 + debug: 4.4.3 + minimatch: 10.2.6 + semver: 7.8.5 + tinyglobby: 0.2.17 + ts-api-utils: 2.5.0(typescript@5.9.3) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/utils@8.68.0(eslint@9.39.5(jiti@2.7.0))(typescript@5.9.3)': + dependencies: + '@eslint-community/eslint-utils': 4.10.1(eslint@9.39.5(jiti@2.7.0)) + '@typescript-eslint/scope-manager': 8.68.0 + '@typescript-eslint/types': 8.68.0 + '@typescript-eslint/typescript-estree': 8.68.0(typescript@5.9.3) + eslint: 9.39.5(jiti@2.7.0) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/visitor-keys@8.68.0': + dependencies: + '@typescript-eslint/types': 8.68.0 + eslint-visitor-keys: 5.0.1 + + '@unrs/resolver-binding-android-arm-eabi@1.12.2': + optional: true + + '@unrs/resolver-binding-android-arm64@1.12.2': + optional: true + + '@unrs/resolver-binding-darwin-arm64@1.12.2': + optional: true + + '@unrs/resolver-binding-darwin-x64@1.12.2': + optional: true + + '@unrs/resolver-binding-freebsd-x64@1.12.2': + optional: true + + '@unrs/resolver-binding-linux-arm-gnueabihf@1.12.2': + optional: true + + '@unrs/resolver-binding-linux-arm-musleabihf@1.12.2': + optional: true + + '@unrs/resolver-binding-linux-arm64-gnu@1.12.2': + optional: true + + '@unrs/resolver-binding-linux-arm64-musl@1.12.2': + optional: true + + '@unrs/resolver-binding-linux-loong64-gnu@1.12.2': + optional: true + + '@unrs/resolver-binding-linux-loong64-musl@1.12.2': + optional: true + + '@unrs/resolver-binding-linux-ppc64-gnu@1.12.2': + optional: true + + '@unrs/resolver-binding-linux-riscv64-gnu@1.12.2': + optional: true + + '@unrs/resolver-binding-linux-riscv64-musl@1.12.2': + optional: true + + '@unrs/resolver-binding-linux-s390x-gnu@1.12.2': + optional: true + + '@unrs/resolver-binding-linux-x64-gnu@1.12.2': + optional: true + + '@unrs/resolver-binding-linux-x64-musl@1.12.2': + optional: true + + '@unrs/resolver-binding-openharmony-arm64@1.12.2': + optional: true + + '@unrs/resolver-binding-wasm32-wasi@1.12.2': + dependencies: + '@emnapi/core': 1.10.0 + '@emnapi/runtime': 1.10.0 + '@napi-rs/wasm-runtime': 1.2.3(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0) + optional: true + + '@unrs/resolver-binding-win32-arm64-msvc@1.12.2': + optional: true + + '@unrs/resolver-binding-win32-ia32-msvc@1.12.2': + optional: true + + '@unrs/resolver-binding-win32-x64-msvc@1.12.2': + optional: true + + '@vercel/oidc@3.2.0': {} + + '@workflow/serde@4.1.0': {} + + acorn-jsx@5.3.2(acorn@8.18.0): + dependencies: + acorn: 8.18.0 + + acorn@8.18.0: {} + + ai@7.0.79(zod@4.4.3): + dependencies: + '@ai-sdk/gateway': 4.0.64(zod@4.4.3) + '@ai-sdk/provider': 4.0.8 + '@ai-sdk/provider-utils': 5.0.30(zod@4.4.3) + zod: 4.4.3 + + ajv@6.15.0: + dependencies: + fast-deep-equal: 3.1.3 + fast-json-stable-stringify: 2.1.0 + json-schema-traverse: 0.4.1 + uri-js: 4.4.1 + + ansi-styles@4.3.0: + dependencies: + color-convert: 2.0.1 + + argparse@2.0.1: {} + + aria-query@5.3.2: {} + + array-buffer-byte-length@1.0.2: + dependencies: + call-bound: 1.0.4 + is-array-buffer: 3.0.5 + + array-includes@3.1.9: + dependencies: + call-bind: 1.0.9 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-abstract: 1.24.2 + es-object-atoms: 1.1.2 + get-intrinsic: 1.3.0 + is-string: 1.1.1 + math-intrinsics: 1.1.0 + + array.prototype.findlast@1.2.5: + dependencies: + call-bind: 1.0.9 + define-properties: 1.2.1 + es-abstract: 1.24.2 + es-errors: 1.3.0 + es-object-atoms: 1.1.2 + es-shim-unscopables: 1.1.0 + + array.prototype.findlastindex@1.2.6: + dependencies: + call-bind: 1.0.9 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-abstract: 1.24.2 + es-errors: 1.3.0 + es-object-atoms: 1.1.2 + es-shim-unscopables: 1.1.0 + + array.prototype.flat@1.3.3: + dependencies: + call-bind: 1.0.9 + define-properties: 1.2.1 + es-abstract: 1.24.2 + es-shim-unscopables: 1.1.0 + + array.prototype.flatmap@1.3.3: + dependencies: + call-bind: 1.0.9 + define-properties: 1.2.1 + es-abstract: 1.24.2 + es-shim-unscopables: 1.1.0 + + array.prototype.tosorted@1.1.4: + dependencies: + call-bind: 1.0.9 + define-properties: 1.2.1 + es-abstract: 1.24.2 + es-errors: 1.3.0 + es-shim-unscopables: 1.1.0 + + arraybuffer.prototype.slice@1.0.4: + dependencies: + array-buffer-byte-length: 1.0.2 + call-bind: 1.0.9 + define-properties: 1.2.1 + es-abstract: 1.24.2 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + is-array-buffer: 3.0.5 + + ast-types-flow@0.0.8: {} + + async-function@1.0.0: {} + + available-typed-arrays@1.0.7: + dependencies: + possible-typed-array-names: 1.1.0 + + axe-core@4.13.0: {} + + axobject-query@4.1.0: {} + + balanced-match@1.0.2: {} + + balanced-match@4.0.4: {} + + baseline-browser-mapping@2.11.19: {} + + brace-expansion@1.1.18: + dependencies: + balanced-match: 1.0.2 + concat-map: 0.0.1 + + brace-expansion@5.0.9: + dependencies: + balanced-match: 4.0.4 + + braces@3.0.3: + dependencies: + fill-range: 7.1.1 + + browserslist@4.28.8: + dependencies: + baseline-browser-mapping: 2.11.19 + caniuse-lite: 1.0.30001810 + electron-to-chromium: 1.5.414 + node-releases: 2.0.53 + update-browserslist-db: 1.3.1(browserslist@4.28.8) + + call-bind-apply-helpers@1.0.2: + dependencies: + es-errors: 1.3.0 + function-bind: 1.1.2 + + call-bind@1.0.9: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-define-property: 1.0.1 + get-intrinsic: 1.3.0 + set-function-length: 1.2.2 + + call-bound@1.0.4: + dependencies: + call-bind-apply-helpers: 1.0.2 + get-intrinsic: 1.3.0 + + callsites@3.1.0: {} + + caniuse-lite@1.0.30001810: {} + + chalk@4.1.2: + dependencies: + ansi-styles: 4.3.0 + supports-color: 7.2.0 + + client-only@0.0.1: {} + + color-convert@2.0.1: + dependencies: + color-name: 1.1.4 + + color-name@1.1.4: {} + + concat-map@0.0.1: {} + + convert-source-map@2.0.0: {} + + cross-spawn@7.0.6: + dependencies: + path-key: 3.1.1 + shebang-command: 2.0.0 + which: 2.0.2 + + csstype@3.2.3: {} + + damerau-levenshtein@1.0.8: {} + + data-view-buffer@1.0.2: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-data-view: 1.0.2 + + data-view-byte-length@1.0.2: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-data-view: 1.0.2 + + data-view-byte-offset@1.0.1: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-data-view: 1.0.2 + + debug@3.2.7: + dependencies: + ms: 2.1.3 + + debug@4.4.3: + dependencies: + ms: 2.1.3 + + deep-is@0.1.4: {} + + define-data-property@1.1.4: + dependencies: + es-define-property: 1.0.1 + es-errors: 1.3.0 + gopd: 1.2.0 + + define-properties@1.2.1: + dependencies: + define-data-property: 1.1.4 + has-property-descriptors: 1.0.2 + object-keys: 1.1.1 + + dequal@2.0.3: {} + + detect-libc@2.1.2: {} + + doctrine@2.1.0: + dependencies: + esutils: 2.0.3 + + dunder-proto@1.0.1: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-errors: 1.3.0 + gopd: 1.2.0 + + electron-to-chromium@1.5.414: {} + + emoji-regex@9.2.2: {} + + enhanced-resolve@5.24.5: + dependencies: + graceful-fs: 4.2.11 + tapable: 2.3.3 + + es-abstract-get@1.0.0: + dependencies: + es-errors: 1.3.0 + es-object-atoms: 1.1.2 + is-callable: 1.2.7 + object-inspect: 1.13.4 + + es-abstract@1.24.2: + dependencies: + array-buffer-byte-length: 1.0.2 + arraybuffer.prototype.slice: 1.0.4 + available-typed-arrays: 1.0.7 + call-bind: 1.0.9 + call-bound: 1.0.4 + data-view-buffer: 1.0.2 + data-view-byte-length: 1.0.2 + data-view-byte-offset: 1.0.1 + es-define-property: 1.0.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.2 + es-set-tostringtag: 2.1.0 + es-to-primitive: 1.3.4 + function.prototype.name: 1.2.0 + get-intrinsic: 1.3.0 + get-proto: 1.0.1 + get-symbol-description: 1.1.0 + globalthis: 1.0.4 + gopd: 1.2.0 + has-property-descriptors: 1.0.2 + has-proto: 1.2.0 + has-symbols: 1.1.0 + hasown: 2.0.4 + internal-slot: 1.1.0 + is-array-buffer: 3.0.5 + is-callable: 1.2.7 + is-data-view: 1.0.2 + is-negative-zero: 2.0.3 + is-regex: 1.2.1 + is-set: 2.0.3 + is-shared-array-buffer: 1.0.4 + is-string: 1.1.1 + is-typed-array: 1.1.15 + is-weakref: 1.1.1 + math-intrinsics: 1.1.0 + object-inspect: 1.13.4 + object-keys: 1.1.1 + object.assign: 4.1.7 + own-keys: 1.0.2 + regexp.prototype.flags: 1.5.4 + safe-array-concat: 1.1.4 + safe-push-apply: 1.0.0 + safe-regex-test: 1.1.0 + set-proto: 1.0.0 + stop-iteration-iterator: 1.1.0 + string.prototype.trim: 1.2.11 + string.prototype.trimend: 1.0.10 + string.prototype.trimstart: 1.0.8 + typed-array-buffer: 1.0.3 + typed-array-byte-length: 1.0.3 + typed-array-byte-offset: 1.0.4 + typed-array-length: 1.0.8 + unbox-primitive: 1.1.0 + which-typed-array: 1.1.22 + + es-define-property@1.0.1: {} + + es-errors@1.3.0: {} + + es-iterator-helpers@1.4.0: + dependencies: + call-bind: 1.0.9 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-abstract: 1.24.2 + es-errors: 1.3.0 + es-set-tostringtag: 2.1.0 + function-bind: 1.1.2 + get-intrinsic: 1.3.0 + globalthis: 1.0.4 + gopd: 1.2.0 + has-property-descriptors: 1.0.2 + has-proto: 1.2.0 + has-symbols: 1.1.0 + internal-slot: 1.1.0 + iterator.prototype: 1.1.5 + math-intrinsics: 1.1.0 + + es-object-atoms@1.1.2: + dependencies: + es-errors: 1.3.0 + + es-set-tostringtag@2.1.0: + dependencies: + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + has-tostringtag: 1.0.2 + hasown: 2.0.4 + + es-shim-unscopables@1.1.0: + dependencies: + hasown: 2.0.4 + + es-to-primitive@1.3.4: + dependencies: + es-abstract-get: 1.0.0 + es-define-property: 1.0.1 + es-errors: 1.3.0 + is-callable: 1.2.7 + is-date-object: 1.1.0 + is-symbol: 1.1.1 + + escalade@3.2.0: {} + + escape-string-regexp@4.0.0: {} + + eslint-config-next@16.3.2(@typescript-eslint/parser@8.68.0(eslint@9.39.5(jiti@2.7.0))(typescript@5.9.3))(eslint@9.39.5(jiti@2.7.0))(typescript@5.9.3): + dependencies: + '@next/eslint-plugin-next': 16.3.2(eslint@9.39.5(jiti@2.7.0)) + eslint: 9.39.5(jiti@2.7.0) + eslint-import-resolver-node: 0.3.10 + eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.5(jiti@2.7.0)) + eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.68.0(eslint@9.39.5(jiti@2.7.0))(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.5(jiti@2.7.0)) + eslint-plugin-jsx-a11y: 6.10.2(eslint@9.39.5(jiti@2.7.0)) + eslint-plugin-react: 7.37.5(eslint@9.39.5(jiti@2.7.0)) + eslint-plugin-react-hooks: 7.1.1(eslint@9.39.5(jiti@2.7.0)) + globals: 16.4.0 + typescript-eslint: 8.68.0(eslint@9.39.5(jiti@2.7.0))(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - '@typescript-eslint/parser' + - eslint-import-resolver-webpack + - eslint-plugin-import-x + - supports-color + + eslint-import-resolver-node@0.3.10: + dependencies: + debug: 3.2.7 + is-core-module: 2.16.2 + resolve: 2.0.0-next.7 + transitivePeerDependencies: + - supports-color + + eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.5(jiti@2.7.0)): + dependencies: + '@nolyfill/is-core-module': 1.0.39 + debug: 4.4.3 + eslint: 9.39.5(jiti@2.7.0) + get-tsconfig: 4.14.3 + is-bun-module: 2.0.0 + stable-hash: 0.0.5 + tinyglobby: 0.2.17 + unrs-resolver: 1.12.2 + optionalDependencies: + eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.68.0(eslint@9.39.5(jiti@2.7.0))(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.5(jiti@2.7.0)) + transitivePeerDependencies: + - supports-color + + eslint-module-utils@2.14.0(@typescript-eslint/parser@8.68.0(eslint@9.39.5(jiti@2.7.0))(typescript@5.9.3))(eslint-import-resolver-node@0.3.10)(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.5(jiti@2.7.0)): + dependencies: + debug: 3.2.7 + optionalDependencies: + '@typescript-eslint/parser': 8.68.0(eslint@9.39.5(jiti@2.7.0))(typescript@5.9.3) + eslint: 9.39.5(jiti@2.7.0) + eslint-import-resolver-node: 0.3.10 + eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.5(jiti@2.7.0)) + transitivePeerDependencies: + - supports-color + + eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.68.0(eslint@9.39.5(jiti@2.7.0))(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.5(jiti@2.7.0)): + dependencies: + '@rtsao/scc': 1.1.0 + array-includes: 3.1.9 + array.prototype.findlastindex: 1.2.6 + array.prototype.flat: 1.3.3 + array.prototype.flatmap: 1.3.3 + debug: 3.2.7 + doctrine: 2.1.0 + eslint: 9.39.5(jiti@2.7.0) + eslint-import-resolver-node: 0.3.10 + eslint-module-utils: 2.14.0(@typescript-eslint/parser@8.68.0(eslint@9.39.5(jiti@2.7.0))(typescript@5.9.3))(eslint-import-resolver-node@0.3.10)(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.5(jiti@2.7.0)) + hasown: 2.0.4 + is-core-module: 2.16.2 + is-glob: 4.0.3 + minimatch: 3.1.5 + object.fromentries: 2.0.8 + object.groupby: 1.0.3 + object.values: 1.2.1 + semver: 6.3.1 + string.prototype.trimend: 1.0.10 + tsconfig-paths: 3.15.0 + optionalDependencies: + '@typescript-eslint/parser': 8.68.0(eslint@9.39.5(jiti@2.7.0))(typescript@5.9.3) + transitivePeerDependencies: + - eslint-import-resolver-typescript + - eslint-import-resolver-webpack + - supports-color + + eslint-plugin-jsx-a11y@6.10.2(eslint@9.39.5(jiti@2.7.0)): + dependencies: + aria-query: 5.3.2 + array-includes: 3.1.9 + array.prototype.flatmap: 1.3.3 + ast-types-flow: 0.0.8 + axe-core: 4.13.0 + axobject-query: 4.1.0 + damerau-levenshtein: 1.0.8 + emoji-regex: 9.2.2 + eslint: 9.39.5(jiti@2.7.0) + hasown: 2.0.4 + jsx-ast-utils: 3.3.5 + language-tags: 1.0.9 + minimatch: 3.1.5 + object.fromentries: 2.0.8 + safe-regex-test: 1.1.0 + string.prototype.includes: 2.0.1 + + eslint-plugin-react-hooks@7.1.1(eslint@9.39.5(jiti@2.7.0)): + dependencies: + '@babel/core': 7.29.7 + '@babel/parser': 7.29.8 + eslint: 9.39.5(jiti@2.7.0) + hermes-parser: 0.25.1 + zod: 4.4.3 + zod-validation-error: 4.0.2(zod@4.4.3) + transitivePeerDependencies: + - supports-color + + eslint-plugin-react@7.37.5(eslint@9.39.5(jiti@2.7.0)): + dependencies: + array-includes: 3.1.9 + array.prototype.findlast: 1.2.5 + array.prototype.flatmap: 1.3.3 + array.prototype.tosorted: 1.1.4 + doctrine: 2.1.0 + es-iterator-helpers: 1.4.0 + eslint: 9.39.5(jiti@2.7.0) + estraverse: 5.3.0 + hasown: 2.0.4 + jsx-ast-utils: 3.3.5 + minimatch: 3.1.5 + object.entries: 1.1.9 + object.fromentries: 2.0.8 + object.values: 1.2.1 + prop-types: 15.8.1 + resolve: 2.0.0-next.7 + semver: 6.3.1 + string.prototype.matchall: 4.0.12 + string.prototype.repeat: 1.0.0 + + eslint-scope@8.4.0: + dependencies: + esrecurse: 4.3.0 + estraverse: 5.3.0 + + eslint-visitor-keys@3.4.3: {} + + eslint-visitor-keys@4.2.1: {} + + eslint-visitor-keys@5.0.1: {} + + eslint@9.39.5(jiti@2.7.0): + dependencies: + '@eslint-community/eslint-utils': 4.10.1(eslint@9.39.5(jiti@2.7.0)) + '@eslint-community/regexpp': 4.12.2 + '@eslint/config-array': 0.21.2 + '@eslint/config-helpers': 0.4.2 + '@eslint/core': 0.17.0 + '@eslint/eslintrc': 3.3.6 + '@eslint/js': 9.39.5 + '@eslint/plugin-kit': 0.4.1 + '@humanfs/node': 0.16.8 + '@humanwhocodes/module-importer': 1.0.1 + '@humanwhocodes/retry': 0.4.3 + '@types/estree': 1.0.9 + ajv: 6.15.0 + chalk: 4.1.2 + cross-spawn: 7.0.6 + debug: 4.4.3 + escape-string-regexp: 4.0.0 + eslint-scope: 8.4.0 + eslint-visitor-keys: 4.2.1 + espree: 10.4.0 + esquery: 1.7.0 + esutils: 2.0.3 + fast-deep-equal: 3.1.3 + file-entry-cache: 8.0.0 + find-up: 5.0.0 + glob-parent: 6.0.2 + ignore: 5.3.2 + imurmurhash: 0.1.4 + is-glob: 4.0.3 + json-stable-stringify-without-jsonify: 1.0.1 + lodash.merge: 4.6.2 + minimatch: 3.1.5 + natural-compare: 1.4.0 + optionator: 0.9.4 + optionalDependencies: + jiti: 2.7.0 + transitivePeerDependencies: + - supports-color + + espree@10.4.0: + dependencies: + acorn: 8.18.0 + acorn-jsx: 5.3.2(acorn@8.18.0) + eslint-visitor-keys: 4.2.1 + + esquery@1.7.0: + dependencies: + estraverse: 5.3.0 + + esrecurse@4.3.0: + dependencies: + estraverse: 5.3.0 + + estraverse@5.3.0: {} + + esutils@2.0.3: {} + + eventsource-parser@3.1.1: {} + + fast-deep-equal@3.1.3: {} + + fast-glob@3.3.1: + dependencies: + '@nodelib/fs.stat': 2.0.5 + '@nodelib/fs.walk': 1.2.8 + glob-parent: 5.1.2 + merge2: 1.4.1 + micromatch: 4.0.8 + + fast-json-stable-stringify@2.1.0: {} + + fast-levenshtein@2.0.6: {} + + fastq@1.20.1: + dependencies: + reusify: 1.1.0 + + fdir@6.5.0(picomatch@4.0.7): + optionalDependencies: + picomatch: 4.0.7 + + file-entry-cache@8.0.0: + dependencies: + flat-cache: 4.0.1 + + fill-range@7.1.1: + dependencies: + to-regex-range: 5.0.1 + + find-up@5.0.0: + dependencies: + locate-path: 6.0.0 + path-exists: 4.0.0 + + flat-cache@4.0.1: + dependencies: + flatted: 3.4.4 + keyv: 4.5.4 + + flatted@3.4.4: {} + + for-each@0.3.5: + dependencies: + is-callable: 1.2.7 + + function-bind@1.1.2: {} + + function.prototype.name@1.2.0: + dependencies: + call-bind: 1.0.9 + call-bound: 1.0.4 + es-define-property: 1.0.1 + es-errors: 1.3.0 + functions-have-names: 1.2.3 + has-property-descriptors: 1.0.2 + hasown: 2.0.4 + is-callable: 1.2.7 + is-document.all: 1.0.0 + + functions-have-names@1.2.3: {} + + generator-function@2.0.1: {} + + gensync@1.0.0-beta.2: {} + + get-intrinsic@1.3.0: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-define-property: 1.0.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.2 + function-bind: 1.1.2 + get-proto: 1.0.1 + gopd: 1.2.0 + has-symbols: 1.1.0 + hasown: 2.0.4 + math-intrinsics: 1.1.0 + + get-proto@1.0.1: + dependencies: + dunder-proto: 1.0.1 + es-object-atoms: 1.1.2 + + get-symbol-description@1.1.0: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + + get-tsconfig@4.14.3: + dependencies: + resolve-pkg-maps: 1.0.0 + + glob-parent@5.1.2: + dependencies: + is-glob: 4.0.3 + + glob-parent@6.0.2: + dependencies: + is-glob: 4.0.3 + + globals@14.0.0: {} + + globals@16.4.0: {} + + globalthis@1.0.4: + dependencies: + define-properties: 1.2.1 + gopd: 1.2.0 + + gopd@1.2.0: {} + + graceful-fs@4.2.11: {} + + has-bigints@1.1.0: {} + + has-flag@4.0.0: {} + + has-property-descriptors@1.0.2: + dependencies: + es-define-property: 1.0.1 + + has-proto@1.2.0: + dependencies: + dunder-proto: 1.0.1 + + has-symbols@1.1.0: {} + + has-tostringtag@1.0.2: + dependencies: + has-symbols: 1.1.0 + + hasown@2.0.4: + dependencies: + function-bind: 1.1.2 + + hermes-estree@0.25.1: {} + + hermes-parser@0.25.1: + dependencies: + hermes-estree: 0.25.1 + + ignore@5.3.2: {} + + ignore@7.0.6: {} + + import-fresh@3.3.1: + dependencies: + parent-module: 1.0.1 + resolve-from: 4.0.0 + + imurmurhash@0.1.4: {} + + internal-slot@1.1.0: + dependencies: + es-errors: 1.3.0 + hasown: 2.0.4 + side-channel: 1.1.1 + + is-array-buffer@3.0.5: + dependencies: + call-bind: 1.0.9 + call-bound: 1.0.4 + get-intrinsic: 1.3.0 + + is-async-function@2.1.1: + dependencies: + async-function: 1.0.0 + call-bound: 1.0.4 + get-proto: 1.0.1 + has-tostringtag: 1.0.2 + safe-regex-test: 1.1.0 + + is-bigint@1.1.0: + dependencies: + has-bigints: 1.1.0 + + is-boolean-object@1.2.2: + dependencies: + call-bound: 1.0.4 + has-tostringtag: 1.0.2 + + is-bun-module@2.0.0: + dependencies: + semver: 7.8.5 + + is-callable@1.2.7: {} + + is-core-module@2.16.2: + dependencies: + hasown: 2.0.4 + + is-data-view@1.0.2: + dependencies: + call-bound: 1.0.4 + get-intrinsic: 1.3.0 + is-typed-array: 1.1.15 + + is-date-object@1.1.0: + dependencies: + call-bound: 1.0.4 + has-tostringtag: 1.0.2 + + is-document.all@1.0.0: + dependencies: + call-bound: 1.0.4 + + is-extglob@2.1.1: {} + + is-finalizationregistry@1.1.1: + dependencies: + call-bound: 1.0.4 + + is-generator-function@1.1.2: + dependencies: + call-bound: 1.0.4 + generator-function: 2.0.1 + get-proto: 1.0.1 + has-tostringtag: 1.0.2 + safe-regex-test: 1.1.0 + + is-glob@4.0.3: + dependencies: + is-extglob: 2.1.1 + + is-map@2.0.3: {} + + is-negative-zero@2.0.3: {} + + is-number-object@1.1.1: + dependencies: + call-bound: 1.0.4 + has-tostringtag: 1.0.2 + + is-number@7.0.0: {} + + is-regex@1.2.1: + dependencies: + call-bound: 1.0.4 + gopd: 1.2.0 + has-tostringtag: 1.0.2 + hasown: 2.0.4 + + is-set@2.0.3: {} + + is-shared-array-buffer@1.0.4: + dependencies: + call-bound: 1.0.4 + + is-string@1.1.1: + dependencies: + call-bound: 1.0.4 + has-tostringtag: 1.0.2 + + is-symbol@1.1.1: + dependencies: + call-bound: 1.0.4 + has-symbols: 1.1.0 + safe-regex-test: 1.1.0 + + is-typed-array@1.1.15: + dependencies: + which-typed-array: 1.1.22 + + is-weakmap@2.0.2: {} + + is-weakref@1.1.1: + dependencies: + call-bound: 1.0.4 + + is-weakset@2.0.4: + dependencies: + call-bound: 1.0.4 + get-intrinsic: 1.3.0 + + isarray@2.0.5: {} + + isexe@2.0.0: {} + + iterator.prototype@1.1.5: + dependencies: + define-data-property: 1.1.4 + es-object-atoms: 1.1.2 + get-intrinsic: 1.3.0 + get-proto: 1.0.1 + has-symbols: 1.1.0 + set-function-name: 2.0.2 + + jiti@2.7.0: {} + + js-tokens@4.0.0: {} + + js-yaml@4.3.1: + dependencies: + argparse: 2.0.1 + + jsesc@3.1.0: {} + + json-buffer@3.0.1: {} + + json-schema-traverse@0.4.1: {} + + json-schema@0.4.0: {} + + json-stable-stringify-without-jsonify@1.0.1: {} + + json5@1.0.2: + dependencies: + minimist: 1.2.8 + + json5@2.2.3: {} + + jsx-ast-utils@3.3.5: + dependencies: + array-includes: 3.1.9 + array.prototype.flat: 1.3.3 + object.assign: 4.1.7 + object.values: 1.2.1 + + keyv@4.5.4: + dependencies: + json-buffer: 3.0.1 + + language-subtag-registry@0.3.23: {} + + language-tags@1.0.9: + dependencies: + language-subtag-registry: 0.3.23 + + levn@0.4.1: + dependencies: + prelude-ls: 1.2.1 + type-check: 0.4.0 + + lightningcss-android-arm64@1.32.0: + optional: true + + lightningcss-darwin-arm64@1.32.0: + optional: true + + lightningcss-darwin-x64@1.32.0: + optional: true + + lightningcss-freebsd-x64@1.32.0: + optional: true + + lightningcss-linux-arm-gnueabihf@1.32.0: + optional: true + + lightningcss-linux-arm64-gnu@1.32.0: + optional: true + + lightningcss-linux-arm64-musl@1.32.0: + optional: true + + lightningcss-linux-x64-gnu@1.32.0: + optional: true + + lightningcss-linux-x64-musl@1.32.0: + optional: true + + lightningcss-win32-arm64-msvc@1.32.0: + optional: true + + lightningcss-win32-x64-msvc@1.32.0: + optional: true + + lightningcss@1.32.0: + dependencies: + detect-libc: 2.1.2 + optionalDependencies: + lightningcss-android-arm64: 1.32.0 + lightningcss-darwin-arm64: 1.32.0 + lightningcss-darwin-x64: 1.32.0 + lightningcss-freebsd-x64: 1.32.0 + lightningcss-linux-arm-gnueabihf: 1.32.0 + lightningcss-linux-arm64-gnu: 1.32.0 + lightningcss-linux-arm64-musl: 1.32.0 + lightningcss-linux-x64-gnu: 1.32.0 + lightningcss-linux-x64-musl: 1.32.0 + lightningcss-win32-arm64-msvc: 1.32.0 + lightningcss-win32-x64-msvc: 1.32.0 + + locate-path@6.0.0: + dependencies: + p-locate: 5.0.0 + + lodash.merge@4.6.2: {} + + loose-envify@1.4.0: + dependencies: + js-tokens: 4.0.0 + + lru-cache@5.1.1: + dependencies: + yallist: 3.1.1 + + magic-string@0.30.21: + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 + + math-intrinsics@1.1.0: {} + + merge2@1.4.1: {} + + micromatch@4.0.8: + dependencies: + braces: 3.0.3 + picomatch: 2.3.2 + + minimatch@10.2.6: + dependencies: + brace-expansion: 5.0.9 + + minimatch@3.1.5: + dependencies: + brace-expansion: 1.1.18 + + minimist@1.2.8: {} + + ms@2.1.3: {} + + nanoid@3.3.18: {} + + napi-postinstall@0.3.4: {} + + natural-compare@1.4.0: {} + + next@16.3.2(@babel/core@7.29.7)(@types/node@20.19.43)(react-dom@19.2.8(react@19.2.8))(react@19.2.8): + dependencies: + '@next/env': 16.3.2 + '@swc/helpers': 0.5.23 + baseline-browser-mapping: 2.11.19 + caniuse-lite: 1.0.30001810 + postcss: 8.5.23 + react: 19.2.8 + react-dom: 19.2.8(react@19.2.8) + styled-jsx: 5.1.6(@babel/core@7.29.7)(react@19.2.8) + optionalDependencies: + '@next/swc-darwin-arm64': 16.3.2 + '@next/swc-darwin-x64': 16.3.2 + '@next/swc-linux-arm64-gnu': 16.3.2 + '@next/swc-linux-arm64-musl': 16.3.2 + '@next/swc-linux-x64-gnu': 16.3.2 + '@next/swc-linux-x64-musl': 16.3.2 + '@next/swc-win32-arm64-msvc': 16.3.2 + '@next/swc-win32-x64-msvc': 16.3.2 + sharp: 0.35.3(@types/node@20.19.43) + transitivePeerDependencies: + - '@babel/core' + - '@types/node' + - babel-plugin-macros + + node-exports-info@1.6.2: + dependencies: + array.prototype.flatmap: 1.3.3 + es-errors: 1.3.0 + object.entries: 1.1.9 + semver: 6.3.1 + + node-releases@2.0.53: {} + + object-assign@4.1.1: {} + + object-inspect@1.13.4: {} + + object-keys@1.1.1: {} + + object.assign@4.1.7: + dependencies: + call-bind: 1.0.9 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-object-atoms: 1.1.2 + has-symbols: 1.1.0 + object-keys: 1.1.1 + + object.entries@1.1.9: + dependencies: + call-bind: 1.0.9 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-object-atoms: 1.1.2 + + object.fromentries@2.0.8: + dependencies: + call-bind: 1.0.9 + define-properties: 1.2.1 + es-abstract: 1.24.2 + es-object-atoms: 1.1.2 + + object.groupby@1.0.3: + dependencies: + call-bind: 1.0.9 + define-properties: 1.2.1 + es-abstract: 1.24.2 + + object.values@1.2.1: + dependencies: + call-bind: 1.0.9 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-object-atoms: 1.1.2 + + optionator@0.9.4: + dependencies: + deep-is: 0.1.4 + fast-levenshtein: 2.0.6 + levn: 0.4.1 + prelude-ls: 1.2.1 + type-check: 0.4.0 + word-wrap: 1.2.5 + + own-keys@1.0.2: + dependencies: + call-bound: 1.0.4 + get-intrinsic: 1.3.0 + object-keys: 1.1.1 + safe-push-apply: 1.0.0 + + p-limit@3.1.0: + dependencies: + yocto-queue: 0.1.0 + + p-locate@5.0.0: + dependencies: + p-limit: 3.1.0 + + parent-module@1.0.1: + dependencies: + callsites: 3.1.0 + + path-exists@4.0.0: {} + + path-key@3.1.1: {} + + path-parse@1.0.7: {} + + picocolors@1.1.1: {} + + picomatch@2.3.2: {} + + picomatch@4.0.7: {} + + pkce-challenge@5.0.1: {} + + possible-typed-array-names@1.1.0: {} + + postcss@8.5.23: + dependencies: + nanoid: 3.3.18 + picocolors: 1.1.1 + source-map-js: 1.2.1 + + postcss@8.5.26: + dependencies: + nanoid: 3.3.18 + picocolors: 1.1.1 + source-map-js: 1.2.1 + + prelude-ls@1.2.1: {} + + prop-types@15.8.1: + dependencies: + loose-envify: 1.4.0 + object-assign: 4.1.1 + react-is: 16.13.1 + + punycode@2.3.1: {} + + queue-microtask@1.2.3: {} + + react-dom@19.2.8(react@19.2.8): + dependencies: + react: 19.2.8 + scheduler: 0.27.0 + + react-is@16.13.1: {} + + react@19.2.8: {} + + reflect.getprototypeof@1.0.10: + dependencies: + call-bind: 1.0.9 + define-properties: 1.2.1 + es-abstract: 1.24.2 + es-errors: 1.3.0 + es-object-atoms: 1.1.2 + get-intrinsic: 1.3.0 + get-proto: 1.0.1 + which-builtin-type: 1.2.1 + + regexp.prototype.flags@1.5.4: + dependencies: + call-bind: 1.0.9 + define-properties: 1.2.1 + es-errors: 1.3.0 + get-proto: 1.0.1 + gopd: 1.2.0 + set-function-name: 2.0.2 + + resolve-from@4.0.0: {} + + resolve-pkg-maps@1.0.0: {} + + resolve@2.0.0-next.7: + dependencies: + es-errors: 1.3.0 + is-core-module: 2.16.2 + node-exports-info: 1.6.2 + object-keys: 1.1.1 + path-parse: 1.0.7 + supports-preserve-symlinks-flag: 1.0.0 + + reusify@1.1.0: {} + + run-parallel@1.2.0: + dependencies: + queue-microtask: 1.2.3 + + safe-array-concat@1.1.4: + dependencies: + call-bind: 1.0.9 + call-bound: 1.0.4 + get-intrinsic: 1.3.0 + has-symbols: 1.1.0 + isarray: 2.0.5 + + safe-push-apply@1.0.0: + dependencies: + es-errors: 1.3.0 + isarray: 2.0.5 + + safe-regex-test@1.1.0: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-regex: 1.2.1 + + scheduler@0.27.0: {} + + semver@6.3.1: {} + + semver@7.8.5: {} + + set-function-length@1.2.2: + dependencies: + define-data-property: 1.1.4 + es-errors: 1.3.0 + function-bind: 1.1.2 + get-intrinsic: 1.3.0 + gopd: 1.2.0 + has-property-descriptors: 1.0.2 + + set-function-name@2.0.2: + dependencies: + define-data-property: 1.1.4 + es-errors: 1.3.0 + functions-have-names: 1.2.3 + has-property-descriptors: 1.0.2 + + set-proto@1.0.0: + dependencies: + dunder-proto: 1.0.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.2 + + sharp@0.35.3(@types/node@20.19.43): + dependencies: + '@img/colour': 1.1.0 + detect-libc: 2.1.2 + semver: 7.8.5 + optionalDependencies: + '@img/sharp-darwin-arm64': 0.35.3 + '@img/sharp-darwin-x64': 0.35.3 + '@img/sharp-freebsd-wasm32': 0.35.3 + '@img/sharp-libvips-darwin-arm64': 1.3.2 + '@img/sharp-libvips-darwin-x64': 1.3.2 + '@img/sharp-libvips-linux-arm': 1.3.2 + '@img/sharp-libvips-linux-arm64': 1.3.2 + '@img/sharp-libvips-linux-ppc64': 1.3.2 + '@img/sharp-libvips-linux-riscv64': 1.3.2 + '@img/sharp-libvips-linux-s390x': 1.3.2 + '@img/sharp-libvips-linux-x64': 1.3.2 + '@img/sharp-libvips-linuxmusl-arm64': 1.3.2 + '@img/sharp-libvips-linuxmusl-x64': 1.3.2 + '@img/sharp-linux-arm': 0.35.3 + '@img/sharp-linux-arm64': 0.35.3 + '@img/sharp-linux-ppc64': 0.35.3 + '@img/sharp-linux-riscv64': 0.35.3 + '@img/sharp-linux-s390x': 0.35.3 + '@img/sharp-linux-x64': 0.35.3 + '@img/sharp-linuxmusl-arm64': 0.35.3 + '@img/sharp-linuxmusl-x64': 0.35.3 + '@img/sharp-webcontainers-wasm32': 0.35.3 + '@img/sharp-win32-arm64': 0.35.3 + '@img/sharp-win32-ia32': 0.35.3 + '@img/sharp-win32-x64': 0.35.3 + '@types/node': 20.19.43 + optional: true + + shebang-command@2.0.0: + dependencies: + shebang-regex: 3.0.0 + + shebang-regex@3.0.0: {} + + side-channel-list@1.0.1: + dependencies: + es-errors: 1.3.0 + object-inspect: 1.13.4 + + side-channel-map@1.0.1: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + object-inspect: 1.13.4 + + side-channel-weakmap@1.0.2: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + object-inspect: 1.13.4 + side-channel-map: 1.0.1 + + side-channel@1.1.1: + dependencies: + es-errors: 1.3.0 + object-inspect: 1.13.4 + side-channel-list: 1.0.1 + side-channel-map: 1.0.1 + side-channel-weakmap: 1.0.2 + + source-map-js@1.2.1: {} + + stable-hash@0.0.5: {} + + stop-iteration-iterator@1.1.0: + dependencies: + es-errors: 1.3.0 + internal-slot: 1.1.0 + + string.prototype.includes@2.0.1: + dependencies: + call-bind: 1.0.9 + define-properties: 1.2.1 + es-abstract: 1.24.2 + + string.prototype.matchall@4.0.12: + dependencies: + call-bind: 1.0.9 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-abstract: 1.24.2 + es-errors: 1.3.0 + es-object-atoms: 1.1.2 + get-intrinsic: 1.3.0 + gopd: 1.2.0 + has-symbols: 1.1.0 + internal-slot: 1.1.0 + regexp.prototype.flags: 1.5.4 + set-function-name: 2.0.2 + side-channel: 1.1.1 + + string.prototype.repeat@1.0.0: + dependencies: + define-properties: 1.2.1 + es-abstract: 1.24.2 + + string.prototype.trim@1.2.11: + dependencies: + call-bind: 1.0.9 + call-bound: 1.0.4 + define-data-property: 1.1.4 + define-properties: 1.2.1 + es-abstract: 1.24.2 + es-object-atoms: 1.1.2 + has-property-descriptors: 1.0.2 + safe-regex-test: 1.1.0 + + string.prototype.trimend@1.0.10: + dependencies: + call-bind: 1.0.9 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-object-atoms: 1.1.2 + + string.prototype.trimstart@1.0.8: + dependencies: + call-bind: 1.0.9 + define-properties: 1.2.1 + es-object-atoms: 1.1.2 + + strip-bom@3.0.0: {} + + strip-json-comments@3.1.1: {} + + styled-jsx@5.1.6(@babel/core@7.29.7)(react@19.2.8): + dependencies: + client-only: 0.0.1 + react: 19.2.8 + optionalDependencies: + '@babel/core': 7.29.7 + + supports-color@7.2.0: + dependencies: + has-flag: 4.0.0 + + supports-preserve-symlinks-flag@1.0.0: {} + + swr@2.5.1(react@19.2.8): + dependencies: + dequal: 2.0.3 + react: 19.2.8 + use-sync-external-store: 1.6.0(react@19.2.8) + + tailwindcss@4.3.3: {} + + tapable@2.3.3: {} + + throttleit@2.1.0: {} + + tinyglobby@0.2.17: + dependencies: + fdir: 6.5.0(picomatch@4.0.7) + picomatch: 4.0.7 + + to-regex-range@5.0.1: + dependencies: + is-number: 7.0.0 + + ts-api-utils@2.5.0(typescript@5.9.3): + dependencies: + typescript: 5.9.3 + + tsconfig-paths@3.15.0: + dependencies: + '@types/json5': 0.0.29 + json5: 1.0.2 + minimist: 1.2.8 + strip-bom: 3.0.0 + + tslib@2.8.1: {} + + type-check@0.4.0: + dependencies: + prelude-ls: 1.2.1 + + typed-array-buffer@1.0.3: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-typed-array: 1.1.15 + + typed-array-byte-length@1.0.3: + dependencies: + call-bind: 1.0.9 + for-each: 0.3.5 + gopd: 1.2.0 + has-proto: 1.2.0 + is-typed-array: 1.1.15 + + typed-array-byte-offset@1.0.4: + dependencies: + available-typed-arrays: 1.0.7 + call-bind: 1.0.9 + for-each: 0.3.5 + gopd: 1.2.0 + has-proto: 1.2.0 + is-typed-array: 1.1.15 + reflect.getprototypeof: 1.0.10 + + typed-array-length@1.0.8: + dependencies: + call-bind: 1.0.9 + for-each: 0.3.5 + gopd: 1.2.0 + is-typed-array: 1.1.15 + possible-typed-array-names: 1.1.0 + reflect.getprototypeof: 1.0.10 + + typescript-eslint@8.68.0(eslint@9.39.5(jiti@2.7.0))(typescript@5.9.3): + dependencies: + '@typescript-eslint/eslint-plugin': 8.68.0(@typescript-eslint/parser@8.68.0(eslint@9.39.5(jiti@2.7.0))(typescript@5.9.3))(eslint@9.39.5(jiti@2.7.0))(typescript@5.9.3) + '@typescript-eslint/parser': 8.68.0(eslint@9.39.5(jiti@2.7.0))(typescript@5.9.3) + '@typescript-eslint/typescript-estree': 8.68.0(typescript@5.9.3) + '@typescript-eslint/utils': 8.68.0(eslint@9.39.5(jiti@2.7.0))(typescript@5.9.3) + eslint: 9.39.5(jiti@2.7.0) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + typescript@5.9.3: {} + + unbox-primitive@1.1.0: + dependencies: + call-bound: 1.0.4 + has-bigints: 1.1.0 + has-symbols: 1.1.0 + which-boxed-primitive: 1.1.1 + + undici-types@6.21.0: {} + + undici@7.29.0: {} + + unrs-resolver@1.12.2: + dependencies: + napi-postinstall: 0.3.4 + optionalDependencies: + '@unrs/resolver-binding-android-arm-eabi': 1.12.2 + '@unrs/resolver-binding-android-arm64': 1.12.2 + '@unrs/resolver-binding-darwin-arm64': 1.12.2 + '@unrs/resolver-binding-darwin-x64': 1.12.2 + '@unrs/resolver-binding-freebsd-x64': 1.12.2 + '@unrs/resolver-binding-linux-arm-gnueabihf': 1.12.2 + '@unrs/resolver-binding-linux-arm-musleabihf': 1.12.2 + '@unrs/resolver-binding-linux-arm64-gnu': 1.12.2 + '@unrs/resolver-binding-linux-arm64-musl': 1.12.2 + '@unrs/resolver-binding-linux-loong64-gnu': 1.12.2 + '@unrs/resolver-binding-linux-loong64-musl': 1.12.2 + '@unrs/resolver-binding-linux-ppc64-gnu': 1.12.2 + '@unrs/resolver-binding-linux-riscv64-gnu': 1.12.2 + '@unrs/resolver-binding-linux-riscv64-musl': 1.12.2 + '@unrs/resolver-binding-linux-s390x-gnu': 1.12.2 + '@unrs/resolver-binding-linux-x64-gnu': 1.12.2 + '@unrs/resolver-binding-linux-x64-musl': 1.12.2 + '@unrs/resolver-binding-openharmony-arm64': 1.12.2 + '@unrs/resolver-binding-wasm32-wasi': 1.12.2 + '@unrs/resolver-binding-win32-arm64-msvc': 1.12.2 + '@unrs/resolver-binding-win32-ia32-msvc': 1.12.2 + '@unrs/resolver-binding-win32-x64-msvc': 1.12.2 + + update-browserslist-db@1.3.1(browserslist@4.28.8): + dependencies: + browserslist: 4.28.8 + escalade: 3.2.0 + picocolors: 1.1.1 + + uri-js@4.4.1: + dependencies: + punycode: 2.3.1 + + use-sync-external-store@1.6.0(react@19.2.8): + dependencies: + react: 19.2.8 + + which-boxed-primitive@1.1.1: + dependencies: + is-bigint: 1.1.0 + is-boolean-object: 1.2.2 + is-number-object: 1.1.1 + is-string: 1.1.1 + is-symbol: 1.1.1 + + which-builtin-type@1.2.1: + dependencies: + call-bound: 1.0.4 + function.prototype.name: 1.2.0 + has-tostringtag: 1.0.2 + is-async-function: 2.1.1 + is-date-object: 1.1.0 + is-finalizationregistry: 1.1.1 + is-generator-function: 1.1.2 + is-regex: 1.2.1 + is-weakref: 1.1.1 + isarray: 2.0.5 + which-boxed-primitive: 1.1.1 + which-collection: 1.0.2 + which-typed-array: 1.1.22 + + which-collection@1.0.2: + dependencies: + is-map: 2.0.3 + is-set: 2.0.3 + is-weakmap: 2.0.2 + is-weakset: 2.0.4 + + which-typed-array@1.1.22: + dependencies: + available-typed-arrays: 1.0.7 + call-bind: 1.0.9 + call-bound: 1.0.4 + for-each: 0.3.5 + get-proto: 1.0.1 + gopd: 1.2.0 + has-tostringtag: 1.0.2 + + which@2.0.2: + dependencies: + isexe: 2.0.0 + + word-wrap@1.2.5: {} + + yallist@3.1.1: {} + + yocto-queue@0.1.0: {} + + zod-validation-error@4.0.2(zod@4.4.3): + dependencies: + zod: 4.4.3 + + zod@4.4.3: {} diff --git a/app/pnpm-workspace.yaml b/app/pnpm-workspace.yaml new file mode 100644 index 0000000..581a9d5 --- /dev/null +++ b/app/pnpm-workspace.yaml @@ -0,0 +1,3 @@ +ignoredBuiltDependencies: + - sharp + - unrs-resolver diff --git a/app/postcss.config.mjs b/app/postcss.config.mjs new file mode 100644 index 0000000..61e3684 --- /dev/null +++ b/app/postcss.config.mjs @@ -0,0 +1,7 @@ +const config = { + plugins: { + "@tailwindcss/postcss": {}, + }, +}; + +export default config; diff --git a/app/public/file.svg b/app/public/file.svg new file mode 100644 index 0000000..004145c --- /dev/null +++ b/app/public/file.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/public/globe.svg b/app/public/globe.svg new file mode 100644 index 0000000..567f17b --- /dev/null +++ b/app/public/globe.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/public/next.svg b/app/public/next.svg new file mode 100644 index 0000000..5174b28 --- /dev/null +++ b/app/public/next.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/public/vercel.svg b/app/public/vercel.svg new file mode 100644 index 0000000..7705396 --- /dev/null +++ b/app/public/vercel.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/public/window.svg b/app/public/window.svg new file mode 100644 index 0000000..b2b2a44 --- /dev/null +++ b/app/public/window.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/src/app/favicon.ico b/app/src/app/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..718d6fea4835ec2d246af9800eddb7ffb276240c GIT binary patch literal 25931 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 LrJugUA?W`A8`#=m literal 0 HcmV?d00001 diff --git a/app/src/app/globals.css b/app/src/app/globals.css new file mode 100644 index 0000000..a2dc41e --- /dev/null +++ b/app/src/app/globals.css @@ -0,0 +1,26 @@ +@import "tailwindcss"; + +:root { + --background: #ffffff; + --foreground: #171717; +} + +@theme inline { + --color-background: var(--background); + --color-foreground: var(--foreground); + --font-sans: var(--font-geist-sans); + --font-mono: var(--font-geist-mono); +} + +@media (prefers-color-scheme: dark) { + :root { + --background: #0a0a0a; + --foreground: #ededed; + } +} + +body { + background: var(--background); + color: var(--foreground); + font-family: Arial, Helvetica, sans-serif; +} diff --git a/app/src/app/layout.tsx b/app/src/app/layout.tsx new file mode 100644 index 0000000..9852c15 --- /dev/null +++ b/app/src/app/layout.tsx @@ -0,0 +1,29 @@ +import type { Metadata } from "next"; +import { Geist, Geist_Mono } from "next/font/google"; +import "./globals.css"; + +const geistSans = Geist({ + variable: "--font-geist-sans", + subsets: ["latin"], +}); + +const geistMono = Geist_Mono({ + variable: "--font-geist-mono", + subsets: ["latin"], +}); + +export const metadata: Metadata = { + title: "Create Next App", + description: "Generated by create next app", +}; + +export default function RootLayout({ children }: LayoutProps<"/">) { + return ( + + {children} + + ); +} diff --git a/app/src/app/page.tsx b/app/src/app/page.tsx new file mode 100644 index 0000000..c887311 --- /dev/null +++ b/app/src/app/page.tsx @@ -0,0 +1,69 @@ +import Image from "next/image"; + +export default function Home() { + return ( +

    +
    + Next.js logo +
    +

    + To get started, edit the{" "} + + page.tsx + {" "} + file. +

    +

    + Looking for a starting point or more instructions? Head over to{" "} + + Templates + {" "} + or the{" "} + + Learning + {" "} + center. +

    +
    + +
    +
    + ); +} diff --git a/app/tsconfig.json b/app/tsconfig.json new file mode 100644 index 0000000..cf9c65d --- /dev/null +++ b/app/tsconfig.json @@ -0,0 +1,34 @@ +{ + "compilerOptions": { + "target": "ES2017", + "lib": ["dom", "dom.iterable", "esnext"], + "allowJs": true, + "skipLibCheck": true, + "strict": true, + "noEmit": true, + "esModuleInterop": true, + "module": "esnext", + "moduleResolution": "bundler", + "resolveJsonModule": true, + "isolatedModules": true, + "jsx": "react-jsx", + "incremental": true, + "plugins": [ + { + "name": "next" + } + ], + "paths": { + "@/*": ["./src/*"] + } + }, + "include": [ + "next-env.d.ts", + "**/*.ts", + "**/*.tsx", + ".next/types/**/*.ts", + ".next/dev/types/**/*.ts", + "**/*.mts" + ], + "exclude": ["node_modules"] +} From 2ad8162899e7c9a783ad80583542e7654f1f4519 Mon Sep 17 00:00:00 2001 From: Sam Frons Date: Tue, 25 Aug 2026 16:43:19 +0200 Subject: [PATCH 05/46] docs: add ingestion and evals scaffold READMEs Placeholder READMEs describing the planned corpus -> chunking -> Voyage embeddings -> Supabase pgvector pipeline (ingestion/) and the independent-judge eval harness over petri/seeds/ scenarios (evals/). Scripts to come. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01HCrPUhxWVtRBsYSSc7sBHU --- evals/README.md | 11 +++++++++++ ingestion/README.md | 9 +++++++++ 2 files changed, 20 insertions(+) create mode 100644 evals/README.md create mode 100644 ingestion/README.md diff --git a/evals/README.md b/evals/README.md new file mode 100644 index 0000000..2235d35 --- /dev/null +++ b/evals/README.md @@ -0,0 +1,11 @@ +# Evals + +Eval harness for the assistant, built on the 26 scenarios in +`petri/seeds/humanitarian_test_scenarios.json` (crisis classification, +protection principles, disinformation resistance, and more, each with +`evaluation_criteria` and expected facts). Unlike the prior prototype's +auditor, the judge here is a model from a different family than the one +being tested, and reports are written honestly — no self-grading, no +thresholds that let empty or off-topic answers pass. See +`research/README.md` for why that distinction matters. Harness scripts to +come. diff --git a/ingestion/README.md b/ingestion/README.md new file mode 100644 index 0000000..b554845 --- /dev/null +++ b/ingestion/README.md @@ -0,0 +1,9 @@ +# Ingestion + +Pipeline for turning primary humanitarian references into the corpus the +`search_standards` tool queries: source documents (Sphere Handbook, Core +Humanitarian Standard, IASC guidance, etc.) go into `ingestion/corpus/`, +get split with contextual chunking (chunks retain surrounding document +context rather than being split blind), are embedded with Voyage AI, and +are written into Supabase pgvector for hybrid (vector + keyword) search. +Ingestion scripts to come. From be516a70869890be810a3b240fda83191e9c836c Mon Sep 17 00:00:00 2001 From: Sam Frons Date: Tue, 25 Aug 2026 16:47:09 +0200 Subject: [PATCH 06/46] Keep corpus PDFs out of git, add reproducible fetch script The Sphere Handbook is all-rights-reserved: its copyright page permits local educational/research use but not redistribution, so the PDFs cannot live in the repo. fetch-corpus.sh re-downloads all five documents from the mirrors recorded in SOURCES.md (the canonical publisher domains sit behind bot-challenge WAFs) and verifies sha256, so the corpus stays reproducible without being committed. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01HCrPUhxWVtRBsYSSc7sBHU --- .gitignore | 10 ++++ ingestion/corpus/SOURCES.md | 76 +++++++++++++++++++++++++++ ingestion/fetch-corpus.sh | 101 ++++++++++++++++++++++++++++++++++++ 3 files changed, 187 insertions(+) create mode 100644 ingestion/corpus/SOURCES.md create mode 100755 ingestion/fetch-corpus.sh diff --git a/.gitignore b/.gitignore index 8c6b40f..d0d4ae5 100644 --- a/.gitignore +++ b/.gitignore @@ -40,6 +40,16 @@ coverage/ !.env.example !*.env.example +# --- Corpus source documents (licensing) --- +# The Sphere Handbook is all-rights-reserved: local educational/research use is +# permitted, redistribution is not. Other corpus PDFs are looser but are kept out +# of git for consistency. Re-download them with ingestion/fetch-corpus.sh. +# Provenance and per-file licenses stay tracked in ingestion/corpus/SOURCES.md. +ingestion/corpus/*.pdf + +# --- Ingestion pipeline artifacts --- +ingestion/.extract-cache/ + # --- Archives / backups --- *.zip *.tar.gz diff --git a/ingestion/corpus/SOURCES.md b/ingestion/corpus/SOURCES.md new file mode 100644 index 0000000..9f11122 --- /dev/null +++ b/ingestion/corpus/SOURCES.md @@ -0,0 +1,76 @@ +# Humanitarian Corpus — Sources + +Download date for all files below: **2026-08-25**. + +Note on provenance: the canonical publisher domains for these documents +(`spherestandards.org`, `interagencystandingcommittee.org`) are behind bot-challenge +WAFs (Cloudflare / AWS WAF) that block non-browser HTTP clients. Every file here was +retrieved from an official secondary mirror of the same publisher-issued PDF +(UNHCR's Emergency Handbook document library, or the CCCM Cluster's S3-hosted +resource library). Content was verified against the publisher's own copyright page +embedded in each PDF where present. No content originates from a non-official +republisher (e.g. no Scribd/summary sites were used). + +| File | Title | Edition/Year | Size | License | +|---|---|---|---|---| +| `Sphere-Handbook-2018-EN.pdf` | The Sphere Handbook: Humanitarian Charter and Minimum Standards in Humanitarian Response | 4th edition, 2018 | 6.0 MB (458 pp) | **All rights reserved** — see flag below | +| `CHS-2024.pdf` | The Core Humanitarian Standard on Quality and Accountability | 2024 Edition | 220 KB (6 pp) | Free educational reproduction w/ attribution; translation/adaptation needs written permission | +| `IASC-Data-Responsibility-2023.pdf` | IASC Operational Guidance on Data Responsibility in Humanitarian Action | April 2023 (2nd edition) | 916 KB (44 pp) | Presumed IASC general-distribution terms — not independently confirmed, see flag below | +| `IASC-Protection-Policy-2016.pdf` | IASC Policy on Protection in Humanitarian Action | October 2016 | 640 KB (40 pp) | Presumed IASC general-distribution terms — not independently confirmed, see flag below | +| `IASC-Disability-Inclusion-2019.pdf` | IASC Guidelines on the Inclusion of Persons with Disabilities in Humanitarian Action | July 2019 | 2.0 MB (110 pp) | © IASC 2019. Reproduction/translation authorized, non-commercial only | + +--- + +## 1. Sphere Handbook 2018 (English) +- **Direct URL used:** `https://emergency.unhcr.org/sites/default/files/2024-01/Sphere-Handbook-2018-EN.pdf` +- **Canonical publisher page:** https://spherestandards.org/handbook-2018/ (PDF at spherestandards.org returns HTTP 403 / Cloudflare challenge to non-browser clients) +- **License — ⚠️ FLAG, does NOT match the assumed CC BY-SA 4.0:** The Handbook's own copyright page (p.3) reads verbatim: + > "Copyright © Sphere Association, 2018 ... All rights reserved. This material is copyrighted but may be reproduced without fee for educational purposes, including for training, research and programme activities, provided that the copyright holder is acknowledged. It is not intended for resale. For copying in other circumstances, posting online, reuse in other publications or for translation or adaptation, prior written permission must be obtained by emailing info@spherestandards.org." + + This is **not** a Creative Commons license and does **not** permit redistribution/posting online/reuse in other publications without written permission from Sphere Association. Sphere's general "Intellectual Property" page (spherestandards.org/intellectual-property/) states other Sphere *resources* are released under CC BY 4.0, but the Handbook PDF itself explicitly reserves rights and requires permission for reuse beyond direct educational/training/research/programme use. **Recommend: keep local for RAG ingestion (falls under "research and programme activities"), but do NOT commit to a public repo or redistribute externally without contacting info@spherestandards.org for written permission. Gitignore candidate.** + +## 2. Core Humanitarian Standard (CHS) 2024 revision +- **Direct URL used:** `https://emergency.unhcr.org/sites/default/files/2024-01/4.%20The%20Core%20Humanitarian%20Standard%20on%20Quality%20and%20Accountability.pdf` +- **Canonical publisher page:** https://www.corehumanitarianstandard.org/ (site is a JS-driven interactive handbook, no static PDF link found; FAQ at corehumanitarianstandard.org/faq describes terms) +- **License:** Copyright held jointly by CHS Alliance, Groupe URD and Sphere. Per the official FAQ: "The copyright holders welcome reproduction of the CHS for educational purposes, including in training, research and programme activities, provided that the CHS is acknowledged. To translate or adapt all or any part of the CHS, written permission must be obtained." No explicit copyright/license text is embedded in this particular 6-page PDF (it is the short-form standard text only, not the full guidance notes edition) — the terms above come from the publisher's FAQ page, not the file itself. +- Freely usable for this RAG project (educational/research use); do not redistribute a modified/translated version without permission. + +## 3–4. IASC operational guidance documents +Two of the three requested IASC documents were obtained (the Centrality of Protection policy was substituted with its closest available equivalent — see note). + +### a. IASC Operational Guidance on Data Responsibility in Humanitarian Action (2023) +- **Direct URL used:** `https://emergency.unhcr.org/sites/default/files/2023-11/IASC%20Operational%20Guidance%20on%20Data%20Responsibility%20in%20Humanitarian%20Action,%202023.pdf` +- **Canonical publisher page:** https://interagencystandingcommittee.org/operational-response/iasc-operational-guidance-data-responsibility-humanitarian-action (blocked to non-browser clients by AWS WAF challenge — HTTP 202 "x-amzn-waf-action: challenge") +- **License:** ⚠️ No copyright/license page found in the extracted text of this specific PDF. IASC's standard boilerplate on sibling documents (see Disability Inclusion doc below) states IASC materials are "issued for general distribution" with reproduction/translation "authorized, except for commercial purposes." This is presumed to apply here as IASC's consistent house policy, but it is **not independently confirmed within this file** — flagging for review before treating as fully verified. + +### b. IASC Policy on Protection in Humanitarian Action (2016) +- Requested item was the "Centrality of Protection" policy; the closest/authoritative source is this 2016 IASC Policy on Protection in Humanitarian Action, which is the document that formally establishes the Centrality of Protection principle (the IASC's 2013 Centrality of Protection statement is folded into this policy). +- **Direct URL used:** `https://emergency.unhcr.org/sites/default/files/2024-01/4.%20IASC%20Policy%20on%20Protection%20in%20Humanitarian%20Action,%202016.pdf` +- **Canonical publisher page:** https://interagencystandingcommittee.org/iasc-protection-priority-global-protection-cluster/iasc-policy-protection-humanitarian-action-2016 (blocked to non-browser clients, same AWS WAF challenge) +- **License:** ⚠️ Same as above — no in-document copyright/license page found; presumed IASC general-distribution terms, not independently confirmed in this file. + +### c. IASC Guidelines on the Inclusion of Persons with Disabilities in Humanitarian Action (2019) +- **Direct URL used:** `https://s3.eu-west-1.amazonaws.com/cccmcluster.org/public/2019-11/iasc_guidelines_on_the_inclusion_of_persons_with_disabilities_in_humanitarian_action_2019.pdf` (mirrored by the CCCM Cluster; the publisher's own site is behind the same AWS WAF challenge as above) +- **Canonical publisher page:** https://interagencystandingcommittee.org/iasc-guidelines-on-inclusion-of-persons-with-disabilities-in-humanitarian-action-2019 +- **License:** Confirmed in-document (p. xiii): "Copyright © Inter-Agency Standing Committee (IASC) 2019. All rights reserved. This document is issued for general distribution. Reproductions and translations are authorized, except for commercial purposes, provided the source is acknowledged." — freely redistributable non-commercially with attribution. **Safe to commit.** + +## 5. Multilingual track (optional) — NOT obtained +- Attempted Sphere Handbook 2018 French and Arabic PDFs. +- The only direct Arabic URL found (`spherestandards.org/wp-content/uploads/The-Sphere-Handbook-2018-AR-2.pdf`, surfaced via a medbox.org mirror page) points back to the Cloudflare-protected spherestandards.org domain and returns HTTP 403 to curl. +- Guessed UNHCR mirror paths for `-FR`/`-AR` suffixes (following the English mirror's naming pattern) returned HTTP 302 redirects to non-existent resources (i.e., not present on that mirror). +- No further mirror was found within a reasonable search effort. **Not downloaded — skipped per "optional if easy" instruction.** + +## Not attempted / blocked entirely +None of the requested documents were fully unobtainable — all 5 primary targets were acquired via mirrors. Only the optional multilingual Sphere editions were skipped. + +--- + +## Summary of license flags for follow-up decision + +| File | Redistribution safe? | Action needed | +|---|---|---| +| `Sphere-Handbook-2018-EN.pdf` | **No** — all rights reserved, no online posting/reuse without written permission | Keep local only; add to `.gitignore`; consider emailing info@spherestandards.org for permission if this repo will ever be shared/published | +| `CHS-2024.pdf` | Educational/research use OK; no redistribution of modified/translated versions | Keep as-is, attribute CHS Alliance/Groupe URD/Sphere | +| `IASC-Data-Responsibility-2023.pdf` | Presumed OK (unconfirmed in-file) | Verify against IASC's site terms of use before treating as fully cleared | +| `IASC-Protection-Policy-2016.pdf` | Presumed OK (unconfirmed in-file) | Same as above | +| `IASC-Disability-Inclusion-2019.pdf` | **Yes** — confirmed non-commercial reuse with attribution | No action needed | diff --git a/ingestion/fetch-corpus.sh b/ingestion/fetch-corpus.sh new file mode 100755 index 0000000..e76b7e8 --- /dev/null +++ b/ingestion/fetch-corpus.sh @@ -0,0 +1,101 @@ +#!/usr/bin/env bash +# +# Re-download the humanitarian standards corpus into ingestion/corpus/. +# +# The PDFs are deliberately NOT committed (see .gitignore). The Sphere Handbook +# is all-rights-reserved — local use for research/training is permitted by its +# copyright page, redistribution is not. This script makes the corpus +# reproducible from the publisher-issued files without redistributing them. +# +# The canonical publisher domains (spherestandards.org, +# interagencystandingcommittee.org) sit behind bot-challenge WAFs that reject +# non-browser HTTP clients, so each entry below points at an official secondary +# mirror of the same publisher-issued PDF (UNHCR Emergency Handbook document +# library, or the CCCM Cluster's S3 resource library). Provenance, canonical +# pages and per-file license terms are recorded in corpus/SOURCES.md. +# +# Usage: +# ./fetch-corpus.sh # download anything missing, verify checksums +# ./fetch-corpus.sh --force # re-download everything +# ./fetch-corpus.sh --check # verify checksums only, download nothing + +set -euo pipefail + +CORPUS_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/corpus" +mkdir -p "$CORPUS_DIR" + +FORCE=0 +CHECK_ONLY=0 +for arg in "$@"; do + case "$arg" in + --force) FORCE=1 ;; + --check) CHECK_ONLY=1 ;; + -h|--help) sed -n '2,25p' "${BASH_SOURCE[0]}"; exit 0 ;; + *) echo "unknown argument: $arg" >&2; exit 2 ;; + esac +done + +# filename|sha256|url +# Checksums are of the files retrieved 2026-08-25; a mismatch means the mirror +# reissued the document (check SOURCES.md and re-verify the license) rather than +# that the download failed. +FILES=( + "Sphere-Handbook-2018-EN.pdf|eeb7e680c296e15389ca8a1e5882a3796f23cbba31cfb21dd4661332c4c8cf63|https://emergency.unhcr.org/sites/default/files/2024-01/Sphere-Handbook-2018-EN.pdf" + "CHS-2024.pdf|79419ef5dae4141bc4100245dfa5af9a3d6dcd1b7de9eb142e344b91694b012d|https://emergency.unhcr.org/sites/default/files/2024-01/4.%20The%20Core%20Humanitarian%20Standard%20on%20Quality%20and%20Accountability.pdf" + "IASC-Data-Responsibility-2023.pdf|662e73bbcf6cac6225cfdec464a477a2961af65edd80e27a64e53fb25d1ad781|https://emergency.unhcr.org/sites/default/files/2023-11/IASC%20Operational%20Guidance%20on%20Data%20Responsibility%20in%20Humanitarian%20Action%2C%202023.pdf" + "IASC-Protection-Policy-2016.pdf|2a36792d65db64ebc6de82063da51c8177ff24007eb88b22ffbbd208ce456a68|https://emergency.unhcr.org/sites/default/files/2024-01/4.%20IASC%20Policy%20on%20Protection%20in%20Humanitarian%20Action%2C%202016.pdf" + "IASC-Disability-Inclusion-2019.pdf|bb93dc370a688ca5c81c030674391687c6cf69fcc2a71253ed7301fb84ec3767|https://s3.eu-west-1.amazonaws.com/cccmcluster.org/public/2019-11/iasc_guidelines_on_the_inclusion_of_persons_with_disabilities_in_humanitarian_action_2019.pdf" +) + +sha256_of() { + if command -v shasum >/dev/null 2>&1; then + shasum -a 256 "$1" | cut -d' ' -f1 + else + sha256sum "$1" | cut -d' ' -f1 + fi +} + +failures=0 +for entry in "${FILES[@]}"; do + IFS='|' read -r name expected url <<< "$entry" + dest="$CORPUS_DIR/$name" + + if [[ $CHECK_ONLY -eq 0 ]] && { [[ $FORCE -eq 1 ]] || [[ ! -f "$dest" ]]; }; then + echo "downloading $name" + # -L follows the mirror's redirects; a browser-ish UA avoids the 403 some + # CDNs return to bare curl. + if ! curl -fsSL \ + -H 'User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0 Safari/537.36' \ + -o "$dest.part" "$url"; then + echo " FAILED: $name could not be downloaded from $url" >&2 + rm -f "$dest.part" + failures=$((failures + 1)) + continue + fi + mv "$dest.part" "$dest" + fi + + if [[ ! -f "$dest" ]]; then + echo " MISSING: $name" + failures=$((failures + 1)) + continue + fi + + actual="$(sha256_of "$dest")" + if [[ "$actual" == "$expected" ]]; then + echo " ok: $name" + else + echo " CHECKSUM MISMATCH: $name" >&2 + echo " expected $expected" >&2 + echo " actual $actual" >&2 + echo " the mirror may have reissued this document — re-check its license in corpus/SOURCES.md" >&2 + failures=$((failures + 1)) + fi +done + +if [[ $failures -gt 0 ]]; then + echo "$failures file(s) missing or failed verification" >&2 + exit 1 +fi + +echo "corpus ready in $CORPUS_DIR" From ad76d0845a709f435c185a83d2af79b21fe566ec Mon Sep 17 00:00:00 2001 From: Sam Frons Date: Tue, 25 Aug 2026 16:50:09 +0200 Subject: [PATCH 07/46] Add standards_chunks schema and hybrid search function pgvector installed into `extensions` (relocated if a prior install put it in public, since the type and opclass are schema-qualified). standards_chunks holds one row per document chunk with a 1024-dim voyage-3.5 embedding, a generated tsvector over content + context_summary, an HNSW cosine index and a GIN index. search_standards_hybrid fuses the two rankings with reciprocal rank fusion (k=60) rather than a weighted score sum, because cosine distance and ts_rank_cd are not on comparable scales and any weighting would need retuning whenever the embedding model changes. Both legs are bounded to 4x match_count and match_count is clamped to 50 so a caller cannot trigger an unbounded scan. RLS on with a read-only anon/authenticated policy; writes go through the service role, which bypasses RLS. Verified against pgvector/pgvector:pg16: migration applies clean, RRF ranks correctly with both legs and with either leg missing, and anon INSERT/DELETE are rejected. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01HCrPUhxWVtRBsYSSc7sBHU --- supabase/.gitignore | 8 + supabase/config.toml | 413 ++++++++++++++++++ .../20260825120000_standards_chunks.sql | 198 +++++++++ 3 files changed, 619 insertions(+) create mode 100644 supabase/.gitignore create mode 100644 supabase/config.toml create mode 100644 supabase/migrations/20260825120000_standards_chunks.sql diff --git a/supabase/.gitignore b/supabase/.gitignore new file mode 100644 index 0000000..ad9264f --- /dev/null +++ b/supabase/.gitignore @@ -0,0 +1,8 @@ +# Supabase +.branches +.temp + +# dotenvx +.env.keys +.env.local +.env.*.local diff --git a/supabase/config.toml b/supabase/config.toml new file mode 100644 index 0000000..1d75316 --- /dev/null +++ b/supabase/config.toml @@ -0,0 +1,413 @@ +# For detailed configuration reference documentation, visit: +# https://supabase.com/docs/guides/local-development/cli/config +# A string used to distinguish different Supabase projects on the same host. Defaults to the +# working directory name when running `supabase init`. +project_id = "hai" + +[api] +enabled = true +# Port to use for the API URL. +port = 54321 +# Schemas to expose in your API. Tables, views and stored procedures in this schema will get API +# endpoints. `public` and `graphql_public` schemas are included by default. +schemas = ["public", "graphql_public"] +# Extra schemas to add to the search_path of every request. +extra_search_path = ["public", "extensions"] +# The maximum number of rows returns from a view, table, or stored procedure. Limits payload size +# for accidental or malicious requests. +max_rows = 1000 +# Controls whether new tables, views, sequences and functions created in the `public` schema by +# `postgres` are reachable through the Data API roles (`anon`, `authenticated`, `service_role`) +# without explicit GRANTs. When unset, new entities are NOT auto-exposed, matching the new cloud +# default. Set to `true` to keep the legacy behaviour of auto-exposing new entities; this is +# deprecated and the field is removed on 2026-10-30 once the always-revoked behaviour is permanent. +# auto_expose_new_tables = true + +[api.tls] +# Enable HTTPS endpoints locally using a self-signed certificate. +enabled = false +# Paths to self-signed certificate pair. +# cert_path = "../certs/my-cert.pem" +# key_path = "../certs/my-key.pem" + +[db] +# Port to use for the local database URL. +port = 54322 +# Port used by db diff command to initialize the shadow database. +shadow_port = 54320 +# Maximum amount of time to wait for health check when starting the local database. +health_timeout = "2m" +# The database major version to use. This has to be the same as your remote database's. Run `SHOW +# server_version;` on the remote database to check. +major_version = 17 + +[db.pooler] +enabled = false +# Port to use for the local connection pooler. +port = 54329 +# Specifies when a server connection can be reused by other clients. +# Configure one of the supported pooler modes: `transaction`, `session`. +pool_mode = "transaction" +# How many server connections to allow per user/database pair. +default_pool_size = 20 +# Maximum number of client connections allowed. +max_client_conn = 100 + +# [db.vault] +# secret_key = "env(SECRET_VALUE)" + +[db.migrations] +# If disabled, migrations will be skipped during a db push or reset. +enabled = true +# Specifies an ordered list of schema files that describe your database. +# Supports glob patterns relative to supabase directory: "./schemas/*.sql" +schema_paths = [] + +[db.seed] +# If enabled, seeds the database after migrations during a db reset. +enabled = true +# Specifies an ordered list of seed files to load during db reset. +# Supports glob patterns relative to supabase directory: "./seeds/*.sql" +sql_paths = ["./seed.sql"] + +[db.network_restrictions] +# Enable management of network restrictions. +enabled = false +# List of IPv4 CIDR blocks allowed to connect to the database. +# Defaults to allow all IPv4 connections. Set empty array to block all IPs. +allowed_cidrs = ["0.0.0.0/0"] +# List of IPv6 CIDR blocks allowed to connect to the database. +# Defaults to allow all IPv6 connections. Set empty array to block all IPs. +allowed_cidrs_v6 = ["::/0"] + +# Uncomment to reject non-secure connections to the database. +# [db.ssl_enforcement] +# enabled = true + +[realtime] +enabled = true +# Bind realtime via either IPv4 or IPv6. (default: IPv4) +# ip_version = "IPv6" +# The maximum length in bytes of HTTP request headers. (default: 4096) +# max_header_length = 4096 + +[studio] +enabled = true +# Port to use for Supabase Studio. +port = 54323 +# External URL of the API server that frontend connects to. +api_url = "http://127.0.0.1" +# OpenAI API Key to use for Supabase AI in the Supabase Studio. +openai_api_key = "env(OPENAI_API_KEY)" + +# Email testing server. Emails sent with the local dev setup are not actually sent - rather, they +# are monitored, and you can view the emails that would have been sent from the web interface. +[inbucket] +enabled = true +# Port to use for the email testing server web interface. +port = 54324 +# Uncomment to expose additional ports for testing user applications that send emails. +# smtp_port = 54325 +# pop3_port = 54326 +# admin_email = "admin@email.com" +# sender_name = "Admin" + +[storage] +enabled = true +# The maximum file size allowed (e.g. "5MB", "500KB"). +file_size_limit = "50MiB" + +# Uncomment to configure local storage buckets +# [storage.buckets.images] +# public = false +# file_size_limit = "50MiB" +# allowed_mime_types = ["image/png", "image/jpeg"] +# objects_path = "./images" + +# Allow connections via S3 compatible clients +[storage.s3_protocol] +enabled = true + +# Image transformation API is available to Supabase Pro plan. +# [storage.image_transformation] +# enabled = true + +# Store analytical data in S3 for running ETL jobs over Iceberg Catalog +# This feature is only available on the hosted platform. +[storage.analytics] +enabled = false +max_namespaces = 5 +max_tables = 10 +max_catalogs = 2 + +# Analytics Buckets is available to Supabase Pro plan. +# [storage.analytics.buckets.my-warehouse] + +# Store vector embeddings in S3 for large and durable datasets +[storage.vector] +enabled = true +max_buckets = 10 +max_indexes = 5 + +# Vector Buckets is available to Supabase Pro plan. +# [storage.vector.buckets.documents-openai] + +[auth] +enabled = true +# The base URL of your website. Used as an allow-list for redirects and for constructing URLs used +# in emails. +site_url = "http://127.0.0.1:3000" +# The public URL that Auth serves on. Defaults to the API external URL with `/auth/v1` appended. +# external_url = "" +# A list of *exact* URLs that auth providers are permitted to redirect to post authentication. +additional_redirect_urls = ["https://127.0.0.1:3000"] +# How long tokens are valid for, in seconds. Defaults to 3600 (1 hour), maximum 604,800 (1 week). +jwt_expiry = 3600 +# JWT issuer URL. If not set, defaults to auth.external_url. +# jwt_issuer = "" +# Path to JWT signing key. DO NOT commit your signing keys file to git. +# signing_keys_path = "./signing_keys.json" +# If disabled, the refresh token will never expire. +enable_refresh_token_rotation = true +# Allows refresh tokens to be reused after expiry, up to the specified interval in seconds. +# Requires enable_refresh_token_rotation = true. +refresh_token_reuse_interval = 10 +# Allow/disallow new user signups to your project. +enable_signup = true +# Allow/disallow anonymous sign-ins to your project. +enable_anonymous_sign_ins = false +# Allow/disallow testing manual linking of accounts +enable_manual_linking = false +# Passwords shorter than this value will be rejected as weak. Minimum 6, recommended 8 or more. +minimum_password_length = 6 +# Passwords that do not meet the following requirements will be rejected as weak. Supported values +# are: `letters_digits`, `lower_upper_letters_digits`, `lower_upper_letters_digits_symbols` +password_requirements = "" + +# Configure passkey sign-ins. +# [auth.passkey] +# enabled = false + +# Configure WebAuthn relying party settings (required when passkey is enabled). +# [auth.webauthn] +# rp_display_name = "Supabase" +# rp_id = "localhost" +# rp_origins = ["http://127.0.0.1:3000"] + +[auth.rate_limit] +# Number of emails that can be sent per hour. Requires auth.email.smtp to be enabled. +email_sent = 2 +# Number of SMS messages that can be sent per hour. Requires auth.sms to be enabled. +sms_sent = 30 +# Number of anonymous sign-ins that can be made per hour per IP address. Requires enable_anonymous_sign_ins = true. +anonymous_users = 30 +# Number of sessions that can be refreshed in a 5 minute interval per IP address. +token_refresh = 150 +# Number of sign up and sign-in requests that can be made in a 5 minute interval per IP address (excludes anonymous users). +sign_in_sign_ups = 30 +# Number of OTP / Magic link verifications that can be made in a 5 minute interval per IP address. +token_verifications = 30 +# Number of Web3 logins that can be made in a 5 minute interval per IP address. +web3 = 30 + +# Configure one of the supported captcha providers: `hcaptcha`, `turnstile`. +# [auth.captcha] +# enabled = true +# provider = "hcaptcha" +# secret = "" + +[auth.email] +# Allow/disallow new user signups via email to your project. +enable_signup = true +# If enabled, a user will be required to confirm any email change on both the old, and new email +# addresses. If disabled, only the new email is required to confirm. +double_confirm_changes = true +# If enabled, users need to confirm their email address before signing in. +enable_confirmations = false +# If enabled, users will need to reauthenticate or have logged in recently to change their password. +secure_password_change = false +# Controls the minimum amount of time that must pass before sending another signup confirmation or password reset email. +max_frequency = "1s" +# Number of characters used in the email OTP. +otp_length = 6 +# Number of seconds before the email OTP expires (defaults to 1 hour). +otp_expiry = 3600 + +# Use a production-ready SMTP server +# [auth.email.smtp] +# enabled = true +# host = "smtp.sendgrid.net" +# port = 587 +# user = "apikey" +# pass = "env(SENDGRID_API_KEY)" +# admin_email = "admin@email.com" +# sender_name = "Admin" + +# Uncomment to customize email template +# [auth.email.template.invite] +# subject = "You have been invited" +# content_path = "./supabase/templates/invite.html" + +# Uncomment to customize notification email template +# [auth.email.notification.password_changed] +# enabled = true +# subject = "Your password has been changed" +# content_path = "./templates/password_changed_notification.html" + +[auth.sms] +# Allow/disallow new user signups via SMS to your project. +enable_signup = false +# If enabled, users need to confirm their phone number before signing in. +enable_confirmations = false +# Template for sending OTP to users +template = "Your code is {{ `{{ .Code }}` }}" +# Controls the minimum amount of time that must pass before sending another sms otp. +max_frequency = "5s" + +# Use pre-defined map of phone number to OTP for testing. +# [auth.sms.test_otp] +# 4152127777 = "123456" + +# Configure logged in session timeouts. +# [auth.sessions] +# Force log out after the specified duration. +# timebox = "24h" +# Force log out if the user has been inactive longer than the specified duration. +# inactivity_timeout = "8h" + +# This hook runs before a new user is created and allows developers to reject the request based on the incoming user object. +# [auth.hook.before_user_created] +# enabled = true +# uri = "pg-functions://postgres/auth/before-user-created-hook" + +# This hook runs before a token is issued and allows you to add additional claims based on the authentication method used. +# [auth.hook.custom_access_token] +# enabled = true +# uri = "pg-functions:////" + +# Configure one of the supported SMS providers: `twilio`, `twilio_verify`, `messagebird`, `textlocal`, `vonage`. +[auth.sms.twilio] +enabled = false +account_sid = "" +message_service_sid = "" +# DO NOT commit your Twilio auth token to git. Use environment variable substitution instead: +auth_token = "env(SUPABASE_AUTH_SMS_TWILIO_AUTH_TOKEN)" + +# Multi-factor-authentication is available to Supabase Pro plan. +[auth.mfa] +# Control how many MFA factors can be enrolled at once per user. +max_enrolled_factors = 10 + +# Control MFA via App Authenticator (TOTP) +[auth.mfa.totp] +enroll_enabled = false +verify_enabled = false + +# Configure MFA via Phone Messaging +[auth.mfa.phone] +enroll_enabled = false +verify_enabled = false +otp_length = 6 +template = "Your code is {{ `{{ .Code }}` }}" +max_frequency = "5s" + +# Configure MFA via WebAuthn +# [auth.mfa.web_authn] +# enroll_enabled = true +# verify_enabled = true + +# Use an external OAuth provider. The full list of providers are: `apple`, `azure`, `bitbucket`, +# `discord`, `facebook`, `github`, `gitlab`, `google`, `keycloak`, `linkedin_oidc`, `notion`, `twitch`, +# `twitter`, `x`, `slack`, `spotify`, `workos`, `zoom`. +[auth.external.apple] +enabled = false +client_id = "" +# DO NOT commit your OAuth provider secret to git. Use environment variable substitution instead: +secret = "env(SUPABASE_AUTH_EXTERNAL_APPLE_SECRET)" +# Overrides the default auth callback URL derived from auth.external_url. +redirect_uri = "" +# Overrides the default auth provider URL. Used to support self-hosted gitlab, single-tenant Azure, +# or any other third-party OIDC providers. +url = "" +# If enabled, the nonce check will be skipped. Required for local sign in with Google auth. +skip_nonce_check = false +# If enabled, it will allow the user to successfully authenticate when the provider does not return an email address. +email_optional = false + +# Allow Solana wallet holders to sign in to your project via the Sign in with Solana (SIWS, EIP-4361) standard. +# You can configure "web3" rate limit in the [auth.rate_limit] section and set up [auth.captcha] if self-hosting. +[auth.web3.solana] +enabled = false + +# Use Firebase Auth as a third-party provider alongside Supabase Auth. +[auth.third_party.firebase] +enabled = false +# project_id = "my-firebase-project" + +# Use Auth0 as a third-party provider alongside Supabase Auth. +[auth.third_party.auth0] +enabled = false +# tenant = "my-auth0-tenant" +# tenant_region = "us" + +# Use AWS Cognito (Amplify) as a third-party provider alongside Supabase Auth. +[auth.third_party.aws_cognito] +enabled = false +# user_pool_id = "my-user-pool-id" +# user_pool_region = "us-east-1" + +# Use Clerk as a third-party provider alongside Supabase Auth. +[auth.third_party.clerk] +enabled = false +# Obtain from https://clerk.com/setup/supabase +# domain = "example.clerk.accounts.dev" + +# OAuth server configuration +[auth.oauth_server] +# Enable OAuth server functionality +enabled = false +# Path for OAuth consent flow UI +authorization_url_path = "/oauth/consent" +# Allow dynamic client registration +allow_dynamic_registration = false + +[edge_runtime] +enabled = true +# Supported request policies: `oneshot`, `per_worker`. +# `per_worker` (default) — enables hot reload during local development. +# `oneshot` — fallback mode if hot reload causes issues (e.g. in large repos or with symlinks). +policy = "per_worker" +# Port to attach the Chrome inspector for debugging edge functions. +inspector_port = 8083 +# The Deno major version to use. +deno_version = 2 + +# [edge_runtime.secrets] +# secret_key = "env(SECRET_VALUE)" + +[analytics] +enabled = true +port = 54327 +# Configure one of the supported backends: `postgres`, `bigquery`. +backend = "postgres" + +# Experimental features may be deprecated any time +[experimental] +# Configures Postgres storage engine to use OrioleDB (S3) +orioledb_version = "" +# Configures S3 bucket URL, eg. .s3-.amazonaws.com +s3_host = "env(S3_HOST)" +# Configures S3 bucket region, eg. us-east-1 +s3_region = "env(S3_REGION)" +# Configures AWS_ACCESS_KEY_ID for S3 bucket +s3_access_key = "env(S3_ACCESS_KEY)" +# Configures AWS_SECRET_ACCESS_KEY for S3 bucket +s3_secret_key = "env(S3_SECRET_KEY)" + +# [experimental.pgdelta] +# When enabled, pg-delta becomes the active engine for supported schema flows. +# enabled = false +# Directory under `supabase/` where declarative files are written. +# declarative_schema_path = "./database" +# JSON string passed through to pg-delta SQL formatting. +# format_options = "{\"keywordCase\":\"upper\",\"indent\":2,\"maxWidth\":80,\"commaStyle\":\"trailing\"}" diff --git a/supabase/migrations/20260825120000_standards_chunks.sql b/supabase/migrations/20260825120000_standards_chunks.sql new file mode 100644 index 0000000..0a1803e --- /dev/null +++ b/supabase/migrations/20260825120000_standards_chunks.sql @@ -0,0 +1,198 @@ +-- Corpus of humanitarian standards, chunked for hybrid retrieval. +-- +-- One row per chunk of a source document (Sphere Handbook, CHS, IASC guidance). +-- Each chunk carries both a Voyage AI embedding (semantic recall) and a tsvector +-- (lexical recall for the numbers and defined terms these standards turn on -- +-- "15 litres", "Protection Principle 2" -- where embeddings alone are weak). +-- search_standards_hybrid() fuses the two rankings. + +-- pgvector lives in `extensions`, not `public`, so the Data API never exposes +-- it. `if not exists` alone would silently leave a pre-existing extension in +-- whatever schema it was installed into, which would break the qualified +-- `extensions.vector` references below, so relocate it when that happens. +create schema if not exists extensions; + +do $$ +declare + ext_schema name; +begin + select n.nspname into ext_schema + from pg_extension e + join pg_namespace n on n.oid = e.extnamespace + where e.extname = 'vector'; + + if ext_schema is null then + create extension vector with schema extensions; + elsif ext_schema <> 'extensions' then + alter extension vector set schema extensions; + end if; +end +$$; + +create table public.standards_chunks ( + -- Deterministic UUIDv5 derived by the ingestion pipeline from + -- (source, section_path, page range, content), so re-ingesting an unchanged + -- document upserts onto the same rows instead of duplicating the corpus. + id uuid primary key, + source text not null, + doc_title text not null, + section_path text not null default '', + page_start integer, + page_end integer, + content text not null, + -- One or two sentences from claude-haiku-4-5 situating the chunk in its + -- document (Anthropic's contextual retrieval). Empty when the pipeline ran + -- without ANTHROPIC_API_KEY. + context_summary text not null default '', + -- voyage-3.5, 1024 dimensions. Nullable so extract/chunk/load can run before + -- embeddings exist. + embedding extensions.vector(1024), + fts tsvector generated always as ( + to_tsvector('english', content || ' ' || context_summary) + ) stored, + created_at timestamptz not null default now(), + + constraint standards_chunks_source_check check ( + source in ( + 'sphere', + 'chs', + 'iasc_data_responsibility', + 'iasc_protection', + 'iasc_disability' + ) + ), + constraint standards_chunks_content_not_blank check (length(btrim(content)) > 0), + constraint standards_chunks_pages_ordered check ( + page_start is null or page_end is null or page_end >= page_start + ) +); + +comment on table public.standards_chunks is + 'Chunks of humanitarian standards documents with Voyage embeddings and full-text vectors for hybrid retrieval. Source PDFs are not redistributable; see ingestion/corpus/SOURCES.md.'; +comment on column public.standards_chunks.section_path is + 'Heading breadcrumb, e.g. "WASH > Water supply > Standard 2.1". Used for citation display, not for filtering.'; +comment on column public.standards_chunks.context_summary is + 'Contextual-retrieval preamble; empty string when contextualization was skipped.'; + +-- Approximate nearest neighbour over cosine distance. HNSW builds slower than +-- IVFFlat but needs no training pass and stays accurate as the corpus grows, +-- which matters here because sources are ingested one document at a time. +create index standards_chunks_embedding_idx + on public.standards_chunks + using hnsw (embedding extensions.vector_cosine_ops); + +create index standards_chunks_fts_idx + on public.standards_chunks + using gin (fts); + +-- search_standards_hybrid() filters by source inside both legs of the fusion. +create index standards_chunks_source_idx + on public.standards_chunks (source); + +alter table public.standards_chunks enable row level security; + +-- The corpus is public reference material: everyone reads, nobody writes +-- through the Data API. Ingestion connects with the service role, which bypasses +-- RLS entirely. +revoke all on public.standards_chunks from anon, authenticated; +grant select on public.standards_chunks to anon, authenticated; + +create policy standards_chunks_public_read + on public.standards_chunks + for select + to anon, authenticated + using (true); + +-- Reciprocal rank fusion over a vector-similarity ranking and a full-text +-- ranking. RRF is used rather than a weighted sum of raw scores because cosine +-- distance and ts_rank_cd are not on comparable scales, so any weighting would +-- have to be retuned whenever the embedding model changes. +-- +-- security invoker (the default) so the caller's RLS applies; search_path is +-- pinned empty and every non-pg_catalog name is schema-qualified. +create or replace function public.search_standards_hybrid( + query_text text, + query_embedding extensions.vector(1024), + match_count integer default 8, + filter_source text default null +) +returns table ( + id uuid, + source text, + doc_title text, + section_path text, + page_start integer, + page_end integer, + content text, + context_summary text, + score double precision, + semantic_rank integer, + fulltext_rank integer +) +language sql +stable +set search_path = '' +as $$ +with + bounded as ( + select + -- Clamp so a caller cannot ask for an unbounded scan. + least(greatest(coalesce(match_count, 8), 1), 50) as n, + websearch_to_tsquery('english', coalesce(query_text, '')) as tsq + ), + full_text as ( + select + c.id, + row_number() over ( + order by ts_rank_cd(c.fts, b.tsq) desc, c.id + )::integer as rank_ix + from public.standards_chunks c + cross join bounded b + where b.tsq is not null + and c.fts @@ b.tsq + and (filter_source is null or c.source = filter_source) + order by rank_ix + limit (select n * 4 from bounded) + ), + semantic as ( + select + c.id, + row_number() over ( + order by c.embedding OPERATOR(extensions.<=>) query_embedding, c.id + )::integer as rank_ix + from public.standards_chunks c + where c.embedding is not null + and query_embedding is not null + and (filter_source is null or c.source = filter_source) + order by rank_ix + limit (select n * 4 from bounded) + ) +select + c.id, + c.source, + c.doc_title, + c.section_path, + c.page_start, + c.page_end, + c.content, + c.context_summary, + -- k = 60, the smoothing constant from Cormack et al.'s RRF paper; it keeps any + -- single top-1 hit from dominating a document ranked well by both legs. + ( + coalesce(1.0 / (60 + s.rank_ix), 0.0) + + coalesce(1.0 / (60 + f.rank_ix), 0.0) + )::double precision as score, + s.rank_ix as semantic_rank, + f.rank_ix as fulltext_rank +from semantic s +full outer join full_text f on f.id = s.id +join public.standards_chunks c on c.id = coalesce(s.id, f.id) +order by score desc, c.id +limit (select n from bounded); +$$; + +comment on function public.search_standards_hybrid is + 'Hybrid retrieval over standards_chunks: reciprocal rank fusion (k=60) of cosine-similarity and full-text rankings. Pass a voyage-3.5 (1024-dim) embedding of the same query_text. filter_source restricts to one source key.'; + +grant execute on function public.search_standards_hybrid(text, extensions.vector(1024), integer, text) + to anon, authenticated, service_role; From 79e3009d212a5004d9b1e05ef90fe82bbc564f07 Mon Sep 17 00:00:00 2001 From: Sam Frons Date: Tue, 25 Aug 2026 16:50:44 +0200 Subject: [PATCH 08/46] Add HAI chat backend: system policy, tools, and streaming chat route Shared system prompt module is the single source of truth for HAI's grounding, data-responsibility, and conflict-sensitivity policy. Tools: search_standards over the standards corpus (retrieval stubbed behind a stable interface pending ingestion), crisis_updates against ReliefWeb, humanitarian_data against HDX HAPI. Both live tools cache for 60s and degrade to a structured error the model can act on. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01HCrPUhxWVtRBsYSSc7sBHU --- app/package.json | 2 + app/pnpm-lock.yaml | 868 +++++++++++++++++++++++++ app/src/app/api/chat/route.ts | 106 +++ app/src/lib/prompts/system.ts | 49 ++ app/src/lib/retrieval/search.ts | 70 ++ app/src/lib/tools/crisis-updates.ts | 280 ++++++++ app/src/lib/tools/humanitarian-data.ts | 284 ++++++++ app/src/lib/tools/index.ts | 16 + app/src/lib/tools/search-standards.ts | 35 + 9 files changed, 1710 insertions(+) create mode 100644 app/src/app/api/chat/route.ts create mode 100644 app/src/lib/prompts/system.ts create mode 100644 app/src/lib/retrieval/search.ts create mode 100644 app/src/lib/tools/crisis-updates.ts create mode 100644 app/src/lib/tools/humanitarian-data.ts create mode 100644 app/src/lib/tools/index.ts create mode 100644 app/src/lib/tools/search-standards.ts diff --git a/app/package.json b/app/package.json index 3c84ee1..e985fe8 100644 --- a/app/package.json +++ b/app/package.json @@ -15,6 +15,8 @@ "next": "16.3.2", "react": "19.2.8", "react-dom": "19.2.8", + "react-markdown": "^10.1.0", + "remark-gfm": "^4.0.1", "zod": "^4.4.3" }, "devDependencies": { diff --git a/app/pnpm-lock.yaml b/app/pnpm-lock.yaml index 9a4f626..f783817 100644 --- a/app/pnpm-lock.yaml +++ b/app/pnpm-lock.yaml @@ -26,6 +26,12 @@ importers: react-dom: specifier: 19.2.8 version: 19.2.8(react@19.2.8) + react-markdown: + specifier: ^10.1.0 + version: 10.1.0(@types/react@19.2.18)(react@19.2.8) + remark-gfm: + specifier: ^4.0.1 + version: 4.0.1 zod: specifier: ^4.4.3 version: 4.4.3 @@ -577,15 +583,30 @@ packages: '@tybys/wasm-util@0.10.3': resolution: {integrity: sha512-F3fo1MYrRJYL3zER0OUOmkutjr1Vp23m7OsSgp7nq4SP6OqX6C/56XFIPAl5bt3zaBRjmW7SGz3u/6LwFpYcOg==} + '@types/debug@4.1.13': + resolution: {integrity: sha512-KSVgmQmzMwPlmtljOomayoR89W4FynCAi3E8PPs7vmDVPe84hT+vGPKkJfThkmXs0x0jAaa9U8uW8bbfyS2fWw==} + + '@types/estree-jsx@1.0.5': + resolution: {integrity: sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg==} + '@types/estree@1.0.9': resolution: {integrity: sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==} + '@types/hast@3.0.5': + resolution: {integrity: sha512-rp/ezSWaD1m44dPKICGhiskI13nVr7qTloFwDa/IYkhhf5nzwP+zIQcIJh3WIFSBOy/H1PzB40jPjMDksN4F+g==} + '@types/json-schema@7.0.15': resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} '@types/json5@0.0.29': resolution: {integrity: sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==} + '@types/mdast@4.0.4': + resolution: {integrity: sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==} + + '@types/ms@2.1.0': + resolution: {integrity: sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==} + '@types/node@20.19.43': resolution: {integrity: sha512-6oYBAi5ikg4Pl+kGsoYtawUMBT2zZMCvPNF7pVLnHZfd1zf38DRiWn/gT01RYCdUqkv7Fhr+C9ot4/tb+2sVvA==} @@ -597,6 +618,12 @@ packages: '@types/react@19.2.18': resolution: {integrity: sha512-AnzbBERsrLKtk2XSfTbYRLjQPdy116Sty4q+T+Bp3IC4l6jNBvreVPAHmpq9qhXQM7CXZPjLVmGMw9sy+hxQ3w==} + '@types/unist@2.0.11': + resolution: {integrity: sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==} + + '@types/unist@3.0.3': + resolution: {integrity: sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==} + '@typescript-eslint/eslint-plugin@8.68.0': resolution: {integrity: sha512-WASHDpCm6qO5jj9g1a+8NiW5+GCkAyLReR56/4VruYmNgfUmqpxOfZ2Yfb8xGfJPWv5Qi6LSD8sXdces3vbp/Q==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -656,6 +683,9 @@ packages: resolution: {integrity: sha512-YR65gGdGvTUAWLldC3xLOvOzamdGzB4A5/N8rehEaHs3Zvoe39BhgY+u0SPch1OvrVTfLcc55wsSgK2NcnTS/A==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@ungap/structured-clone@1.3.3': + resolution: {integrity: sha512-60YRaenCQcVjYEKOcG824+DRGGIQ3VKErcBoAEDJZz5bKIs2ZG+X/H9Nk+Q6EVkwJk5QNApxbrc5QtBSwtrXAg==} + '@unrs/resolver-binding-android-arm-eabi@1.12.2': resolution: {integrity: sha512-g5T90pqg1bo/7mytQx6F4iBNC0Wsh9cu+z9veDbFjc7HjpesJFWD7QMS0NGStXM075+7dJPPVvBbpZlnrdpi/w==} cpu: [arm] @@ -854,6 +884,9 @@ packages: resolution: {integrity: sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==} engines: {node: '>= 0.4'} + bail@2.0.2: + resolution: {integrity: sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==} + balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} @@ -901,10 +934,25 @@ packages: caniuse-lite@1.0.30001810: resolution: {integrity: sha512-TITQPUkaz+aVk5GL6NhOdwk1aEaNTSDPsGFWrTuhKGtjTF70jL/Oht2W4c6rXUe5fu7Ie19VIahAXHIIiWWNeg==} + ccount@2.0.1: + resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==} + chalk@4.1.2: resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} engines: {node: '>=10'} + character-entities-html4@2.1.0: + resolution: {integrity: sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==} + + character-entities-legacy@3.0.0: + resolution: {integrity: sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==} + + character-entities@2.0.2: + resolution: {integrity: sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==} + + character-reference-invalid@2.0.1: + resolution: {integrity: sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==} + client-only@0.0.1: resolution: {integrity: sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==} @@ -915,6 +963,9 @@ packages: color-name@1.1.4: resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + comma-separated-tokens@2.0.3: + resolution: {integrity: sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==} + concat-map@0.0.1: resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} @@ -960,6 +1011,9 @@ packages: supports-color: optional: true + decode-named-character-reference@1.3.0: + resolution: {integrity: sha512-GtpQYB283KrPp6nRw50q3U9/VfOutZOe103qlN7BPP6Ad27xYnOIWv4lPzo8HCAL+mMZofJ9KEy30fq6MfaK6Q==} + deep-is@0.1.4: resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} @@ -979,6 +1033,9 @@ packages: resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} engines: {node: '>=8'} + devlop@1.1.0: + resolution: {integrity: sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==} + doctrine@2.1.0: resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==} engines: {node: '>=0.10.0'} @@ -1041,6 +1098,10 @@ packages: resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} engines: {node: '>=10'} + escape-string-regexp@5.0.0: + resolution: {integrity: sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==} + engines: {node: '>=12'} + eslint-config-next@16.3.2: resolution: {integrity: sha512-gTABOJmyc6pEgSX1Z1VOjBxkSmo5Hkdj+ePclDf8HLGTsnVWzgtDdrOeQrAnUkf0JNJJhwPuXrsIwmFZRKJLoQ==} peerDependencies: @@ -1158,6 +1219,9 @@ packages: resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} engines: {node: '>=4.0'} + estree-util-is-identifier-name@3.0.0: + resolution: {integrity: sha512-hFtqIDZTIUZ9BXLb8y4pYGyk6+wekIivNVTcmvk8NoOh+VeRn5y6cEHzbURrWbfp1fIqdVipilzj+lfaadNZmg==} + esutils@2.0.3: resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} engines: {node: '>=0.10.0'} @@ -1166,6 +1230,9 @@ packages: resolution: {integrity: sha512-EKN1vKAMcZ8MlYMpaNuxN6R9yakzH6uajHcHVTqWJzvu5pWw9DyhbP35HH8MVBQ+dZjAfDxk+A8NiR9KWaXiyQ==} engines: {node: '>=18.0.0'} + extend@3.0.2: + resolution: {integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==} + fast-deep-equal@3.1.3: resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} @@ -1301,12 +1368,21 @@ packages: resolution: {integrity: sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==} engines: {node: '>= 0.4'} + hast-util-to-jsx-runtime@2.3.6: + resolution: {integrity: sha512-zl6s8LwNyo1P9uw+XJGvZtdFF1GdAkOg8ujOw+4Pyb76874fLps4ueHXDhXWdk6YHQ6OgUtinliG7RsYvCbbBg==} + + hast-util-whitespace@3.0.0: + resolution: {integrity: sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==} + hermes-estree@0.25.1: resolution: {integrity: sha512-0wUoCcLp+5Ev5pDW2OriHC2MJCbwLwuRx+gAqMTOkGKJJiBCLjtrvy4PWUGn6MIVefecRpzoOZ/UV6iGdOr+Cw==} hermes-parser@0.25.1: resolution: {integrity: sha512-6pEjquH3rqaI6cYAXYPcz9MS4rY6R4ngRgrgfDshRptUZIc3lw0MCIJIGDj9++mfySOuPTHB4nrSW99BCvOPIA==} + html-url-attributes@3.0.1: + resolution: {integrity: sha512-ol6UPyBWqsrO6EJySPz2O7ZSr856WDrEzM5zMqp+FJJLGMW35cLYmmZnl0vztAZxRUoNZJFTCohfjuIJ8I4QBQ==} + ignore@5.3.2: resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} engines: {node: '>= 4'} @@ -1323,10 +1399,19 @@ packages: resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} engines: {node: '>=0.8.19'} + inline-style-parser@0.2.7: + resolution: {integrity: sha512-Nb2ctOyNR8DqQoR0OwRG95uNWIC0C1lCgf5Naz5H6Ji72KZ8OcFZLz2P5sNgwlyoJ8Yif11oMuYs5pBQa86csA==} + internal-slot@1.1.0: resolution: {integrity: sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==} engines: {node: '>= 0.4'} + is-alphabetical@2.0.1: + resolution: {integrity: sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ==} + + is-alphanumerical@2.0.1: + resolution: {integrity: sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw==} + is-array-buffer@3.0.5: resolution: {integrity: sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==} engines: {node: '>= 0.4'} @@ -1362,6 +1447,9 @@ packages: resolution: {integrity: sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==} engines: {node: '>= 0.4'} + is-decimal@2.0.1: + resolution: {integrity: sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A==} + is-document.all@1.0.0: resolution: {integrity: sha512-+XSoyS05OdBbhFuELhgTCpFNHkpBOJqtsZfUFFpe5QTw+9Sjbh8zitxhQkYAo6wV7e1Vb8cAPvpCk9jGam/82g==} engines: {node: '>= 0.4'} @@ -1382,6 +1470,9 @@ packages: resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} engines: {node: '>=0.10.0'} + is-hexadecimal@2.0.1: + resolution: {integrity: sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg==} + is-map@2.0.3: resolution: {integrity: sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==} engines: {node: '>= 0.4'} @@ -1398,6 +1489,10 @@ packages: resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} engines: {node: '>=0.12.0'} + is-plain-obj@4.1.0: + resolution: {integrity: sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==} + engines: {node: '>=12'} + is-regex@1.2.1: resolution: {integrity: sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==} engines: {node: '>= 0.4'} @@ -1576,6 +1671,9 @@ packages: lodash.merge@4.6.2: resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} + longest-streak@3.1.0: + resolution: {integrity: sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==} + loose-envify@1.4.0: resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} hasBin: true @@ -1586,14 +1684,146 @@ packages: magic-string@0.30.21: resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} + markdown-table@3.0.4: + resolution: {integrity: sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw==} + math-intrinsics@1.1.0: resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} engines: {node: '>= 0.4'} + mdast-util-find-and-replace@3.0.2: + resolution: {integrity: sha512-Tmd1Vg/m3Xz43afeNxDIhWRtFZgM2VLyaf4vSTYwudTyeuTneoL3qtWMA5jeLyz/O1vDJmmV4QuScFCA2tBPwg==} + + mdast-util-from-markdown@2.0.3: + resolution: {integrity: sha512-W4mAWTvSlKvf8L6J+VN9yLSqQ9AOAAvHuoDAmPkz4dHf553m5gVj2ejadHJhoJmcmxEnOv6Pa8XJhpxE93kb8Q==} + + mdast-util-gfm-autolink-literal@2.0.1: + resolution: {integrity: sha512-5HVP2MKaP6L+G6YaxPNjuL0BPrq9orG3TsrZ9YXbA3vDw/ACI4MEsnoDpn6ZNm7GnZgtAcONJyPhOP8tNJQavQ==} + + mdast-util-gfm-footnote@2.1.0: + resolution: {integrity: sha512-sqpDWlsHn7Ac9GNZQMeUzPQSMzR6Wv0WKRNvQRg0KqHh02fpTz69Qc1QSseNX29bhz1ROIyNyxExfawVKTm1GQ==} + + mdast-util-gfm-strikethrough@2.0.0: + resolution: {integrity: sha512-mKKb915TF+OC5ptj5bJ7WFRPdYtuHv0yTRxK2tJvi+BDqbkiG7h7u/9SI89nRAYcmap2xHQL9D+QG/6wSrTtXg==} + + mdast-util-gfm-table@2.0.0: + resolution: {integrity: sha512-78UEvebzz/rJIxLvE7ZtDd/vIQ0RHv+3Mh5DR96p7cS7HsBhYIICDBCu8csTNWNO6tBWfqXPWekRuj2FNOGOZg==} + + mdast-util-gfm-task-list-item@2.0.0: + resolution: {integrity: sha512-IrtvNvjxC1o06taBAVJznEnkiHxLFTzgonUdy8hzFVeDun0uTjxxrRGVaNFqkU1wJR3RBPEfsxmU6jDWPofrTQ==} + + mdast-util-gfm@3.1.0: + resolution: {integrity: sha512-0ulfdQOM3ysHhCJ1p06l0b0VKlhU0wuQs3thxZQagjcjPrlFRqY215uZGHHJan9GEAXd9MbfPjFJz+qMkVR6zQ==} + + mdast-util-mdx-expression@2.0.1: + resolution: {integrity: sha512-J6f+9hUp+ldTZqKRSg7Vw5V6MqjATc+3E4gf3CFNcuZNWD8XdyI6zQ8GqH7f8169MM6P7hMBRDVGnn7oHB9kXQ==} + + mdast-util-mdx-jsx@3.2.0: + resolution: {integrity: sha512-lj/z8v0r6ZtsN/cGNNtemmmfoLAFZnjMbNyLzBafjzikOM+glrjNHPlf6lQDOTccj9n5b0PPihEBbhneMyGs1Q==} + + mdast-util-mdxjs-esm@2.0.1: + resolution: {integrity: sha512-EcmOpxsZ96CvlP03NghtH1EsLtr0n9Tm4lPUJUBccV9RwUOneqSycg19n5HGzCf+10LozMRSObtVr3ee1WoHtg==} + + mdast-util-phrasing@4.1.0: + resolution: {integrity: sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w==} + + mdast-util-to-hast@13.2.1: + resolution: {integrity: sha512-cctsq2wp5vTsLIcaymblUriiTcZd0CwWtCbLvrOzYCDZoWyMNV8sZ7krj09FSnsiJi3WVsHLM4k6Dq/yaPyCXA==} + + mdast-util-to-markdown@2.1.2: + resolution: {integrity: sha512-xj68wMTvGXVOKonmog6LwyJKrYXZPvlwabaryTjLh9LuvovB/KAH+kvi8Gjj+7rJjsFi23nkUxRQv1KqSroMqA==} + + mdast-util-to-string@4.0.0: + resolution: {integrity: sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==} + merge2@1.4.1: resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} engines: {node: '>= 8'} + micromark-core-commonmark@2.0.3: + resolution: {integrity: sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg==} + + micromark-extension-gfm-autolink-literal@2.1.0: + resolution: {integrity: sha512-oOg7knzhicgQ3t4QCjCWgTmfNhvQbDDnJeVu9v81r7NltNCVmhPy1fJRX27pISafdjL+SVc4d3l48Gb6pbRypw==} + + micromark-extension-gfm-footnote@2.1.0: + resolution: {integrity: sha512-/yPhxI1ntnDNsiHtzLKYnE3vf9JZ6cAisqVDauhp4CEHxlb4uoOTxOCJ+9s51bIB8U1N1FJ1RXOKTIlD5B/gqw==} + + micromark-extension-gfm-strikethrough@2.1.0: + resolution: {integrity: sha512-ADVjpOOkjz1hhkZLlBiYA9cR2Anf8F4HqZUO6e5eDcPQd0Txw5fxLzzxnEkSkfnD0wziSGiv7sYhk/ktvbf1uw==} + + micromark-extension-gfm-table@2.1.1: + resolution: {integrity: sha512-t2OU/dXXioARrC6yWfJ4hqB7rct14e8f7m0cbI5hUmDyyIlwv5vEtooptH8INkbLzOatzKuVbQmAYcbWoyz6Dg==} + + micromark-extension-gfm-tagfilter@2.0.0: + resolution: {integrity: sha512-xHlTOmuCSotIA8TW1mDIM6X2O1SiX5P9IuDtqGonFhEK0qgRI4yeC6vMxEV2dgyr2TiD+2PQ10o+cOhdVAcwfg==} + + micromark-extension-gfm-task-list-item@2.1.0: + resolution: {integrity: sha512-qIBZhqxqI6fjLDYFTBIa4eivDMnP+OZqsNwmQ3xNLE4Cxwc+zfQEfbs6tzAo2Hjq+bh6q5F+Z8/cksrLFYWQQw==} + + micromark-extension-gfm@3.0.0: + resolution: {integrity: sha512-vsKArQsicm7t0z2GugkCKtZehqUm31oeGBV/KVSorWSy8ZlNAv7ytjFhvaryUiCUJYqs+NoE6AFhpQvBTM6Q4w==} + + micromark-factory-destination@2.0.1: + resolution: {integrity: sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA==} + + micromark-factory-label@2.0.1: + resolution: {integrity: sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg==} + + micromark-factory-space@2.0.1: + resolution: {integrity: sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==} + + micromark-factory-title@2.0.1: + resolution: {integrity: sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw==} + + micromark-factory-whitespace@2.0.1: + resolution: {integrity: sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ==} + + micromark-util-character@2.1.1: + resolution: {integrity: sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==} + + micromark-util-chunked@2.0.1: + resolution: {integrity: sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA==} + + micromark-util-classify-character@2.0.1: + resolution: {integrity: sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q==} + + micromark-util-combine-extensions@2.0.1: + resolution: {integrity: sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg==} + + micromark-util-decode-numeric-character-reference@2.0.2: + resolution: {integrity: sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw==} + + micromark-util-decode-string@2.0.1: + resolution: {integrity: sha512-nDV/77Fj6eH1ynwscYTOsbK7rR//Uj0bZXBwJZRfaLEJ1iGBR6kIfNmlNqaqJf649EP0F3NWNdeJi03elllNUQ==} + + micromark-util-encode@2.0.1: + resolution: {integrity: sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==} + + micromark-util-html-tag-name@2.0.1: + resolution: {integrity: sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA==} + + micromark-util-normalize-identifier@2.0.1: + resolution: {integrity: sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q==} + + micromark-util-resolve-all@2.0.1: + resolution: {integrity: sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg==} + + micromark-util-sanitize-uri@2.0.1: + resolution: {integrity: sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==} + + micromark-util-subtokenize@2.1.0: + resolution: {integrity: sha512-XQLu552iSctvnEcgXw6+Sx75GflAPNED1qx7eBJ+wydBb2KCbRZe+NwvIEEMM83uml1+2WSXpBAcp9IUCgCYWA==} + + micromark-util-symbol@2.0.1: + resolution: {integrity: sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==} + + micromark-util-types@2.0.2: + resolution: {integrity: sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==} + + micromark@4.0.2: + resolution: {integrity: sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA==} + micromatch@4.0.8: resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} engines: {node: '>=8.6'} @@ -1705,6 +1935,9 @@ packages: resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} engines: {node: '>=6'} + parse-entities@4.0.2: + resolution: {integrity: sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw==} + path-exists@4.0.0: resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} engines: {node: '>=8'} @@ -1750,6 +1983,9 @@ packages: prop-types@15.8.1: resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==} + property-information@7.2.0: + resolution: {integrity: sha512-IAtzIB6sUiWaJYrX9smp3V46pBGbBeLFRGdh25kg1334VcBlD8HzhPeNIWQH9zhGmo2itIe25EHt9dQP7G5hmg==} + punycode@2.3.1: resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} engines: {node: '>=6'} @@ -1765,6 +2001,12 @@ packages: react-is@16.13.1: resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} + react-markdown@10.1.0: + resolution: {integrity: sha512-qKxVopLT/TyA6BX3Ue5NwabOsAzm0Q7kAPwq6L+wWDwisYs7R8vZ0nRXqq6rkueboxpkjvLGU9fWifiX/ZZFxQ==} + peerDependencies: + '@types/react': '>=18' + react: '>=18' + react@19.2.8: resolution: {integrity: sha512-PWaYA1L/q9u2u7xYQi+Y3L3Yfnie7XyLeaJICV1MGD6LprsBxcAqGjYyr0eY3p+QdsA+x/Irkt4Qif8D63+Sbw==} engines: {node: '>=0.10.0'} @@ -1777,6 +2019,18 @@ packages: resolution: {integrity: sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==} engines: {node: '>= 0.4'} + remark-gfm@4.0.1: + resolution: {integrity: sha512-1quofZ2RQ9EWdeN34S79+KExV1764+wCUGop5CPL1WGdD0ocPpu91lzPGbwWMECpEpd42kJGQwzRfyov9j4yNg==} + + remark-parse@11.0.0: + resolution: {integrity: sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA==} + + remark-rehype@11.1.2: + resolution: {integrity: sha512-Dh7l57ianaEoIpzbp0PC9UKAdCSVklD8E5Rpw7ETfbTl3FqcOOgq5q2LVDhgGCkaBv7p24JXikPdvhhmHvKMsw==} + + remark-stringify@11.0.0: + resolution: {integrity: sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw==} + resolve-from@4.0.0: resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} engines: {node: '>=4'} @@ -1869,6 +2123,9 @@ packages: resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} engines: {node: '>=0.10.0'} + space-separated-tokens@2.0.2: + resolution: {integrity: sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==} + stable-hash@0.0.5: resolution: {integrity: sha512-+L3ccpzibovGXFK+Ap/f8LOS0ahMrHTf3xu7mMLSpEGU0EO9ucaysSylKo9eRDFNhWve/y275iPmIZ4z39a9iA==} @@ -1899,6 +2156,9 @@ packages: resolution: {integrity: sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==} engines: {node: '>= 0.4'} + stringify-entities@4.0.4: + resolution: {integrity: sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==} + strip-bom@3.0.0: resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==} engines: {node: '>=4'} @@ -1907,6 +2167,12 @@ packages: resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} engines: {node: '>=8'} + style-to-js@1.1.21: + resolution: {integrity: sha512-RjQetxJrrUJLQPHbLku6U/ocGtzyjbJMP9lCNK7Ag0CNh690nSH8woqWH9u16nMjYBAok+i7JO1NP2pOy8IsPQ==} + + style-to-object@1.0.14: + resolution: {integrity: sha512-LIN7rULI0jBscWQYaSswptyderlarFkjQ+t79nzty8tcIAceVomEVlLzH5VP4Cmsv6MtKhs7qaAiwlcp+Mgaxw==} + styled-jsx@5.1.6: resolution: {integrity: sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA==} engines: {node: '>= 12.0.0'} @@ -1952,6 +2218,12 @@ packages: resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} engines: {node: '>=8.0'} + trim-lines@3.0.1: + resolution: {integrity: sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==} + + trough@2.2.0: + resolution: {integrity: sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==} + ts-api-utils@2.5.0: resolution: {integrity: sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==} engines: {node: '>=18.12'} @@ -2007,6 +2279,24 @@ packages: resolution: {integrity: sha512-IDxfleLmmbSskfWSUATiN1nfn2rDuvnMOqb5CWR92iIfojA0Ud+ulOAAEQ57LPr9rWmsreUyf5lwyao+7GNNVw==} engines: {node: '>=20.18.1'} + unified@11.0.5: + resolution: {integrity: sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==} + + unist-util-is@6.0.1: + resolution: {integrity: sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g==} + + unist-util-position@5.0.0: + resolution: {integrity: sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==} + + unist-util-stringify-position@4.0.0: + resolution: {integrity: sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==} + + unist-util-visit-parents@6.0.2: + resolution: {integrity: sha512-goh1s1TBrqSqukSc8wrjwWhL0hiJxgA8m4kFxGlQ+8FYQ3C/m11FcTs4YYem7V664AhHVvgoQLk890Ssdsr2IQ==} + + unist-util-visit@5.1.0: + resolution: {integrity: sha512-m+vIdyeCOpdr/QeQCu2EzxX/ohgS8KbnPDgFni4dQsfSCtpz8UqDyY5GjRru8PDKuYn7Fq19j1CQ+nJSsGKOzg==} + unrs-resolver@1.12.2: resolution: {integrity: sha512-dmlRxBJJayXjqTwC+JtF1HhJmgf3ftQ3YejFcZrf4+KKtJv0qDsK1pjqaaVjG7wJ5NJ6UVP1OqRMQ71Z4C3rxQ==} @@ -2024,6 +2314,12 @@ packages: peerDependencies: react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + vfile-message@4.0.3: + resolution: {integrity: sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw==} + + vfile@6.0.3: + resolution: {integrity: sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==} + which-boxed-primitive@1.1.1: resolution: {integrity: sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==} engines: {node: '>= 0.4'} @@ -2065,6 +2361,9 @@ packages: zod@4.4.3: resolution: {integrity: sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==} + zwitch@2.0.4: + resolution: {integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==} + snapshots: '@ai-sdk/anthropic@4.0.42(zod@4.4.3)': @@ -2565,12 +2864,30 @@ snapshots: tslib: 2.8.1 optional: true + '@types/debug@4.1.13': + dependencies: + '@types/ms': 2.1.0 + + '@types/estree-jsx@1.0.5': + dependencies: + '@types/estree': 1.0.9 + '@types/estree@1.0.9': {} + '@types/hast@3.0.5': + dependencies: + '@types/unist': 3.0.3 + '@types/json-schema@7.0.15': {} '@types/json5@0.0.29': {} + '@types/mdast@4.0.4': + dependencies: + '@types/unist': 3.0.3 + + '@types/ms@2.1.0': {} + '@types/node@20.19.43': dependencies: undici-types: 6.21.0 @@ -2583,6 +2900,10 @@ snapshots: dependencies: csstype: 3.2.3 + '@types/unist@2.0.11': {} + + '@types/unist@3.0.3': {} + '@typescript-eslint/eslint-plugin@8.68.0(@typescript-eslint/parser@8.68.0(eslint@9.39.5(jiti@2.7.0))(typescript@5.9.3))(eslint@9.39.5(jiti@2.7.0))(typescript@5.9.3)': dependencies: '@eslint-community/regexpp': 4.12.2 @@ -2674,6 +2995,8 @@ snapshots: '@typescript-eslint/types': 8.68.0 eslint-visitor-keys: 5.0.1 + '@ungap/structured-clone@1.3.3': {} + '@unrs/resolver-binding-android-arm-eabi@1.12.2': optional: true @@ -2855,6 +3178,8 @@ snapshots: axobject-query@4.1.0: {} + bail@2.0.2: {} + balanced-match@1.0.2: {} balanced-match@4.0.4: {} @@ -2903,11 +3228,21 @@ snapshots: caniuse-lite@1.0.30001810: {} + ccount@2.0.1: {} + chalk@4.1.2: dependencies: ansi-styles: 4.3.0 supports-color: 7.2.0 + character-entities-html4@2.1.0: {} + + character-entities-legacy@3.0.0: {} + + character-entities@2.0.2: {} + + character-reference-invalid@2.0.1: {} + client-only@0.0.1: {} color-convert@2.0.1: @@ -2916,6 +3251,8 @@ snapshots: color-name@1.1.4: {} + comma-separated-tokens@2.0.3: {} + concat-map@0.0.1: {} convert-source-map@2.0.0: {} @@ -2956,6 +3293,10 @@ snapshots: dependencies: ms: 2.1.3 + decode-named-character-reference@1.3.0: + dependencies: + character-entities: 2.0.2 + deep-is@0.1.4: {} define-data-property@1.1.4: @@ -2974,6 +3315,10 @@ snapshots: detect-libc@2.1.2: {} + devlop@1.1.0: + dependencies: + dequal: 2.0.3 + doctrine@2.1.0: dependencies: esutils: 2.0.3 @@ -3108,6 +3453,8 @@ snapshots: escape-string-regexp@4.0.0: {} + escape-string-regexp@5.0.0: {} + eslint-config-next@16.3.2(@typescript-eslint/parser@8.68.0(eslint@9.39.5(jiti@2.7.0))(typescript@5.9.3))(eslint@9.39.5(jiti@2.7.0))(typescript@5.9.3): dependencies: '@next/eslint-plugin-next': 16.3.2(eslint@9.39.5(jiti@2.7.0)) @@ -3311,10 +3658,14 @@ snapshots: estraverse@5.3.0: {} + estree-util-is-identifier-name@3.0.0: {} + esutils@2.0.3: {} eventsource-parser@3.1.1: {} + extend@3.0.2: {} + fast-deep-equal@3.1.3: {} fast-glob@3.3.1: @@ -3452,12 +3803,38 @@ snapshots: dependencies: function-bind: 1.1.2 + hast-util-to-jsx-runtime@2.3.6: + dependencies: + '@types/estree': 1.0.9 + '@types/hast': 3.0.5 + '@types/unist': 3.0.3 + comma-separated-tokens: 2.0.3 + devlop: 1.1.0 + estree-util-is-identifier-name: 3.0.0 + hast-util-whitespace: 3.0.0 + mdast-util-mdx-expression: 2.0.1 + mdast-util-mdx-jsx: 3.2.0 + mdast-util-mdxjs-esm: 2.0.1 + property-information: 7.2.0 + space-separated-tokens: 2.0.2 + style-to-js: 1.1.21 + unist-util-position: 5.0.0 + vfile-message: 4.0.3 + transitivePeerDependencies: + - supports-color + + hast-util-whitespace@3.0.0: + dependencies: + '@types/hast': 3.0.5 + hermes-estree@0.25.1: {} hermes-parser@0.25.1: dependencies: hermes-estree: 0.25.1 + html-url-attributes@3.0.1: {} + ignore@5.3.2: {} ignore@7.0.6: {} @@ -3469,12 +3846,21 @@ snapshots: imurmurhash@0.1.4: {} + inline-style-parser@0.2.7: {} + internal-slot@1.1.0: dependencies: es-errors: 1.3.0 hasown: 2.0.4 side-channel: 1.1.1 + is-alphabetical@2.0.1: {} + + is-alphanumerical@2.0.1: + dependencies: + is-alphabetical: 2.0.1 + is-decimal: 2.0.1 + is-array-buffer@3.0.5: dependencies: call-bind: 1.0.9 @@ -3519,6 +3905,8 @@ snapshots: call-bound: 1.0.4 has-tostringtag: 1.0.2 + is-decimal@2.0.1: {} + is-document.all@1.0.0: dependencies: call-bound: 1.0.4 @@ -3541,6 +3929,8 @@ snapshots: dependencies: is-extglob: 2.1.1 + is-hexadecimal@2.0.1: {} + is-map@2.0.3: {} is-negative-zero@2.0.3: {} @@ -3552,6 +3942,8 @@ snapshots: is-number@7.0.0: {} + is-plain-obj@4.1.0: {} + is-regex@1.2.1: dependencies: call-bound: 1.0.4 @@ -3705,6 +4097,8 @@ snapshots: lodash.merge@4.6.2: {} + longest-streak@3.1.0: {} + loose-envify@1.4.0: dependencies: js-tokens: 4.0.0 @@ -3717,10 +4111,356 @@ snapshots: dependencies: '@jridgewell/sourcemap-codec': 1.5.5 + markdown-table@3.0.4: {} + math-intrinsics@1.1.0: {} + mdast-util-find-and-replace@3.0.2: + dependencies: + '@types/mdast': 4.0.4 + escape-string-regexp: 5.0.0 + unist-util-is: 6.0.1 + unist-util-visit-parents: 6.0.2 + + mdast-util-from-markdown@2.0.3: + dependencies: + '@types/mdast': 4.0.4 + '@types/unist': 3.0.3 + decode-named-character-reference: 1.3.0 + devlop: 1.1.0 + mdast-util-to-string: 4.0.0 + micromark: 4.0.2 + micromark-util-decode-numeric-character-reference: 2.0.2 + micromark-util-decode-string: 2.0.1 + micromark-util-normalize-identifier: 2.0.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + unist-util-stringify-position: 4.0.0 + transitivePeerDependencies: + - supports-color + + mdast-util-gfm-autolink-literal@2.0.1: + dependencies: + '@types/mdast': 4.0.4 + ccount: 2.0.1 + devlop: 1.1.0 + mdast-util-find-and-replace: 3.0.2 + micromark-util-character: 2.1.1 + + mdast-util-gfm-footnote@2.1.0: + dependencies: + '@types/mdast': 4.0.4 + devlop: 1.1.0 + mdast-util-from-markdown: 2.0.3 + mdast-util-to-markdown: 2.1.2 + micromark-util-normalize-identifier: 2.0.1 + transitivePeerDependencies: + - supports-color + + mdast-util-gfm-strikethrough@2.0.0: + dependencies: + '@types/mdast': 4.0.4 + mdast-util-from-markdown: 2.0.3 + mdast-util-to-markdown: 2.1.2 + transitivePeerDependencies: + - supports-color + + mdast-util-gfm-table@2.0.0: + dependencies: + '@types/mdast': 4.0.4 + devlop: 1.1.0 + markdown-table: 3.0.4 + mdast-util-from-markdown: 2.0.3 + mdast-util-to-markdown: 2.1.2 + transitivePeerDependencies: + - supports-color + + mdast-util-gfm-task-list-item@2.0.0: + dependencies: + '@types/mdast': 4.0.4 + devlop: 1.1.0 + mdast-util-from-markdown: 2.0.3 + mdast-util-to-markdown: 2.1.2 + transitivePeerDependencies: + - supports-color + + mdast-util-gfm@3.1.0: + dependencies: + mdast-util-from-markdown: 2.0.3 + mdast-util-gfm-autolink-literal: 2.0.1 + mdast-util-gfm-footnote: 2.1.0 + mdast-util-gfm-strikethrough: 2.0.0 + mdast-util-gfm-table: 2.0.0 + mdast-util-gfm-task-list-item: 2.0.0 + mdast-util-to-markdown: 2.1.2 + transitivePeerDependencies: + - supports-color + + mdast-util-mdx-expression@2.0.1: + dependencies: + '@types/estree-jsx': 1.0.5 + '@types/hast': 3.0.5 + '@types/mdast': 4.0.4 + devlop: 1.1.0 + mdast-util-from-markdown: 2.0.3 + mdast-util-to-markdown: 2.1.2 + transitivePeerDependencies: + - supports-color + + mdast-util-mdx-jsx@3.2.0: + dependencies: + '@types/estree-jsx': 1.0.5 + '@types/hast': 3.0.5 + '@types/mdast': 4.0.4 + '@types/unist': 3.0.3 + ccount: 2.0.1 + devlop: 1.1.0 + mdast-util-from-markdown: 2.0.3 + mdast-util-to-markdown: 2.1.2 + parse-entities: 4.0.2 + stringify-entities: 4.0.4 + unist-util-stringify-position: 4.0.0 + vfile-message: 4.0.3 + transitivePeerDependencies: + - supports-color + + mdast-util-mdxjs-esm@2.0.1: + dependencies: + '@types/estree-jsx': 1.0.5 + '@types/hast': 3.0.5 + '@types/mdast': 4.0.4 + devlop: 1.1.0 + mdast-util-from-markdown: 2.0.3 + mdast-util-to-markdown: 2.1.2 + transitivePeerDependencies: + - supports-color + + mdast-util-phrasing@4.1.0: + dependencies: + '@types/mdast': 4.0.4 + unist-util-is: 6.0.1 + + mdast-util-to-hast@13.2.1: + dependencies: + '@types/hast': 3.0.5 + '@types/mdast': 4.0.4 + '@ungap/structured-clone': 1.3.3 + devlop: 1.1.0 + micromark-util-sanitize-uri: 2.0.1 + trim-lines: 3.0.1 + unist-util-position: 5.0.0 + unist-util-visit: 5.1.0 + vfile: 6.0.3 + + mdast-util-to-markdown@2.1.2: + dependencies: + '@types/mdast': 4.0.4 + '@types/unist': 3.0.3 + longest-streak: 3.1.0 + mdast-util-phrasing: 4.1.0 + mdast-util-to-string: 4.0.0 + micromark-util-classify-character: 2.0.1 + micromark-util-decode-string: 2.0.1 + unist-util-visit: 5.1.0 + zwitch: 2.0.4 + + mdast-util-to-string@4.0.0: + dependencies: + '@types/mdast': 4.0.4 + merge2@1.4.1: {} + micromark-core-commonmark@2.0.3: + dependencies: + decode-named-character-reference: 1.3.0 + devlop: 1.1.0 + micromark-factory-destination: 2.0.1 + micromark-factory-label: 2.0.1 + micromark-factory-space: 2.0.1 + micromark-factory-title: 2.0.1 + micromark-factory-whitespace: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-chunked: 2.0.1 + micromark-util-classify-character: 2.0.1 + micromark-util-html-tag-name: 2.0.1 + micromark-util-normalize-identifier: 2.0.1 + micromark-util-resolve-all: 2.0.1 + micromark-util-subtokenize: 2.1.0 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-extension-gfm-autolink-literal@2.1.0: + dependencies: + micromark-util-character: 2.1.1 + micromark-util-sanitize-uri: 2.0.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-extension-gfm-footnote@2.1.0: + dependencies: + devlop: 1.1.0 + micromark-core-commonmark: 2.0.3 + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-normalize-identifier: 2.0.1 + micromark-util-sanitize-uri: 2.0.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-extension-gfm-strikethrough@2.1.0: + dependencies: + devlop: 1.1.0 + micromark-util-chunked: 2.0.1 + micromark-util-classify-character: 2.0.1 + micromark-util-resolve-all: 2.0.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-extension-gfm-table@2.1.1: + dependencies: + devlop: 1.1.0 + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-extension-gfm-tagfilter@2.0.0: + dependencies: + micromark-util-types: 2.0.2 + + micromark-extension-gfm-task-list-item@2.1.0: + dependencies: + devlop: 1.1.0 + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-extension-gfm@3.0.0: + dependencies: + micromark-extension-gfm-autolink-literal: 2.1.0 + micromark-extension-gfm-footnote: 2.1.0 + micromark-extension-gfm-strikethrough: 2.1.0 + micromark-extension-gfm-table: 2.1.1 + micromark-extension-gfm-tagfilter: 2.0.0 + micromark-extension-gfm-task-list-item: 2.1.0 + micromark-util-combine-extensions: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-factory-destination@2.0.1: + dependencies: + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-factory-label@2.0.1: + dependencies: + devlop: 1.1.0 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-factory-space@2.0.1: + dependencies: + micromark-util-character: 2.1.1 + micromark-util-types: 2.0.2 + + micromark-factory-title@2.0.1: + dependencies: + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-factory-whitespace@2.0.1: + dependencies: + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-util-character@2.1.1: + dependencies: + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-util-chunked@2.0.1: + dependencies: + micromark-util-symbol: 2.0.1 + + micromark-util-classify-character@2.0.1: + dependencies: + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-util-combine-extensions@2.0.1: + dependencies: + micromark-util-chunked: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-util-decode-numeric-character-reference@2.0.2: + dependencies: + micromark-util-symbol: 2.0.1 + + micromark-util-decode-string@2.0.1: + dependencies: + decode-named-character-reference: 1.3.0 + micromark-util-character: 2.1.1 + micromark-util-decode-numeric-character-reference: 2.0.2 + micromark-util-symbol: 2.0.1 + + micromark-util-encode@2.0.1: {} + + micromark-util-html-tag-name@2.0.1: {} + + micromark-util-normalize-identifier@2.0.1: + dependencies: + micromark-util-symbol: 2.0.1 + + micromark-util-resolve-all@2.0.1: + dependencies: + micromark-util-types: 2.0.2 + + micromark-util-sanitize-uri@2.0.1: + dependencies: + micromark-util-character: 2.1.1 + micromark-util-encode: 2.0.1 + micromark-util-symbol: 2.0.1 + + micromark-util-subtokenize@2.1.0: + dependencies: + devlop: 1.1.0 + micromark-util-chunked: 2.0.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-util-symbol@2.0.1: {} + + micromark-util-types@2.0.2: {} + + micromark@4.0.2: + dependencies: + '@types/debug': 4.1.13 + debug: 4.4.3 + decode-named-character-reference: 1.3.0 + devlop: 1.1.0 + micromark-core-commonmark: 2.0.3 + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-chunked: 2.0.1 + micromark-util-combine-extensions: 2.0.1 + micromark-util-decode-numeric-character-reference: 2.0.2 + micromark-util-encode: 2.0.1 + micromark-util-normalize-identifier: 2.0.1 + micromark-util-resolve-all: 2.0.1 + micromark-util-sanitize-uri: 2.0.1 + micromark-util-subtokenize: 2.1.0 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + transitivePeerDependencies: + - supports-color + micromatch@4.0.8: dependencies: braces: 3.0.3 @@ -3848,6 +4588,16 @@ snapshots: dependencies: callsites: 3.1.0 + parse-entities@4.0.2: + dependencies: + '@types/unist': 2.0.11 + character-entities-legacy: 3.0.0 + character-reference-invalid: 2.0.1 + decode-named-character-reference: 1.3.0 + is-alphanumerical: 2.0.1 + is-decimal: 2.0.1 + is-hexadecimal: 2.0.1 + path-exists@4.0.0: {} path-key@3.1.1: {} @@ -3884,6 +4634,8 @@ snapshots: object-assign: 4.1.1 react-is: 16.13.1 + property-information@7.2.0: {} + punycode@2.3.1: {} queue-microtask@1.2.3: {} @@ -3895,6 +4647,24 @@ snapshots: react-is@16.13.1: {} + react-markdown@10.1.0(@types/react@19.2.18)(react@19.2.8): + dependencies: + '@types/hast': 3.0.5 + '@types/mdast': 4.0.4 + '@types/react': 19.2.18 + devlop: 1.1.0 + hast-util-to-jsx-runtime: 2.3.6 + html-url-attributes: 3.0.1 + mdast-util-to-hast: 13.2.1 + react: 19.2.8 + remark-parse: 11.0.0 + remark-rehype: 11.1.2 + unified: 11.0.5 + unist-util-visit: 5.1.0 + vfile: 6.0.3 + transitivePeerDependencies: + - supports-color + react@19.2.8: {} reflect.getprototypeof@1.0.10: @@ -3917,6 +4687,40 @@ snapshots: gopd: 1.2.0 set-function-name: 2.0.2 + remark-gfm@4.0.1: + dependencies: + '@types/mdast': 4.0.4 + mdast-util-gfm: 3.1.0 + micromark-extension-gfm: 3.0.0 + remark-parse: 11.0.0 + remark-stringify: 11.0.0 + unified: 11.0.5 + transitivePeerDependencies: + - supports-color + + remark-parse@11.0.0: + dependencies: + '@types/mdast': 4.0.4 + mdast-util-from-markdown: 2.0.3 + micromark-util-types: 2.0.2 + unified: 11.0.5 + transitivePeerDependencies: + - supports-color + + remark-rehype@11.1.2: + dependencies: + '@types/hast': 3.0.5 + '@types/mdast': 4.0.4 + mdast-util-to-hast: 13.2.1 + unified: 11.0.5 + vfile: 6.0.3 + + remark-stringify@11.0.0: + dependencies: + '@types/mdast': 4.0.4 + mdast-util-to-markdown: 2.1.2 + unified: 11.0.5 + resolve-from@4.0.0: {} resolve-pkg-maps@1.0.0: {} @@ -4053,6 +4857,8 @@ snapshots: source-map-js@1.2.1: {} + space-separated-tokens@2.0.2: {} + stable-hash@0.0.5: {} stop-iteration-iterator@1.1.0: @@ -4111,10 +4917,23 @@ snapshots: define-properties: 1.2.1 es-object-atoms: 1.1.2 + stringify-entities@4.0.4: + dependencies: + character-entities-html4: 2.1.0 + character-entities-legacy: 3.0.0 + strip-bom@3.0.0: {} strip-json-comments@3.1.1: {} + style-to-js@1.1.21: + dependencies: + style-to-object: 1.0.14 + + style-to-object@1.0.14: + dependencies: + inline-style-parser: 0.2.7 + styled-jsx@5.1.6(@babel/core@7.29.7)(react@19.2.8): dependencies: client-only: 0.0.1 @@ -4149,6 +4968,10 @@ snapshots: dependencies: is-number: 7.0.0 + trim-lines@3.0.1: {} + + trough@2.2.0: {} + ts-api-utils@2.5.0(typescript@5.9.3): dependencies: typescript: 5.9.3 @@ -4223,6 +5046,39 @@ snapshots: undici@7.29.0: {} + unified@11.0.5: + dependencies: + '@types/unist': 3.0.3 + bail: 2.0.2 + devlop: 1.1.0 + extend: 3.0.2 + is-plain-obj: 4.1.0 + trough: 2.2.0 + vfile: 6.0.3 + + unist-util-is@6.0.1: + dependencies: + '@types/unist': 3.0.3 + + unist-util-position@5.0.0: + dependencies: + '@types/unist': 3.0.3 + + unist-util-stringify-position@4.0.0: + dependencies: + '@types/unist': 3.0.3 + + unist-util-visit-parents@6.0.2: + dependencies: + '@types/unist': 3.0.3 + unist-util-is: 6.0.1 + + unist-util-visit@5.1.0: + dependencies: + '@types/unist': 3.0.3 + unist-util-is: 6.0.1 + unist-util-visit-parents: 6.0.2 + unrs-resolver@1.12.2: dependencies: napi-postinstall: 0.3.4 @@ -4264,6 +5120,16 @@ snapshots: dependencies: react: 19.2.8 + vfile-message@4.0.3: + dependencies: + '@types/unist': 3.0.3 + unist-util-stringify-position: 4.0.0 + + vfile@6.0.3: + dependencies: + '@types/unist': 3.0.3 + vfile-message: 4.0.3 + which-boxed-primitive@1.1.1: dependencies: is-bigint: 1.1.0 @@ -4320,3 +5186,5 @@ snapshots: zod: 4.4.3 zod@4.4.3: {} + + zwitch@2.0.4: {} diff --git a/app/src/app/api/chat/route.ts b/app/src/app/api/chat/route.ts new file mode 100644 index 0000000..c467b0e --- /dev/null +++ b/app/src/app/api/chat/route.ts @@ -0,0 +1,106 @@ +import { anthropic } from '@ai-sdk/anthropic'; +import { + convertToModelMessages, + stepCountIs, + streamText, + type InferUITools, + type UIDataTypes, + type UIMessage, +} from 'ai'; + +import { SYSTEM_PROMPT } from '@/lib/prompts/system'; +import { haiTools } from '@/lib/tools'; + +export const dynamic = 'force-dynamic'; +export const maxDuration = 60; + +// cost: ~$0.003-0.03 per message (Claude Sonnet input+output, varies with tool loops) + +/** Shared with the client so message parts are typed against the real tools. */ +export type HaiUIMessage = UIMessage>; + +const RATE_LIMIT_PER_MINUTE = 20; +const RATE_LIMIT_WINDOW_MS = 60_000; + +/** + * Per-IP rate limit held in process memory. This is genuinely not a production + * rate limiter: it resets on deploy, and every serverless instance keeps its + * own counter, so the effective limit is the configured one multiplied by the + * number of live instances. Anything user-facing needs durable shared storage + * (Redis, Upstash, or a database table) before it can be relied on. + */ +const requestLog = new Map(); + +function clientKey(request: Request): string { + const forwarded = request.headers.get('x-forwarded-for'); + if (forwarded) return forwarded.split(',')[0].trim(); + return request.headers.get('x-real-ip') ?? 'unknown'; +} + +function isRateLimited(key: string): boolean { + const now = Date.now(); + const cutoff = now - RATE_LIMIT_WINDOW_MS; + const recent = (requestLog.get(key) ?? []).filter((at) => at > cutoff); + + if (recent.length >= RATE_LIMIT_PER_MINUTE) { + requestLog.set(key, recent); + return true; + } + + recent.push(now); + requestLog.set(key, recent); + + // Opportunistic sweep so the map does not grow without bound. + if (requestLog.size > 5_000) { + for (const [entryKey, timestamps] of requestLog) { + if (timestamps.every((at) => at <= cutoff)) requestLog.delete(entryKey); + } + } + + return false; +} + +export async function POST(request: Request) { + if (isRateLimited(clientKey(request))) { + return Response.json( + { + error: `Rate limit exceeded — ${RATE_LIMIT_PER_MINUTE} messages per minute. Wait a moment and try again.`, + }, + { status: 429, headers: { 'Retry-After': '60' } }, + ); + } + + if (!process.env.ANTHROPIC_API_KEY) { + return Response.json( + { error: 'ANTHROPIC_API_KEY is not configured on the server.' }, + { status: 500 }, + ); + } + + let messages: HaiUIMessage[]; + try { + ({ messages } = (await request.json()) as { messages: HaiUIMessage[] }); + } catch { + return Response.json({ error: 'Malformed request body.' }, { status: 400 }); + } + + if (!Array.isArray(messages) || messages.length === 0) { + return Response.json({ error: 'No messages provided.' }, { status: 400 }); + } + + const result = streamText({ + model: anthropic('claude-sonnet-5'), + system: SYSTEM_PROMPT, + messages: await convertToModelMessages(messages), + tools: haiTools, + // Multi-step: the model searches the standards, reads what came back, and + // then answers — each of those is a step, and a grounded answer that + // consults several sources needs several. + stopWhen: stepCountIs(6), + }); + + return result.toUIMessageStreamResponse({ + onError: (error) => + error instanceof Error ? error.message : 'The assistant hit an unexpected error.', + }); +} diff --git a/app/src/lib/prompts/system.ts b/app/src/lib/prompts/system.ts new file mode 100644 index 0000000..56f86d9 --- /dev/null +++ b/app/src/lib/prompts/system.ts @@ -0,0 +1,49 @@ +/** + * Single source of truth for HAI's system policy. + * + * Every surface that talks to the model (the chat route, evals, any future + * batch job) imports `SYSTEM_PROMPT` from here. Do not copy this text into + * another file — divergent copies were the main correctness bug in the + * previous iteration of this app. + */ + +export const HAI_STANDARDS_CORPUS = [ + 'Sphere Handbook (2018 edition)', + 'Core Humanitarian Standard on Quality and Accountability (CHS, 2024 revision)', + 'IASC guidance and guidelines', +] as const; + +export const SYSTEM_PROMPT = `You are HAI, an operations assistant for humanitarian practitioners — field coordinators, cluster leads, programme officers, and emergency responders. + +Your authoritative reference corpus is: +- The Sphere Handbook (2018 edition) — minimum standards in humanitarian response +- The Core Humanitarian Standard on Quality and Accountability (CHS, 2024 revision) +- IASC guidance and guidelines + +# Principles + +You operate under the humanitarian principles: humanity, impartiality, neutrality, and independence. You apply do-no-harm analysis to every recommendation, and you treat the centrality of protection as a standing requirement rather than a specialist add-on: flag protection risks in your answers even when the question was framed as purely technical (a WASH, shelter, or logistics question is also a protection question). + +# Grounding and citation + +ALWAYS call the search_standards tool before making any claim about a standard, indicator, threshold, minimum, target figure, or commitment. This includes claims you are confident about. Never answer a standards question from memory — the handbooks are revised, your recollection of a number may be from a superseded edition, and an unsourced figure that looks authoritative is worse than no figure at all. + +Cite what you retrieve: name the source and the section for each substantive claim, so the reader can verify it against the handbook. If the retrieval returns nothing relevant, say plainly that you could not find the standard in the corpus and describe what you do know as general practice, explicitly labelled as unsourced. + +For current figures on an active crisis — caseloads, displacement, funding, food security phases, recent situation reports — call the live-data tools rather than relying on training data, which is stale by definition. Always state the reference period and the source of any figure you report. + +# Behaviour + +Respond in the language the user writes in. + +Refuse to process identifiable personal data about affected people. If a user pastes beneficiary names, phone numbers, precise locations of individuals, biometric or registration identifiers, case notes, or similar, do not repeat, store, structure, or analyse it. Say clearly what you are declining and why: under humanitarian data responsibility norms, identifiable data about affected populations carries protection risk, and a general-purpose assistant is not an appropriate processing environment for it. Then offer the useful alternative — help with the aggregate question, the data-management process, the consent or minimisation design, or the referral pathway. + +For clinical, legal, or security-protocol questions, give the general guidance the standards contain, and pair it with a clear statement that the decision requires qualified personnel: a clinician, a legal adviser, or the mission's security focal point. Do not substitute for that judgement. + +Acknowledge uncertainty rather than fabricating. If you do not know, if the corpus does not cover it, or if the answer depends on context you were not given, say so and name the context you would need. Do not invent section numbers, indicator values, cluster positions, or figures. + +Be conflict-sensitive. Do not advocate political positions, characterise parties to a conflict, assign blame for a crisis, or take a stance on contested sovereignty or status questions. Describe humanitarian consequences and standards, not politics. + +# Style + +Write for a practitioner under time pressure. Lead with the operative answer, then the sourcing, then the caveats that change what they would do. Use the field's own vocabulary without inflating it. Prefer a short table or numbered list when presenting several standards or thresholds. Keep hedging proportional — a well-sourced minimum standard should be stated plainly, not buried in qualifications.`; diff --git a/app/src/lib/retrieval/search.ts b/app/src/lib/retrieval/search.ts new file mode 100644 index 0000000..c0bc113 --- /dev/null +++ b/app/src/lib/retrieval/search.ts @@ -0,0 +1,70 @@ +/** + * Retrieval interface over the humanitarian standards corpus. + * + * The implementation of `searchStandards` is currently a stub. The contract + * below (argument shape, return shape, the `notice` escape hatch) is the part + * that other modules depend on — the ingestion work replaces the function + * body only, and nothing else in the app needs to change. + */ + +export type StandardsSource = 'sphere' | 'chs' | 'iasc'; + +/** `'all'` is accepted at the call site and means "no source filter". */ +export type StandardsSourceFilter = StandardsSource | 'all'; + +export interface StandardsChunk { + /** Stable identifier for the chunk, unique within the corpus. */ + id: string; + source: StandardsSource; + /** + * Human-readable location within the source document, specific enough to + * verify against the printed handbook — e.g. + * "WASH Standard 2.1: Water supply" or "CHS Commitment 5". + */ + section: string; + /** The passage text to be quoted and cited. */ + text: string; + /** Relevance score, higher is better. Comparable only within one result set. */ + score: number; + /** Optional deep link to the passage on the publisher's site. */ + url?: string; +} + +export interface SearchStandardsOptions { + query: string; + source?: StandardsSourceFilter; + /** Maximum chunks to return. Implementations may return fewer. */ + limit?: number; +} + +export interface SearchStandardsResult { + chunks: StandardsChunk[]; + /** + * Set when the result set is empty or degraded for a reason the model needs + * to tell the user about (corpus not ingested, retrieval backend down). + * Absent on a normal successful search, including one that legitimately + * found no match. + */ + notice?: string; +} + +const NOT_INGESTED_NOTICE = + 'knowledge base not yet ingested — the standards corpus is unavailable, so no sourced answer can be given for this query'; + +/** + * Search the standards corpus for passages relevant to `query`. + * + * STUB: returns an empty result set with a notice. Replace the body with the + * real retrieval (embed the query, search the vector store, return ranked + * chunks) — the signature and types above stay as they are. + */ +export async function searchStandards( + options: SearchStandardsOptions, +): Promise { + void options; + + return { + chunks: [], + notice: NOT_INGESTED_NOTICE, + }; +} diff --git a/app/src/lib/tools/crisis-updates.ts b/app/src/lib/tools/crisis-updates.ts new file mode 100644 index 0000000..11c9208 --- /dev/null +++ b/app/src/lib/tools/crisis-updates.ts @@ -0,0 +1,280 @@ +import { tool } from 'ai'; +import { z } from 'zod'; + +// cost: free public API — ReliefWeb (UN OCHA). No key, no per-call charge. + +/** + * ReliefWeb has two access paths, and this tool uses whichever is available: + * + * 1. The JSON API (api.reliefweb.int/v2). Richer and preferred, but since + * 1 November 2025 it rejects any `appname` that OCHA has not pre-approved + * — approval is a reviewed request form, not a self-service key. Set + * RELIEFWEB_APPNAME once you have one and this path activates on its own. + * 2. The public RSS feed on reliefweb.int. No registration, same editorial + * content, fewer fields. This is what runs today. + */ + +const RELIEFWEB_API = 'https://api.reliefweb.int/v2/reports'; +const RELIEFWEB_RSS = 'https://reliefweb.int/updates/rss.xml'; +const RESULT_LIMIT = 5; +const REQUEST_TIMEOUT_MS = 15_000; + +export interface CrisisUpdate { + title: string; + date: string; + source: string; + url: string; + excerpt: string; +} + +export interface CrisisUpdatesResult { + query: string; + retrievedVia: 'reliefweb-api' | 'reliefweb-rss'; + updates: CrisisUpdate[]; + notice?: string; +} + +/** + * 60s in-memory cache. Per-instance and lost on restart, which is fine for a + * feed that updates on the order of hours — it exists to stop a single + * multi-step tool loop from hitting ReliefWeb repeatedly for the same query. + */ +const CACHE_TTL_MS = 60_000; +const cache = new Map(); + +function readCache(key: string): CrisisUpdatesResult | undefined { + const hit = cache.get(key); + if (!hit) return undefined; + if (hit.expiresAt < Date.now()) { + cache.delete(key); + return undefined; + } + return hit.value; +} + +function writeCache(key: string, value: CrisisUpdatesResult) { + cache.set(key, { expiresAt: Date.now() + CACHE_TTL_MS, value }); +} + +const HTML_ENTITIES: Record = { + amp: '&', + lt: '<', + gt: '>', + quot: '"', + apos: "'", + nbsp: ' ', +}; + +function decodeEntities(input: string): string { + return input.replace(/&(#x?[0-9a-fA-F]+|[a-zA-Z]+);/g, (match, entity: string) => { + if (entity.startsWith('#x') || entity.startsWith('#X')) { + return String.fromCodePoint(Number.parseInt(entity.slice(2), 16)); + } + if (entity.startsWith('#')) { + return String.fromCodePoint(Number.parseInt(entity.slice(1), 10)); + } + return HTML_ENTITIES[entity.toLowerCase()] ?? match; + }); +} + +function toPlainText(html: string, maxLength: number): string { + const text = decodeEntities(decodeEntities(html)) + .replace(/<[^>]*>/g, ' ') + .replace(/\s+/g, ' ') + .trim(); + return text.length > maxLength ? `${text.slice(0, maxLength).trimEnd()}…` : text; +} + +function tagContent(xml: string, tag: string): string | undefined { + const match = new RegExp(`<${tag}[^>]*>([\\s\\S]*?)`).exec(xml); + if (!match) return undefined; + const raw = match[1].replace(/^$/, '$1'); + return decodeEntities(raw).trim(); +} + +/** + * The RSS `description` packs the ReliefWeb tags and the report body into one + * escaped HTML blob. The publishing organisation is the `source` tag, which is + * what a practitioner needs to judge the report. + */ +function parseRssItem(item: string): CrisisUpdate | undefined { + const title = tagContent(item, 'title'); + const url = tagContent(item, 'link'); + if (!title || !url) return undefined; + + const description = tagContent(item, 'description') ?? ''; + const sourceTag = /
    Sources?:\s*([\s\S]*?)<\/div>/.exec( + decodeEntities(description), + ); + + const body = decodeEntities(description).replace( + /
    [\s\S]*?<\/div>/g, + '', + ); + + return { + title, + date: tagContent(item, 'pubDate') ?? 'unknown', + source: sourceTag ? toPlainText(sourceTag[1], 200) : 'ReliefWeb', + url, + excerpt: toPlainText(body, 700) || 'No excerpt available.', + }; +} + +async function fetchWithTimeout(url: string, init?: RequestInit): Promise { + return fetch(url, { ...init, signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS) }); +} + +async function fetchViaRss(query: string, country?: string): Promise { + const search = country ? `${country} ${query}`.trim() : query; + const url = `${RELIEFWEB_RSS}?search=${encodeURIComponent(search)}`; + + const response = await fetchWithTimeout(url, { + headers: { Accept: 'application/rss+xml, application/xml' }, + }); + if (!response.ok) { + throw new Error(`ReliefWeb RSS returned ${response.status}`); + } + + const xml = await response.text(); + const items = xml.match(/[\s\S]*?<\/item>/g) ?? []; + + return items + .slice(0, RESULT_LIMIT) + .map(parseRssItem) + .filter((update): update is CrisisUpdate => update !== undefined); +} + +interface ReliefWebApiFields { + title?: string; + url?: string; + date?: { created?: string }; + source?: Array<{ name?: string }>; + 'body-html'?: string; +} + +async function fetchViaApi( + appname: string, + query: string, + country?: string, +): Promise { + const filters = []; + if (country) { + filters.push({ field: 'country', value: country }); + } + + const response = await fetchWithTimeout( + `${RELIEFWEB_API}?appname=${encodeURIComponent(appname)}`, + { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + query: { value: query, operator: 'AND' }, + ...(filters.length ? { filter: { operator: 'AND', conditions: filters } } : {}), + fields: { + include: ['title', 'date.created', 'source.name', 'url', 'body-html'], + }, + sort: ['date.created:desc'], + limit: RESULT_LIMIT, + }), + }, + ); + + if (!response.ok) { + throw new Error(`ReliefWeb API returned ${response.status}`); + } + + const payload = (await response.json()) as { + data?: Array<{ fields?: ReliefWebApiFields }>; + }; + + return (payload.data ?? []).map((entry) => { + const fields = entry.fields ?? {}; + return { + title: fields.title ?? 'Untitled report', + date: fields.date?.created ?? 'unknown', + source: + fields.source?.map((s) => s.name).filter(Boolean).join(', ') || 'ReliefWeb', + url: fields.url ?? 'https://reliefweb.int', + excerpt: toPlainText(fields['body-html'] ?? '', 700) || 'No excerpt available.', + }; + }); +} + +export async function getCrisisUpdates( + query: string, + country?: string, +): Promise { + const cacheKey = `${country ?? ''}::${query}`; + const cached = readCache(cacheKey); + if (cached) return cached; + + const appname = process.env.RELIEFWEB_APPNAME; + let result: CrisisUpdatesResult; + + if (appname) { + try { + result = { + query, + retrievedVia: 'reliefweb-api', + updates: await fetchViaApi(appname, query, country), + }; + } catch { + // An unapproved or revoked appname 403s. Fall through to the open feed + // rather than failing the user's question. + result = { + query, + retrievedVia: 'reliefweb-rss', + updates: await fetchViaRss(query, country), + notice: + 'The ReliefWeb JSON API rejected the configured appname; these results come from the public ReliefWeb feed instead.', + }; + } + } else { + result = { + query, + retrievedVia: 'reliefweb-rss', + updates: await fetchViaRss(query, country), + }; + } + + if (result.updates.length === 0) { + result.notice = + 'ReliefWeb returned no reports for this query. Try a broader search term or the country name on its own.'; + } + + writeCache(cacheKey, result); + return result; +} + +export const crisisUpdatesTool = tool({ + description: + 'Fetch recent situation reports, updates, and analysis published on ReliefWeb (UN OCHA) for a crisis, country, or theme. Use this for anything about the current state of an emergency — displacement, access, response activity, recent developments — rather than answering from memory. Returns titles, publication dates, publishing organisations, links, and excerpts.', + inputSchema: z.object({ + query: z + .string() + .min(1) + .describe( + 'What to search ReliefWeb for — a crisis, theme, or sector, e.g. "displacement", "cholera outbreak response", "humanitarian access".', + ), + country: z + .string() + .optional() + .describe( + 'Country name to focus the search on, e.g. "Sudan", "Democratic Republic of the Congo". Omit for a global search.', + ), + }), + execute: async ({ query, country }) => { + try { + return await getCrisisUpdates(query, country); + } catch (error) { + return { + query, + updates: [], + error: `Could not reach ReliefWeb: ${ + error instanceof Error ? error.message : 'unknown error' + }. Tell the user that live situation reports are unavailable right now rather than substituting remembered figures.`, + }; + } + }, +}); diff --git a/app/src/lib/tools/humanitarian-data.ts b/app/src/lib/tools/humanitarian-data.ts new file mode 100644 index 0000000..9360cb9 --- /dev/null +++ b/app/src/lib/tools/humanitarian-data.ts @@ -0,0 +1,284 @@ +import { tool } from 'ai'; +import { z } from 'zod'; + +// cost: free public API — HDX HAPI (OCHA Centre for Humanitarian Data). No key, +// no per-call charge. The app identifier below is an attribution string, not a +// secret or a credential. + +const HAPI_BASE = 'https://hapi.humdata.org/api/v2'; +const REQUEST_TIMEOUT_MS = 15_000; +const CACHE_TTL_MS = 60_000; + +/** + * HAPI identifies callers with a base64 of "appname:email". It is generated + * locally (their /encode_app_identifier endpoint does the same thing) and + * needs no registration. Override with HDX_APP_IDENTIFIER to attribute traffic + * to a real deployment. + */ +const DEFAULT_APP_IDENTIFIER = Buffer.from('hai-demo:demo@example.org').toString( + 'base64', +); + +function appIdentifier(): string { + return process.env.HDX_APP_IDENTIFIER || DEFAULT_APP_IDENTIFIER; +} + +type Dataset = 'population' | 'food_security' | 'funding' | 'humanitarian_needs'; + +const ENDPOINTS: Record = { + population: '/geography-infrastructure/baseline-population', + food_security: '/food-security-nutrition-poverty/food-security', + funding: '/coordination-context/funding', + humanitarian_needs: '/affected-people/humanitarian-needs', +}; + +const cache = new Map(); + +async function hapiGet( + path: string, + params: Record, +): Promise { + const search = new URLSearchParams({ + ...Object.fromEntries(Object.entries(params).map(([k, v]) => [k, String(v)])), + app_identifier: appIdentifier(), + }); + const url = `${HAPI_BASE}${path}?${search}`; + + const cached = cache.get(url); + if (cached && cached.expiresAt > Date.now()) { + return cached.value as T[]; + } + + const response = await fetchWithTimeout(url); + if (!response.ok) { + throw new Error(`HDX HAPI returned ${response.status} for ${path}`); + } + + const payload = (await response.json()) as { data?: T[] }; + const data = payload.data ?? []; + cache.set(url, { expiresAt: Date.now() + CACHE_TTL_MS, value: data }); + return data; +} + +async function fetchWithTimeout(url: string): Promise { + return fetch(url, { + headers: { Accept: 'application/json' }, + signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS), + }); +} + +interface PeriodRow { + reference_period_start?: string; + reference_period_end?: string; + location_name?: string; +} + +function formatPeriod(row: PeriodRow): string { + const start = row.reference_period_start?.slice(0, 10) ?? '?'; + const end = row.reference_period_end?.slice(0, 10) ?? '?'; + return `${start} to ${end}`; +} + +/** HAPI has no sort parameter, so recency is resolved client-side. */ +function latestPeriodStart(rows: PeriodRow[]): string | undefined { + return rows.reduce((latest, row) => { + const start = row.reference_period_start; + if (!start) return latest; + return !latest || start > latest ? start : latest; + }, undefined); +} + +function isoYearsAgo(years: number): string { + const date = new Date(); + date.setFullYear(date.getFullYear() - years); + return date.toISOString().slice(0, 10); +} + +interface PopulationRow extends PeriodRow { + gender?: string | null; + age_range?: string | null; + population?: number; +} + +async function getPopulation(iso3: string) { + const rows = await hapiGet(ENDPOINTS.population, { + location_code: iso3, + admin_level: 0, + limit: 200, + }); + if (rows.length === 0) return { dataset: 'population', rows: [] }; + + const latest = latestPeriodStart(rows); + const current = rows.filter((row) => row.reference_period_start === latest); + + const total = current.reduce((sum, row) => sum + (row.population ?? 0), 0); + const byGender = current.reduce>((acc, row) => { + const key = row.gender ?? 'unspecified'; + acc[key] = (acc[key] ?? 0) + (row.population ?? 0); + return acc; + }, {}); + + return { + dataset: 'population', + location: current[0]?.location_name ?? iso3, + referencePeriod: formatPeriod(current[0] ?? {}), + totalPopulation: total, + byGender, + note: 'Baseline population, national level. Gender keys: f = female, m = male.', + }; +} + +interface FoodSecurityRow extends PeriodRow { + ipc_phase?: string; + ipc_type?: string; + population_in_phase?: number; + population_fraction_in_phase?: number; +} + +async function getFoodSecurity(iso3: string) { + const rows = await hapiGet(ENDPOINTS.food_security, { + location_code: iso3, + admin_level: 0, + ipc_type: 'current', + start_date: isoYearsAgo(2), + limit: 100, + }); + if (rows.length === 0) return { dataset: 'food_security', phases: [] }; + + const latest = latestPeriodStart(rows); + const current = rows + .filter((row) => row.reference_period_start === latest) + .sort((a, b) => (a.ipc_phase ?? '').localeCompare(b.ipc_phase ?? '')); + + return { + dataset: 'food_security', + location: current[0]?.location_name ?? iso3, + referencePeriod: formatPeriod(current[0] ?? {}), + phases: current.map((row) => ({ + ipcPhase: row.ipc_phase, + populationInPhase: row.population_in_phase, + shareOfPopulation: row.population_fraction_in_phase, + })), + note: 'IPC acute food insecurity, current-period classification, national level. Phase 3+ is crisis or worse.', + }; +} + +interface FundingRow extends PeriodRow { + appeal_name?: string; + appeal_type?: string; + requirements_usd?: number; + funding_usd?: number; + funding_pct?: number; +} + +async function getFunding(iso3: string) { + const rows = await hapiGet(ENDPOINTS.funding, { + location_code: iso3, + start_date: isoYearsAgo(2), + limit: 50, + }); + + const appeals = rows + .sort((a, b) => + (b.reference_period_start ?? '').localeCompare(a.reference_period_start ?? ''), + ) + .slice(0, 6) + .map((row) => ({ + appeal: row.appeal_name, + type: row.appeal_type, + requirementsUsd: row.requirements_usd, + fundedUsd: row.funding_usd, + fundedPercent: row.funding_pct, + referencePeriod: formatPeriod(row), + })); + + return { + dataset: 'funding', + location: rows[0]?.location_name ?? iso3, + appeals, + note: 'Appeal requirements and funding received, from OCHA FTS via HDX HAPI. Figures are cumulative for the appeal period and move during the year.', + }; +} + +interface NeedsRow extends PeriodRow { + sector_name?: string; + sector_code?: string; + population_status?: string; + population?: number; +} + +const POPULATION_STATUS_LABELS: Record = { + INN: 'in need', + TGT: 'targeted', + AFF: 'affected', + REA: 'reached', +}; + +async function getHumanitarianNeeds(iso3: string) { + const rows = await hapiGet(ENDPOINTS.humanitarian_needs, { + location_code: iso3, + admin_level: 0, + start_date: isoYearsAgo(2), + limit: 300, + }); + if (rows.length === 0) return { dataset: 'humanitarian_needs', sectors: [] }; + + const latest = latestPeriodStart(rows); + const current = rows.filter((row) => row.reference_period_start === latest); + + return { + dataset: 'humanitarian_needs', + location: current[0]?.location_name ?? iso3, + referencePeriod: formatPeriod(current[0] ?? {}), + sectors: current + .map((row) => ({ + sector: row.sector_name ?? row.sector_code, + status: + POPULATION_STATUS_LABELS[row.population_status ?? ''] ?? + row.population_status, + population: row.population, + })) + .sort((a, b) => (b.population ?? 0) - (a.population ?? 0)) + .slice(0, 25), + note: 'People in need / targeted by sector, from the Humanitarian Needs and Response Plan via HDX HAPI.', + }; +} + +const HANDLERS: Record Promise> = { + population: getPopulation, + food_security: getFoodSecurity, + funding: getFunding, + humanitarian_needs: getHumanitarianNeeds, +}; + +export const humanitarianDataTool = tool({ + description: + 'Look up quantitative humanitarian indicators for a country from HDX HAPI (OCHA Centre for Humanitarian Data): baseline population, IPC acute food insecurity phases, appeal funding against requirements, and people in need by sector. Use this whenever a question needs a current figure for a country, rather than recalling one. Every result carries its reference period — always report that period alongside the figure.', + inputSchema: z.object({ + country_iso3: z + .string() + .length(3) + .describe( + 'ISO 3166-1 alpha-3 country code, uppercase — e.g. SDN for Sudan, COD for the Democratic Republic of the Congo, AFG for Afghanistan.', + ), + dataset: z + .enum(['population', 'food_security', 'funding', 'humanitarian_needs']) + .describe( + 'Which indicator to retrieve. Call the tool more than once to combine datasets.', + ), + }), + execute: async ({ country_iso3, dataset }) => { + const iso3 = country_iso3.toUpperCase(); + try { + return await HANDLERS[dataset as Dataset](iso3); + } catch (error) { + return { + dataset, + country_iso3: iso3, + error: `Could not reach HDX HAPI: ${ + error instanceof Error ? error.message : 'unknown error' + }. Fall back to ReliefWeb situation reports via crisis_updates, and tell the user the structured figures were unavailable rather than substituting remembered ones.`, + }; + } + }, +}); diff --git a/app/src/lib/tools/index.ts b/app/src/lib/tools/index.ts new file mode 100644 index 0000000..b0ca88a --- /dev/null +++ b/app/src/lib/tools/index.ts @@ -0,0 +1,16 @@ +import { crisisUpdatesTool } from './crisis-updates'; +import { humanitarianDataTool } from './humanitarian-data'; +import { searchStandardsTool } from './search-standards'; + +/** + * The tool set exposed to the model. Keys are the names the model calls and + * the names the UI matches on for its `tool-` message parts, so changing + * one means changing `TOOL_LABELS` in the UI too. + */ +export const haiTools = { + search_standards: searchStandardsTool, + crisis_updates: crisisUpdatesTool, + humanitarian_data: humanitarianDataTool, +}; + +export type HaiTools = typeof haiTools; diff --git a/app/src/lib/tools/search-standards.ts b/app/src/lib/tools/search-standards.ts new file mode 100644 index 0000000..abe153b --- /dev/null +++ b/app/src/lib/tools/search-standards.ts @@ -0,0 +1,35 @@ +import { tool } from 'ai'; +import { z } from 'zod'; + +import { searchStandards } from '@/lib/retrieval/search'; + +// cost: free — local retrieval over the ingested corpus, no third-party call. + +export const searchStandardsTool = tool({ + description: + 'Search the humanitarian standards corpus (Sphere Handbook 2018, Core Humanitarian Standard 2024, IASC guidance) for passages relevant to a question. Call this before stating any standard, indicator, threshold, minimum figure, or commitment — including ones you believe you know. Returns ranked passages with their source and section for citation.', + inputSchema: z.object({ + query: z + .string() + .min(1) + .describe( + 'What to look for, in the vocabulary of the standards themselves — e.g. "minimum litres of water per person per day" rather than "how much water".', + ), + source: z + .enum(['sphere', 'chs', 'iasc', 'all']) + .default('all') + .describe( + 'Restrict to one publication, or "all" to search the whole corpus. Prefer "all" unless the user named a specific handbook.', + ), + }), + execute: async ({ query, source }) => { + const result = await searchStandards({ query, source, limit: 6 }); + + return { + query, + source, + chunks: result.chunks, + ...(result.notice ? { notice: result.notice } : {}), + }; + }, +}); From 2c254bc5bb6fdbc4cb3cf0bb6dfc2779f5c35742 Mon Sep 17 00:00:00 2001 From: Sam Frons Date: Tue, 25 Aug 2026 17:18:14 +0200 Subject: [PATCH 09/46] Add role-based AI enablement playbooks Six playbooks (program officer, protection officer, MEAL officer, communications, grants & partnerships, field logistics) covering where AI genuinely helps, where it should not be used, example prompts by skill level, and role-specific verification habits. Plus a machine- readable index.json for app rendering. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01HCrPUhxWVtRBsYSSc7sBHU --- content/playbooks/communications.md | 61 ++++++++++++++++++++++++ content/playbooks/field-logistics.md | 61 ++++++++++++++++++++++++ content/playbooks/grants-partnerships.md | 61 ++++++++++++++++++++++++ content/playbooks/index.json | 44 +++++++++++++++++ content/playbooks/meal-officer.md | 61 ++++++++++++++++++++++++ content/playbooks/program-officer.md | 61 ++++++++++++++++++++++++ content/playbooks/protection-officer.md | 61 ++++++++++++++++++++++++ 7 files changed, 410 insertions(+) create mode 100644 content/playbooks/communications.md create mode 100644 content/playbooks/field-logistics.md create mode 100644 content/playbooks/grants-partnerships.md create mode 100644 content/playbooks/index.json create mode 100644 content/playbooks/meal-officer.md create mode 100644 content/playbooks/program-officer.md create mode 100644 content/playbooks/protection-officer.md diff --git a/content/playbooks/communications.md b/content/playbooks/communications.md new file mode 100644 index 0000000..4ef02d7 --- /dev/null +++ b/content/playbooks/communications.md @@ -0,0 +1,61 @@ +--- +id: communications +title: Communications Officer Playbook +role: Communications Officer +icon: 📰 +skill_levels: [beginner, intermediate, advanced] +summary: Using HAI for sitrep summarization, donor reporting drafts, and multilingual drafting — with every fact traced back to a source before publication. +--- + +# Communications Officer Playbook + +Communications work is where an AI-invented statistic or a mistranslated protection term does the most damage fastest, because it goes out publicly. The rule that makes AI safe to use here is simple: AI can touch the prose, but every fact in that prose has to trace back to a document you gave it or a source it retrieved — never to what the model "remembers." + +## Where AI genuinely helps in this role + +1. **Summarizing lengthy sitreps or reports** into a shorter internal or public-facing brief. +2. **Drafting a first-pass donor report narrative** from bullet-point inputs and indicator data you supply. +3. **Multilingual drafting support** for public communications — a first pass that a native or fluent speaker then reviews. +4. **Pulling current ReliefWeb updates** on a crisis to draft a situation snapshot with dates and sources attached. +5. **Producing multiple framing variants** of the same fact set — outcome-focused, needs-focused, partnership-focused — for different audiences. + +## Where AI should NOT be used + +- **Never publish AI-drafted content about an active crisis without a fact-check pass** against a primary source — a ReliefWeb report, an OCHA sitrep, a cluster update. +- **Never draft messaging that names or identifies specific beneficiaries, survivors, or vulnerable individuals.** +- **Never let AI draft final messaging on a politically sensitive or conflict-related topic without senior sign-off** — nuance errors here carry real safety and reputational risk. +- **Never treat an AI translation as publication-ready.** Route it through a native or fluent speaker first, especially for terms with legal or protection significance. +- **Never let AI invent a statistic or attribute a figure to a source it hasn't actually retrieved.** + +## Example prompts + +**1. (Beginner)** "Summarize this sitrep [paste public document] into 5 bullet points for an internal team update." +*Why it works: the input is already a public document, the output length is bounded, and the task is low-risk since nothing new is being asserted.* + +**2. (Beginner)** "Pull the latest ReliefWeb updates on [crisis] from the past week and list the headlines with dates and sources." +*Why it works: uses the live data tool and requests dates and sources per item, so the summary is independently checkable.* + +**3. (Intermediate)** "Draft a 150-word public update on our [sector] response in [context], using only the figures I provide here [paste figures]. Do not add any statistic I haven't given you." +*Why it works: an explicit instruction against fabricating numbers, plus a word-count constraint that controls format.* + +**4. (Intermediate)** "Translate this English program update into [language] at a reading level appropriate for a general community audience, and flag any phrase you're unsure translates precisely." +*Why it works: asks the model to surface its own translation uncertainty instead of presenting a guess with false confidence.* + +**5. (Intermediate)** "Rewrite this donor report paragraph [paste] in a more concise, results-focused tone, keeping every number and citation exactly as written." +*Why it works: separates what AI can touch (prose, tone) from what it can't (facts, citations) — the instruction makes that boundary explicit.* + +**6. (Advanced)** "Act as an editor: read this draft press statement [paste] and flag any claim that isn't sourced to a document I've provided, any language that could compromise beneficiary anonymity, and any term that doesn't match IASC-standard terminology." +*Why it works: three distinct, checkable review criteria — sourcing, anonymity, terminology — replace an open-ended "improve this" that would be hard to verify.* + +**7. (Advanced)** "Draft 3 alternate versions of this donor report executive summary [paste]: one emphasizing outcomes, one emphasizing needs context (citing the ReliefWeb/HDX data used), and one emphasizing partnership. Keep the underlying facts identical across all three." +*Why it works: fixes the factual content as a constant and isolates framing as the only variable, so you're comparing tone choices, not accidentally comparing different facts.* + +**8. (Advanced)** "Given this cluster-level sitrep [paste, public], draft a table comparing reported needs by sector against Sphere's four technical chapters, and note which sectors have the least standards coverage in this update." +*Why it works: a structured cross-reference against a named framework, with the output format specified up front.* + +## Verify before you act + +- Fact-check every number and quote in an AI draft against the primary source before it goes external — treat the draft as unverified until you've checked it line by line. +- Route anything naming individuals, survivors, or specific communities through a protection review before publication. +- Have a native or fluent speaker review any AI-assisted translation before public use, especially for legal, protection, or medical terms. +- Get senior or comms-lead sign-off on anything touching a politically sensitive angle of a crisis, no matter how polished the AI draft reads. diff --git a/content/playbooks/field-logistics.md b/content/playbooks/field-logistics.md new file mode 100644 index 0000000..f346b6b --- /dev/null +++ b/content/playbooks/field-logistics.md @@ -0,0 +1,61 @@ +--- +id: field-logistics +title: Field Logistics Officer Playbook +role: Field Logistics Officer +icon: 🚚 +skill_levels: [beginner, intermediate, advanced] +summary: Using HAI for procurement document drafting, distribution planning aids, and supply pipeline queries — never for vendor decisions or real-time safety calls. +--- + +# Field Logistics Officer Playbook + +Logistics work has two things AI should never touch: who gets a contract, and whether a distribution site is safe today. Everything short of those two calls — drafting language, checking a document against a standard, pulling supply data — is fair game. + +## Where AI genuinely helps in this role + +1. **Drafting first-pass procurement document language** — RFQ or tender descriptions — from the technical specs you provide. +2. **Drafting distribution planning checklists** referencing Sphere technical standards for the relevant sector, such as water point spacing or shelter plot sizing. +3. **Summarizing HDX HAPI supply, market, and price data** to inform a procurement planning brief. +4. **Drafting a generic distribution site flow-plan description** — crowd flow, queuing, protection considerations — for review by the field team. +5. **Cross-checking a draft procurement package** against a standard documentation checklist. + +## Where AI should NOT be used + +- **Never let AI select a vendor or make an award decision.** Procurement integrity requires the actual competitive process and a documented human decision. +- **Never paste actual beneficiary lists, headcounts by name, or household-level distribution data into a prompt.** Use aggregated numbers only. +- **Never rely on AI for real-time supply chain or market price data.** Always pull the live HDX HAPI or market monitoring data rather than trusting a remembered figure. +- **Never use AI-drafted content as the final safety or security assessment for a distribution site.** Site risk assessment needs an in-person or currently-informed security review. +- **Never let a procurement document go out with AI-drafted technical specs that haven't been checked by the technical or sector lead who knows the actual equipment needs.** + +## Example prompts + +**1. (Beginner)** "What does Sphere recommend as the minimum water quantity per person per day for a general distribution scenario?" +*Why it works: a direct factual lookup with a citation, and no site-specific data is needed to answer it.* + +**2. (Beginner)** "Draft a generic RFQ template structure — section headers only — for procuring [item category]." +*Why it works: a template-only request keeps vendor and pricing data out of the prompt entirely.* + +**3. (Intermediate)** "Pull the latest HDX HAPI market or price data available for [commodity] in [country] and summarize the trend over the last quarter with sources." +*Why it works: uses the live data tool and requires a sourced trend rather than a guessed or remembered figure.* + +**4. (Intermediate)** "Using these technical specifications [paste specs], draft the technical requirements section of an RFQ for [item], keeping every spec exactly as given." +*Why it works: restricts the drafting to the provided specs, guarding against invented technical details slipping into a procurement document.* + +**5. (Intermediate)** "Draft a distribution site flow-plan checklist for [item] distribution to approximately [aggregate number] households, referencing Sphere guidance on site planning and crowd management." +*Why it works: uses an aggregate household count rather than a household list, and cites a named standard as the basis for the checklist.* + +**6. (Advanced)** "Act as a procurement compliance reviewer: given this draft procurement package [paste, redact vendor-identifying info if sensitive] and our standard procurement checklist [paste], list any required document that appears to be missing." +*Why it works: role-framed as a document-completeness check, explicitly not a vendor evaluation or award recommendation.* + +**7. (Advanced)** "Compare the shelter plot sizing in this site plan draft [paste, aggregate figures only] against Sphere's Shelter and Settlement minimum standards, citing the specific standard and indicator." +*Why it works: uses aggregate, site-level figures rather than household-level data, and requires a citation to the exact chapter and indicator.* + +**8. (Advanced)** "Given this list of recent HDX HAPI supply and market indicators for [region] [tool output], draft a one-page procurement risk brief flagging any commodity showing a price spike or supply constraint, and note where the data is more than 2 weeks old." +*Why it works: asks the model to flag its own data's recency, preventing a stale figure from being presented as current in a risk brief.* + +## Verify before you act + +- Confirm any market, price, or supply figure against the live HDX HAPI dashboard or a current market monitoring report before using it in a procurement decision — pulled data can be dated by the time a plan is finalized. +- Route every procurement document through the actual competitive procurement process and required human sign-offs — AI drafting doesn't replace the paper-trail integrity procurement compliance requires. +- Have the technical or sector lead verify AI-drafted technical specifications against actual equipment and material needs before an RFQ goes out. +- Get a current, field-informed security assessment for any distribution site plan. Never treat an AI-drafted flow-plan as a safety clearance. diff --git a/content/playbooks/grants-partnerships.md b/content/playbooks/grants-partnerships.md new file mode 100644 index 0000000..4f5a936 --- /dev/null +++ b/content/playbooks/grants-partnerships.md @@ -0,0 +1,61 @@ +--- +id: grants-partnerships +title: Grants & Partnerships Officer Playbook +role: Grants & Partnerships Officer +icon: 🤝 +skill_levels: [beginner, intermediate, advanced] +summary: Using HAI for donor compliance checks, budget narrative drafting, and due diligence summaries — always grounded in the actual current guidance text, never model memory of donor rules. +--- + +# Grants & Partnerships Officer Playbook + +Donor rules change every funding round, and getting them wrong costs real money and real relationships. The one rule that governs everything else in this playbook: **never ask AI to recall a donor's rules from memory.** Paste the actual current guidance, RFA, or checklist text, and ask AI to work only from what's in front of it. + +## Where AI genuinely helps in this role + +1. **Cross-checking a draft proposal or report against a specific donor's compliance checklist** — when you provide the actual checklist text. +2. **Drafting first-pass budget narrative language** from a line-item budget and activity description you supply. +3. **Summarizing publicly available information** (registration status, published reports) as a starting point for a partner due diligence file. +4. **Drafting partnership agreement clause language** for legal or grants staff to review — not to finalize. +5. **Extracting eligibility criteria and deadlines** from a long donor call or RFA document into a quick-reference bullet list. + +## Where AI should NOT be used + +- **Never rely on AI's memory of donor rules** — USAID, ECHO, BHA, FCDO, or any other donor's requirements shift per call and per year. Always paste the current guidance and constrain AI to that text. +- **Never let AI make the final due diligence risk determination on a partner.** That call requires the actual verification steps — registration checks, reference calls, sanctions screening — done by a person. +- **Never submit AI-drafted compliance language without a grants specialist verifying it against the live donor portal or guidelines.** +- **Never paste a partner's confidential financial or personal data into a due diligence prompt.** Work from redacted summaries only. +- **Never treat an AI-drafted budget narrative number as authoritative.** It should mirror the actual budget file, not recalculate or estimate figures. + +## Example prompts + +**1. (Beginner)** "Summarize this donor RFA document [paste actual text] into a bullet list of eligibility criteria and the submission deadline." +*Why it works: works from pasted primary text rather than model memory of donor rules, and the extraction task is bounded and easy to spot-check.* + +**2. (Beginner)** "What are Sphere's Shelter and Settlement minimum standards I should reference in a shelter proposal's technical approach section?" +*Why it works: a general standards lookup, answerable with a citation, with no donor-specific or partner-specific risk involved.* + +**3. (Intermediate)** "Using only this line-item budget [paste] and this activity description [paste], draft a budget narrative paragraph explaining the cost basis for the staffing line. Don't invent any figure not in the budget." +*Why it works: an explicit anti-fabrication instruction restricts the model to the provided data rather than letting it interpolate plausible-sounding numbers.* + +**4. (Intermediate)** "Compare this draft proposal section [paste] against this donor compliance checklist [paste actual checklist text] and list any checklist item not addressed." +*Why it works: both sides of the comparison are pasted primary text, not recalled from memory, so the resulting gap list is checkable against real documents.* + +**5. (Intermediate)** "Draft a due diligence summary template — headers and prompts only, not content — for assessing a new local partner, covering registration, governance, financial management, and safeguarding." +*Why it works: requests a structural template, not a real judgment about a real partner, so no confidential data needs to enter the prompt.* + +**6. (Advanced)** "Act as a compliance reviewer: given this current [donor] cost principles excerpt [paste] and our draft budget narrative [paste], flag any language that doesn't align with the excerpt, citing the specific clause." +*Why it works: grounds every flag in pasted source text with a citation requirement, closing off the risk of relying on stale donor-rule memory.* + +**7. (Advanced)** "Draft 3 clauses for a sub-award agreement covering data protection responsibilities, referencing the IASC Data Responsibility principles on confidentiality and personal data protection, for our legal team to review." +*Why it works: names the specific principles being drawn on and explicitly routes the output to legal review rather than treating it as final language.* + +**8. (Advanced)** "Given this partner's publicly available annual report and registration record [paste public excerpts], summarize what's verifiable from public sources and separately list what still needs verification through direct outreach or reference checks." +*Why it works: explicitly separates what's confirmed from public sources from what still needs human follow-up, preventing the summary from reading as a completed due diligence file when it isn't one.* + +## Verify before you act + +- Always paste the actual current donor guidance or RFA text rather than asking AI to recall rules from memory — requirements change per funding round. +- Have a grants specialist verify any AI-generated compliance gap list against the live donor portal before submission. +- Complete required due diligence steps — sanctions screening, reference checks, registration verification — through the actual verification channels. An AI summary of public information is a starting point, not a substitute. +- Confirm every number in a budget narrative against the actual budget file, line by line, before submission. diff --git a/content/playbooks/index.json b/content/playbooks/index.json new file mode 100644 index 0000000..b440621 --- /dev/null +++ b/content/playbooks/index.json @@ -0,0 +1,44 @@ +[ + { + "id": "program-officer", + "title": "Program Officer Playbook", + "role": "Program Officer", + "summary": "Using HAI for program design, proposal drafting, and workplan review — grounded in Sphere, CHS, and live crisis data rather than model memory.", + "file": "program-officer.md" + }, + { + "id": "protection-officer", + "title": "Protection Officer Playbook", + "role": "Protection Officer", + "summary": "Using HAI for protection mainstreaming checks, referral pathway lookups, and template drafting — with a hard line against any identifiable case data.", + "file": "protection-officer.md" + }, + { + "id": "meal-officer", + "title": "MEAL Officer Playbook", + "role": "MEAL Officer", + "summary": "Using HAI for indicator design, survey question review, and qualitative coding assistance across Monitoring, Evaluation, Accountability and Learning work.", + "file": "meal-officer.md" + }, + { + "id": "communications", + "title": "Communications Officer Playbook", + "role": "Communications Officer", + "summary": "Using HAI for sitrep summarization, donor reporting drafts, and multilingual drafting — with every fact traced back to a source before publication.", + "file": "communications.md" + }, + { + "id": "grants-partnerships", + "title": "Grants & Partnerships Officer Playbook", + "role": "Grants & Partnerships Officer", + "summary": "Using HAI for donor compliance checks, budget narrative drafting, and due diligence summaries — always grounded in current guidance text, never model memory of donor rules.", + "file": "grants-partnerships.md" + }, + { + "id": "field-logistics", + "title": "Field Logistics Officer Playbook", + "role": "Field Logistics Officer", + "summary": "Using HAI for procurement document drafting, distribution planning aids, and supply pipeline queries — never for vendor decisions or real-time safety calls.", + "file": "field-logistics.md" + } +] diff --git a/content/playbooks/meal-officer.md b/content/playbooks/meal-officer.md new file mode 100644 index 0000000..9e9d155 --- /dev/null +++ b/content/playbooks/meal-officer.md @@ -0,0 +1,61 @@ +--- +id: meal-officer +title: MEAL Officer Playbook +role: MEAL Officer +icon: 📊 +skill_levels: [beginner, intermediate, advanced] +summary: Using HAI for indicator design, survey question review, and qualitative coding assistance across Monitoring, Evaluation, Accountability and Learning work. +--- + +# MEAL Officer Playbook + +MEAL work depends on getting the mechanics right — a question that isn't leading, an indicator that's actually measurable, a coding frame that holds up against real transcripts. AI is useful for the mechanical checks and the first-draft structure. It is not a substitute for the evidence itself, and it should never see raw respondent data. + +## Where AI genuinely helps in this role + +1. **Cross-checking draft indicators** against Sphere's key indicators for the relevant sector, flagging which are established and which are custom. +2. **Reviewing survey questions** for leading, double-barreled, or otherwise biased phrasing before piloting. +3. **Drafting a first-pass qualitative coding framework** from a research question, to be validated against real anonymized data before use. +4. **Grounding indicator targets in live baseline data** pulled from HDX HAPI rather than an assumed number. +5. **Drafting the structure of a learning brief or lessons-learned summary** from bullet-point inputs, before you fill in the actual findings. + +## Where AI should NOT be used + +- **Never to code actual qualitative transcripts or open-ended survey responses that could contain identifiable information.** Aggregate or fully anonymize first — and if that's not possible, don't use AI on that data at all. +- **Never as the sole validator of an indicator target.** Target-setting needs your actual baseline data and sector judgment, not a generic benchmark. +- **Never to fabricate a completion or impact percentage.** If the real dataset isn't available, ask for the data to be pulled — don't ask AI to estimate a plausible-sounding number as a stand-in. +- **Never to draft final evaluation conclusions or ratings.** Turning evidence into a judgment is the evaluator's job; AI can organize the evidence, not weigh it. +- **Never to translate or adapt a survey instrument into another language without a bilingual reviewer checking cultural and semantic accuracy** — a mistranslated question can silently bias an entire dataset. + +## Example prompts + +**1. (Beginner)** "What indicators does Sphere recommend for measuring food security and nutrition outcomes, and what's the minimum standard for each?" +*Why it works: a direct standards lookup, answerable via `search_standards` with a citation for each indicator.* + +**2. (Beginner)** "Review this survey question for leading or double-barreled phrasing: '[paste question]'" +*Why it works: a narrow, mechanical linguistic check with no data involved — easy to verify the answer yourself.* + +**3. (Intermediate)** "Compare our draft indicator list [paste, no PII] against Sphere's WASH key indicators and tell me which of ours map to an established Sphere indicator versus are custom, citing the Sphere reference for each match." +*Why it works: requires a citation per matched item, so you can spot-check the mapping rather than trust it wholesale.* + +**4. (Intermediate)** "Draft 5 candidate qualitative codes for analyzing responses to the open-ended question 'What barriers did you face accessing [service]?' based on common barrier categories in humanitarian access literature. I'll validate these against our own anonymized transcripts." +*Why it works: explicitly frames the output as a draft to be validated against real data, not a finished coding scheme.* + +**5. (Intermediate)** "Pull the most recent HDX HAPI indicators available for [sector] in [country] and tell me what baseline data exists that we could use to set a realistic indicator target." +*Why it works: uses the live data tool for grounding instead of guessing at a target number from general knowledge.* + +**6. (Advanced)** "Act as a MEAL technical reviewer: read this draft logframe indicator table [paste] and flag every indicator that is not SMART (specific, measurable, achievable, relevant, time-bound), explaining which criterion it fails." +*Why it works: applies a named, consistent framework and bounds the task to flagging, not rewriting — the decision authority stays with you.* + +**7. (Advanced)** "Given this list of already-anonymized, aggregated theme frequencies from our qualitative analysis [paste counts only, no quotes or identifiers], draft a plain-language summary paragraph suitable for a donor report, and flag any theme where the sample size seems too small to generalize." +*Why it works: the input is explicitly restricted to aggregated counts, and the model is asked to flag its own uncertainty about small-N generalization rather than presenting every theme with equal confidence.* + +**8. (Advanced)** "Draft an accountability-to-affected-people feedback loop description for a cash program, referencing CHS commitments 5 and 7, in a format I can hand to a designer." +*Why it works: names the specific commitments by number and specifies the downstream format, avoiding a vague "make this better" ask.* + +## Verify before you act + +- Never paste raw transcripts, open-ended responses, or interview notes that could contain names or locations — aggregate or anonymize first, and skip AI entirely if you can't. +- Validate any AI-suggested coding framework against a sample of real, anonymized data with a second coder before applying it at scale. +- Confirm indicator target numbers against your own baseline dataset — an AI-suggested target is a starting point, not a commitment. +- Have a MEAL technical lead review any SMART-indicator gap list before changing a logframe already agreed with a donor. diff --git a/content/playbooks/program-officer.md b/content/playbooks/program-officer.md new file mode 100644 index 0000000..7275fc6 --- /dev/null +++ b/content/playbooks/program-officer.md @@ -0,0 +1,61 @@ +--- +id: program-officer +title: Program Officer Playbook +role: Program Officer +icon: 🗂️ +skill_levels: [beginner, intermediate, advanced] +summary: Using HAI for program design, proposal drafting, and workplan review — grounded in Sphere, CHS, and live crisis data rather than model memory. +--- + +# Program Officer Playbook + +Program design and proposal writing run on judgment calls AI can't make — what a community actually needs, what's politically feasible, what a donor will fund this cycle. What AI can do is speed up the parts of the job that are mechanical: finding the right standard, summarizing a pile of sitreps, checking a draft against a checklist. Use it there, and keep the judgment calls yours. + +## Where AI genuinely helps in this role + +1. **Drafting first-cut theory-of-change or logframe language** against Sphere and CHS anchors, so you start from a structured draft instead of a blank page. +2. **Summarizing situation reports and crisis updates** (via live ReliefWeb data) into a needs-analysis section, with dates and sources attached so it's checkable. +3. **Cross-checking proposal indicators** against Sphere's key indicators for the relevant sector, flagging which are standard and which are custom. +4. **Turning bullet-point inputs into workplan narrative** — activity descriptions, sequencing rationale — that you then edit for accuracy and tone. +5. **Spotting compliance gaps** between a draft proposal and CHS's nine commitments before a technical reviewer sees it. + +## Where AI should NOT be used + +- **Never for the final go/no-go on program design trade-offs.** Whether to prioritize shelter over WASH in a given context is a contextual and often political judgment — AI can lay out the standards, not make the call. +- **Never with identifiable beneficiary data pasted into a prompt.** Needs data belongs in aggregate; case-level detail doesn't belong in a chat window. +- **Never as the sole word on indicator targets.** Targets need baseline data and sector-specialist sign-off, not a plausible-sounding number. +- **Never for donor-specific compliance rules from memory.** Donor requirements change per call — always work from the actual current guidance document, not what a model "remembers" about a donor. +- **Never to fill a gap in the data.** If a needs or coverage figure isn't available, ask HAI to pull it live or flag it as missing — don't let a plausible-sounding placeholder become the number that ships. + +## Example prompts + +**1. (Beginner)** "Summarize the last 30 days of ReliefWeb situation reports for [country] related to [crisis], and list the top 3 sectoral needs mentioned, with the source and date for each." +*Why it works: specific country and timeframe bound the search; asking for source and date per item makes the summary independently checkable rather than a vague impression.* + +**2. (Beginner)** "What does Sphere specify as the minimum standard for safe drinking water access per person per day, and which technical chapter is it from?" +*Why it works: a direct factual question HAI can answer from `search_standards` with a citation — no ambiguity about what "answer" means.* + +**3. (Intermediate)** "Using CHS commitment 4 (support that does not cause harm) and commitment 1 (participation and voice), draft 3 bullet points for our proposal's do-no-harm section for a WASH project in [context]. Cite the specific commitment language you're drawing from." +*Why it works: names the exact commitments by number, bounds the output to 3 bullets, and requires a citation — so you can verify the language actually traces back to CHS rather than a generic paraphrase.* + +**4. (Intermediate)** "Pull the latest HDX HAPI population and displacement figures for [region] and draft a two-sentence needs-justification paragraph citing the data source and date." +*Why it works: requests a live pull instead of trusting a remembered number, and specifies the exact output length and required citation.* + +**5. (Intermediate)** "Compare this workplan activity list [paste] against Sphere's Shelter and Settlement technical chapter and flag any standard activities we may have missed. Don't assume — cite the section for each flag." +*Why it works: explicit anti-hallucination instruction ("don't assume — cite"), and the task is scoped to flagging gaps, not deciding what to add.* + +**6. (Advanced)** "Act as a reviewer: read this draft proposal narrative [paste, no beneficiary names] and identify every place a numeric claim needs a citation to Sphere, HDX, or ReliefWeb. List them as a table with the paragraph reference." +*Why it works: role framing plus a structured table output narrows the task to exactly one thing — finding uncited numbers — rather than an open-ended edit.* + +**7. (Advanced)** "Draft a one-paragraph theory of change for an integrated nutrition and WASH response in [context], explicitly referencing the linkage between Sphere's WASH and Food Security and Nutrition chapters. Flag anything in your draft that's your inference versus something grounded in a cited source." +*Why it works: asks the model to separate grounded content from its own inference — a check most prompts skip, and one that matters most on cross-sector reasoning.* + +**8. (Advanced)** "Given this indicator table [paste, anonymized], check whether each indicator matches an established Sphere key indicator or is custom, and note which custom indicators would need MEAL sign-off before submission." +*Why it works: bounded classification task that explicitly routes the harder decision — whether a custom indicator is acceptable — back to a human specialist instead of letting the model decide.* + +## Verify before you act + +- Confirm any Sphere or CHS citation against the actual handbook page before it goes into a proposal — retrieval can mis-locate context even when it looks confident. +- Cross-check donor-specific compliance language against the donor's current published guidelines, not a remembered summary — requirements shift by funding round. +- Get sector technical-lead sign-off on any indicator language before submission. +- Re-verify any live data figure (population, displacement, coverage) against the HDX or ReliefWeb source directly before publishing — a pulled number can be stale by the time a proposal is finalized. diff --git a/content/playbooks/protection-officer.md b/content/playbooks/protection-officer.md new file mode 100644 index 0000000..a8dd952 --- /dev/null +++ b/content/playbooks/protection-officer.md @@ -0,0 +1,61 @@ +--- +id: protection-officer +title: Protection Officer Playbook +role: Protection Officer +icon: 🛡️ +skill_levels: [beginner, intermediate, advanced] +summary: Using HAI for protection mainstreaming checks, referral pathway lookups, and template drafting — with a hard line against any identifiable case data. +--- + +# Protection Officer Playbook + +Protection work runs on trust — a survivor's trust that their information stays contained, a community's trust that "confidential" means confidential. That trust is the reason this playbook leads with a boundary, not a use case: **treat any AI chat, including HAI, as a channel that could someday be read by someone other than you.** Never put a real case, a real name, or a real identifying detail into it. Everything below assumes that boundary holds. + +## Where AI genuinely helps in this role + +1. **Looking up referral pathway structure and standards** — what a referral pathway document should contain, what IASC guidance says about timelines — as a starting point, not a live directory. +2. **Checking a program design against protection mainstreaming principles** (Sphere's Protection Principles, CHS commitment 4) using only generic, non-case program details. +3. **Drafting blank case-note or intake templates** — headers and structure only, never filled with real content. +4. **Drafting generic, non-individualized talking points** for explaining complaints and feedback mechanisms to a community. +5. **Summarizing publicly available IASC or cluster guidance** on protection mainstreaming, GBV, or child safeguarding minimum standards. + +## Where AI should NOT be used + +- **Never paste actual beneficiary names, ages, locations, case numbers, or incident details into any prompt** — even a first name plus a village plus an incident type can re-identify someone. If you're unsure whether something counts as identifying, don't paste it. +- **Never for beneficiary eligibility or protection risk-level decisions.** That judgment belongs to a trained case worker who has seen the actual case. +- **Never as the sole source for a live referral pathway.** Pathways change; always confirm the current version with the actual cluster or Area of Responsibility (AoR) focal point before referring someone. +- **Never for safety planning content tied to an individual survivor.** Use AI only for generic protocol or template drafting — a real safety plan is caseworker-only work. +- **Never assume de-identification is complete just because you removed the name.** Age, location, incident type, and date together can still identify a person even with no name attached. + +## Example prompts + +**1. (Beginner)** "What are the minimum actions for protection mainstreaming according to Sphere's Protection Principles?" +*Why it works: a general standards lookup with no case data involved, answerable with a citation.* + +**2. (Beginner)** "Draft a blank case-note template structure — headers only, no example content — covering presenting concern, actions taken, and referral status." +*Why it works: explicitly restricts the request to structure, not content, which keeps identifiable information out by construction.* + +**3. (Intermediate)** "Summarize the IASC guidelines on GBV referral pathway design — what are the core components a referral pathway document should have?" +*Why it works: asks for structural guidance rather than a specific, current pathway with real service names or locations, and is answerable from a citable source.* + +**4. (Intermediate)** "Using CHS commitment 5 (safe reporting of concerns) language, draft 3 generic talking points staff can use to explain our complaints mechanism to a community, without referencing any individual case." +*Why it works: ties the ask to a named commitment and explicitly excludes individual cases from the output.* + +**5. (Intermediate)** "Check this generic distribution plan description [paste, no beneficiary data] against protection mainstreaming principles and flag anything that could create protection risks — crowding, visibility, unsafe access routes." +*Why it works: the input is a general program plan, not case data, and the task is bounded to flagging risks, not adjudicating them.* + +**6. (Advanced)** "Act as a reviewer: given this de-identified, hypothetical case-type scenario (not a real case) [paste a synthetic example], what referral options exist under the cluster system for this kind of situation, and what does IASC guidance say about referral timelines?" +*Why it works: the explicit "hypothetical, not a real case" framing lets you reason about pathway logic without any real person's data ever entering the prompt.* + +**7. (Advanced)** "Draft a one-page protection mainstreaming checklist for a cash assistance program in [context], citing Sphere and CHS sources for each checklist item." +*Why it works: a per-item citation requirement makes every checklist line independently verifiable, and the task needs no case-level data at all.* + +**8. (Advanced)** "Compare our draft child safeguarding SOP [paste, redacted of names] against the CHS commitments and IASC guidance on Protection from Sexual Exploitation and Abuse (PSEA), and list gaps as a table." +*Why it works: names the correct framework (PSEA, not a generic "safeguarding standard"), requires redaction up front, and asks for a structured, checkable gap list.* + +## Verify before you act + +- Before sending anything to AI, re-read it once specifically hunting for names, ages, locations, case numbers, or photos. If in doubt, don't paste it — rephrase generically instead. +- Confirm any referral pathway or service list with the current cluster or AoR focal point — an AI summary can be outdated or too generic to act on. +- Route anything touching an actual case, safety plan, or risk assessment to a supervisor or caseworker. An AI draft is never the case record. +- Re-check PSEA and GBV minimum-standard references against the current IASC guidance version before finalizing an SOP or training — this guidance is periodically revised. From 9bd4a3980b5da8f311ccd6bbd9b486750df8a899 Mon Sep 17 00:00:00 2001 From: Sam Frons Date: Tue, 25 Aug 2026 17:19:49 +0200 Subject: [PATCH 10/46] Add enablement guides and content philosophy README Three skill-progressive guides: effective prompting (anatomy of a good prompt through advanced techniques like role framing and inference flagging), responsible use (grounded in IASC's Operational Guidance on Data Responsibility, with realistic PII near-miss examples), and starting a community of practice (champions, prompt-sharing, office hours, adoption metrics, and the onboarding-vs-custom-build feedback loop). Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01HCrPUhxWVtRBsYSSc7sBHU --- content/README.md | 3 + content/guides/effective-prompting.md | 96 +++++++++++++++++++ content/guides/responsible-use.md | 53 ++++++++++ .../starting-a-community-of-practice.md | 52 ++++++++++ 4 files changed, 204 insertions(+) create mode 100644 content/README.md create mode 100644 content/guides/effective-prompting.md create mode 100644 content/guides/responsible-use.md create mode 100644 content/guides/starting-a-community-of-practice.md diff --git a/content/README.md b/content/README.md new file mode 100644 index 0000000..d26c77b --- /dev/null +++ b/content/README.md @@ -0,0 +1,3 @@ +# Content + +This directory holds HAI's AI-enablement content — playbooks and guides for humanitarian staff adopting the assistant into their daily work. The philosophy behind it: meet staff where they are, at whatever skill level they're starting from, rather than assuming AI fluency; teach through transparency, showing why a prompt works instead of handing over templates to copy blindly; treat adoption as the goal before automation, building trust and good habits with general-purpose prompting before investing engineering effort in custom features; and escalate proven use cases to custom tooling — when a real, repeated need outgrows what a well-crafted prompt can do, that's the signal to build, not a reason to have built it sooner. diff --git a/content/guides/effective-prompting.md b/content/guides/effective-prompting.md new file mode 100644 index 0000000..6247de4 --- /dev/null +++ b/content/guides/effective-prompting.md @@ -0,0 +1,96 @@ +--- +id: effective-prompting +title: Effective Prompting for Humanitarian Work +skill_levels: [beginner, intermediate, advanced] +summary: A skill-progressive guide to writing prompts that get accurate, citable, actionable answers from HAI — with every example drawn from real humanitarian scenarios. +--- + +# Effective Prompting for Humanitarian Work + +A good prompt isn't a magic phrase — it's a clearly scoped question with enough context that the answer can be checked. This guide walks from the basics up to the techniques experienced users rely on daily. Every example uses a real humanitarian scenario, because the habits that work for "summarize this sitrep" are different from the habits that work for "help me plan a response." + +## Beginner: the anatomy of a good prompt + +A prompt that works well usually has three parts: + +1. **What you want** — a specific action (summarize, compare, draft, look up), not a vague one ("help me with"). +2. **The scope** — what's in bounds and what's out. A country, a date range, a document, a sector. +3. **The format** — how you want the answer shaped, so you can use it without reformatting. + +Compare these two prompts: + +> ❌ "Tell me about water standards." +> ✅ "What does Sphere specify as the minimum water quantity per person per day for a general distribution scenario, and which technical chapter is that from?" + +The second version tells HAI exactly what to look up (a specific indicator), where to look (Sphere), and implicitly asks for a citation (the chapter). The first version could produce three paragraphs of generalities that are hard to act on or verify. + +Another comparison: + +> ❌ "Summarize the situation in [country]." +> ✅ "Summarize ReliefWeb updates on [crisis] from the last 7 days into 5 bullet points, each with its source and date." + +Adding a timeframe, a bullet count, and a citation requirement turns an open-ended request into something you can check line by line. + +## Beginner: iteration is normal, not a failure + +The first answer rarely comes back exactly right, and that's fine — treat the first response as a draft, not a verdict. If the scope is too broad, narrow it: "That covered too much — focus only on the WASH-related updates." If the format isn't useful, ask for a different one: "Put that in a table instead of prose." Iterating on a prompt takes seconds and almost always beats trying to write the perfect prompt on the first try. + +## Intermediate: asking for sources + +Every claim HAI makes about a standard, a figure, or a piece of guidance should be traceable to something you can check — either a passage in the standards corpus or a live data pull. Build that into the prompt rather than checking it after the fact: + +> "Using CHS commitment 4 (support that does not cause harm), draft 3 bullet points for our proposal's do-no-harm section for a WASH project in [context]. Cite the specific commitment language you're drawing from." + +> "Pull the latest HDX HAPI displacement figures for [region] and give me the number with its source and date — don't round or estimate." + +If an answer doesn't come with a source, ask for one directly: "Where is that from — can you point me to the specific passage or dataset?" If HAI can't produce one, treat the claim as unverified. + +## Intermediate: structured outputs + +Asking for a specific format does two things: it makes the answer directly usable, and it makes gaps visible. A table with an empty cell is obviously incomplete; a paragraph with a missing fact just reads as vague. + +> "Compare this draft indicator list [paste] against Sphere's WASH key indicators. Output a table with columns: our indicator, matching Sphere indicator (or 'custom'), and the Sphere citation." + +> "List the eligibility criteria and submission deadline from this donor RFA [paste] as a bulleted checklist." + +## Intermediate: multilingual prompting + +HAI can draft in and translate to other languages, but treat the output as a first pass for a fluent-speaker review, not a finished translation — especially for terms with legal, medical, or protection significance where a slightly-off word choice changes meaning. + +> "Translate this program update into French at a reading level appropriate for a general community audience, and flag any phrase you're unsure translates precisely." + +> "Draft this same complaints-mechanism explanation in both English and Swahili, keeping the meaning of 'confidential' and 'safe to report' as close as possible in both — flag any term where a direct translation might lose nuance." + +Asking the model to flag its own uncertainty is more useful than asking it to just produce a confident-sounding translation. + +## Advanced: role framing and bounded tasks + +Telling HAI to act as a specific kind of reviewer narrows what it looks for, which makes the review more useful and easier to check: + +> "Act as a MEAL technical reviewer: read this draft logframe indicator table [paste] and flag every indicator that is not SMART (specific, measurable, achievable, relevant, time-bound), explaining which criterion it fails." + +Notice this prompt does three things advanced users rely on: it names a role, it names a specific framework to check against (SMART), and it bounds the task to *flagging*, not *deciding* — the model surfaces problems, you make the call. + +## Advanced: separating grounded content from inference + +For any task that mixes retrieval (standards, live data) with reasoning (drafting a theory of change, connecting two sectors), ask the model to distinguish what it's citing from what it's inferring: + +> "Draft a one-paragraph theory of change for an integrated nutrition and WASH response in [context], referencing the linkage between Sphere's WASH and Food Security and Nutrition chapters. Flag anything in your draft that's your own inference versus something grounded in a cited source." + +This single instruction — "flag your inference" — is the difference between a draft you can trust at a glance and one you have to re-derive from scratch to check. + +## Advanced: anti-fabrication instructions + +When you're supplying the facts and only want help with structure or language, say so explicitly: + +> "Using only this line-item budget [paste] and this activity description [paste], draft a budget narrative paragraph for the staffing line. Don't invent any figure not in the budget." + +Without that instruction, a model asked to "make this sound more complete" may fill perceived gaps with plausible-sounding numbers. With it, a missing figure becomes a visible gap instead of an invisible guess. + +## A quick checklist before you send a prompt + +- Did I say exactly what I want (summarize / compare / draft / look up)? +- Did I scope it (country, date range, document, sector)? +- Did I specify the format I need back? +- If facts matter, did I ask for sources or restrict the model to data I provided? +- Did I check the response for a citation, not just plausibility? diff --git a/content/guides/responsible-use.md b/content/guides/responsible-use.md new file mode 100644 index 0000000..db57024 --- /dev/null +++ b/content/guides/responsible-use.md @@ -0,0 +1,53 @@ +--- +id: responsible-use +title: Responsible Use of AI in Humanitarian Work +skill_levels: [beginner, intermediate, advanced] +summary: Grounded in the IASC Operational Guidance on Data Responsibility — what never to input, how to verify, and when to escalate to a human. +--- + +# Responsible Use of AI in Humanitarian Work + +This guide is grounded in the IASC's *Operational Guidance on Data Responsibility in Humanitarian Action* (Data Responsibility Working Group, April 2023), which sets out twelve principles humanitarian actors already work under: accountability, confidentiality, coordination and collaboration, data security, defined purpose/necessity/proportionality, fairness and legitimacy, human rights-based approach, people-centered and inclusive, personal data protection, quality, retention and destruction, and transparency — all underpinned by the overarching commitment to do no harm. Using AI doesn't create a new ethics framework. It creates a new place those same principles have to be applied. + +## Never input beneficiary personal data — including near-misses + +The clearest-cut rule: no personally identifiable information (PII) about an affected person goes into an AI prompt. Not a name, not a case number, not a combination of details that could re-identify someone even without a name attached. + +The near-misses are where this actually goes wrong, because they don't feel like a violation in the moment: + +- **"Can you summarize this case list for me?"** — pasting a spreadsheet of household names, ages, and needs to save time on a report. This is a personal data protection breach the moment it's pasted, regardless of what happens to the output. +- **"Help me write up this incident."** — describing a protection incident with the survivor's age, village, and the date, thinking the name is enough to omit. Age plus location plus date is frequently enough to identify someone in a small community. +- **"What's the best way to phrase this feedback from [name]?"** — a single beneficiary's quote, verbatim, dropped into a drafting prompt for a report. Even one identifiable quote is personal data. +- **"Translate this list of GBV survivor testimonies for the annex."** — arguably the highest-risk version of this mistake, combining sensitive personal data with a task (translation) people don't usually flag as a data-handling task at all. + +The test that catches most of these: **if you wouldn't paste it into a public Slack channel, don't paste it into an AI chat.** Aggregate first, anonymize first, or don't use AI for that specific task. + +## Verification is a duty, not a courtesy + +Under the *quality* and *accountability* principles, the people relying on your output — colleagues, donors, affected communities — are trusting that what you tell them is accurate. AI-assisted output doesn't lower that bar; if anything it raises the verification workload, because a fluent, confident-sounding answer is easier to under-check than a rough draft you know needs work. + +Concretely: every number gets checked against its source before it's used in anything that leaves your desk. Every standards citation gets checked against the actual handbook page. Every "the current guidance says" gets checked against the current guidance, because AI can retrieve an outdated or general version of something that's since been revised. + +## Bias awareness in conflict contexts + +Humanitarian information environments are contested by design — parties to a conflict have incentives to shape the narrative, and public data sources reflect whatever got reported, not necessarily what's most severe. An AI system summarizing that environment inherits its skew: if reporting from one area is thinner than another, a summary can understate need there without any signal that it's doing so. + +Two working habits address this directly: + +- Ask explicitly about coverage gaps: "What areas or population groups does this ReliefWeb data likely under-represent?" +- Treat a clean, confident-sounding summary of a contested situation with more suspicion, not less. Reality on the ground is rarely as tidy as a well-written paragraph. + +## When to escalate to a human + +Escalate — meaning, stop and get a person's judgment — whenever a task involves: + +- A decision about an individual's eligibility, risk level, or safety. +- Content that will be published under your organization's name on a politically sensitive topic. +- Any donor compliance question where getting it wrong has financial consequences. +- A situation where AI flags its own uncertainty (a translation it's unsure about, an inference it's separated from a citation) — that flag is a signal to bring in a person, not to accept the best available guess. + +AI producing a well-formatted, confident answer to a question it shouldn't be answering is a known failure mode. The way to catch it is a habit, not a filter: ask "is this a judgment call a trained person needs to make?" before you ask "did AI answer this well?" + +## Environmental and cost awareness: local versus cloud models + +Not every task needs the largest available cloud model. A local, on-device model draws far less power per query and keeps data from leaving your environment at all — which matters both for the retention and destruction principle and for contexts with limited or costly connectivity. As a rough guide: routine, low-stakes drafting and lookups are well suited to smaller or local models; tasks that need the most current live data, the deepest reasoning, or the widest context are where a larger cloud model earns its higher cost and footprint. Match the tool to the task rather than defaulting to the most powerful option out of habit. diff --git a/content/guides/starting-a-community-of-practice.md b/content/guides/starting-a-community-of-practice.md new file mode 100644 index 0000000..d223d86 --- /dev/null +++ b/content/guides/starting-a-community-of-practice.md @@ -0,0 +1,52 @@ +--- +id: starting-a-community-of-practice +title: Starting an AI Community of Practice +skill_levels: [beginner, intermediate] +summary: A practical guide to building adoption through champions, prompt-sharing, and office hours — and feeding real use cases back into engineering priorities. +--- + +# Starting an AI Community of Practice + +Rolling out a tool like HAI doesn't end at the training session. Adoption happens in the weeks after, in the small moments where someone is stuck on a report and doesn't know whether AI could help, or tries it once, gets a mediocre result, and quietly stops. A community of practice is how you catch those moments instead of losing them. + +## The champions model + +Pick 3–6 people across roles — not necessarily the most technical staff, but the ones colleagues already ask for help. A program officer who's genuinely curious and vocal about what worked (and what didn't) does more for adoption than a technical expert who finds the tool obvious and can't articulate why others find it hard. + +Champions' job is small and concrete: use the tool visibly in their own work, answer questions when a colleague gets stuck, and bring back what they're seeing — good and bad — to the group running this community of practice. It's not a training role and shouldn't be treated as extra unpaid work; budget a modest, explicit time allowance (an hour or two a week) so it doesn't quietly compete with their actual job. + +## Prompt-sharing rituals + +The single highest-leverage habit is a shared, low-friction place to post prompts that worked — a channel, a shared doc, whatever your organization already uses for this kind of thing. The habit matters more than the tool. A simple format keeps it useful: + +- The prompt itself +- What it was for +- What made it work (specific to your context, cited a source, restricted to provided data, etc.) + +This does two things at once: it gives newer users a library of starting points instead of a blank page, and it surfaces, over time, which kinds of tasks the tool handles well versus poorly in your organization's actual work — which feeds directly into the feedback loop below. + +## Office hours + +A recurring, short, drop-in session — 30 minutes, same time every week or two — where anyone can bring a real task and work through it live with a champion. Two things make office hours work: + +- **Keep it task-based, not lecture-based.** People show up with an actual report or proposal they're stuck on, not to hear a refresher presentation. +- **Protect it from becoming an escalation queue for tool bugs.** Office hours are for "how do I prompt this well," not "the tool is broken" — route the latter to whoever owns the engineering side, and say so explicitly so people don't lose confidence in the session when a bug shows up. + +## Measuring adoption: leading indicators, not vanity metrics + +Total query volume looks good on a slide and tells you almost nothing about whether adoption is actually taking hold. Watch these instead: + +- **Breadth, not just volume**: how many distinct roles and teams are using it regularly, not just how many queries one enthusiastic person is running. +- **Return usage**: are people coming back to it for a second and third task, or trying it once and stopping? A drop-off after one use is a stronger signal than low volume. +- **Prompt-sharing participation**: are people other than the champions contributing to the shared prompt library? That's a sign the tool has become part of normal workflow, not a champion-only novelty. +- **Unprompted requests for access or training**: someone from a team you haven't onboarded yet asking to join is worth more than a dozen queries from a team that's already in. + +Vanity metrics to be skeptical of: raw query counts, "percentage of staff who attended a training," or any number that goes up just because you told people to use the tool for a week. + +## The feedback loop: onboarding versus custom build + +Not every use case that comes up in office hours or the prompt-sharing channel is done being served by prompting alone. Some of them are the same request showing up over and over, in a form specific enough that it's worth engineering time to build a dedicated feature or tool integration instead of asking every user to reconstruct the same careful prompt from scratch. + +The signal to watch for is repetition with friction: the same task, requested by multiple people, that consistently needs a long or fragile prompt to get right. When you see that pattern, that's the case to escalate to whoever owns HAI's engineering roadmap — not every good prompt, just the ones enough people need often enough that a purpose-built tool would clearly beat teaching everyone the workaround. + +Keep this loop visible: when a use case does get escalated and built, say so back to the community of practice. It closes the loop for the people who raised it, and it reinforces that office hours and prompt-sharing aren't just training exercises — they're a real input into what gets built next. From 79ed9c5c8b1f2a8c908caea3aba0114730692446 Mon Sep 17 00:00:00 2001 From: Sam Frons Date: Tue, 25 Aug 2026 17:20:09 +0200 Subject: [PATCH 11/46] Run HAI on local Ollama and build the chat UI Replace the Anthropic provider with an OpenAI-compatible one behind a single module, so LLM_BASE_URL/LLM_MODEL switch between local Ollama and any hosted compatible endpoint without a code change. Local inference is free, so the per-message cost comment changes to $0.00. Chat UI renders streaming markdown, inline tool activity, and citation chips that open a source drawer. Palette follows OCHA/UN products: one institutional blue, amber reserved for advisory notices. Also correct three HDX HAPI read bugs found against live data: baseline population summed the aggregate rows alongside their own breakdowns and reported 4x the real figure; funding surfaced unnamed forward-year pledge rows as current appeals; needs-by-sector collapsed to whichever sector reported last. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01HCrPUhxWVtRBsYSSc7sBHU --- app/.env.example | 20 ++- app/package.json | 2 +- app/pnpm-lock.yaml | 30 ++-- app/src/app/api/chat/route.ts | 16 +- app/src/app/globals.css | 183 +++++++++++++++++++-- app/src/app/layout.tsx | 5 +- app/src/app/page.tsx | 68 +------- app/src/components/chat.tsx | 147 +++++++++++++++++ app/src/components/citations.tsx | 42 +++++ app/src/components/composer.tsx | 75 +++++++++ app/src/components/empty-state.tsx | 59 +++++++ app/src/components/markdown.tsx | 21 +++ app/src/components/source-panel.tsx | 87 ++++++++++ app/src/components/sources.ts | 57 +++++++ app/src/components/tool-activity.tsx | 75 +++++++++ app/src/lib/llm/provider.ts | 50 ++++++ app/src/lib/tools/crisis-updates.ts | 219 +++++++++++++------------ app/src/lib/tools/humanitarian-data.ts | 70 ++++++-- 18 files changed, 996 insertions(+), 230 deletions(-) create mode 100644 app/src/components/chat.tsx create mode 100644 app/src/components/citations.tsx create mode 100644 app/src/components/composer.tsx create mode 100644 app/src/components/empty-state.tsx create mode 100644 app/src/components/markdown.tsx create mode 100644 app/src/components/source-panel.tsx create mode 100644 app/src/components/sources.ts create mode 100644 app/src/components/tool-activity.tsx create mode 100644 app/src/lib/llm/provider.ts diff --git a/app/.env.example b/app/.env.example index 8f5a3b7..a4cb215 100644 --- a/app/.env.example +++ b/app/.env.example @@ -1,5 +1,11 @@ -# Claude API access for /api/chat -ANTHROPIC_API_KEY= +# Chat model, via any OpenAI-compatible endpoint. +# Defaults below run entirely on local Ollama — no key, no per-token cost. +# Swap LLM_BASE_URL for a hosted OpenAI-compatible endpoint (Groq, Gemini's +# compatibility layer, OpenRouter) without touching code. Check that +# endpoint's pricing first: hosted inference may bill per token. +LLM_BASE_URL=http://localhost:11434/v1 +LLM_MODEL=qwen2.5:14b +LLM_API_KEY=ollama # Voyage AI embeddings for corpus ingestion + search_standards tool VOYAGE_API_KEY= @@ -13,6 +19,14 @@ SUPABASE_SERVICE_ROLE_KEY= NEXT_PUBLIC_SUPABASE_URL= NEXT_PUBLIC_SUPABASE_ANON_KEY= +# Optional. ReliefWeb's JSON API requires an appname pre-approved by OCHA +# (https://apidoc.reliefweb.int/parameters#appname). Without it, crisis_updates +# falls back to IFRC GO, which needs no registration. +RELIEFWEB_APPNAME= + +# Optional. HDX HAPI attribution string: base64 of "appname:email". +# Defaults to a demo identifier; no registration required either way. +HDX_APP_IDENTIFIER= + # Cost / abuse controls -DAILY_SPEND_CAP_USD= RATE_LIMIT_RPM= diff --git a/app/package.json b/app/package.json index e985fe8..ff65db2 100644 --- a/app/package.json +++ b/app/package.json @@ -9,7 +9,7 @@ "lint": "eslint" }, "dependencies": { - "@ai-sdk/anthropic": "^4.0.42", + "@ai-sdk/openai-compatible": "^3.0.37", "@ai-sdk/react": "^4.0.82", "ai": "^7.0.79", "next": "16.3.2", diff --git a/app/pnpm-lock.yaml b/app/pnpm-lock.yaml index f783817..d8f18c2 100644 --- a/app/pnpm-lock.yaml +++ b/app/pnpm-lock.yaml @@ -8,9 +8,9 @@ importers: .: dependencies: - '@ai-sdk/anthropic': - specifier: ^4.0.42 - version: 4.0.42(zod@4.4.3) + '@ai-sdk/openai-compatible': + specifier: ^3.0.37 + version: 3.0.37(zod@4.4.3) '@ai-sdk/react': specifier: ^4.0.82 version: 4.0.82(react@19.2.8)(zod@4.4.3) @@ -63,12 +63,6 @@ importers: packages: - '@ai-sdk/anthropic@4.0.42': - resolution: {integrity: sha512-U3RJDo2/rWdH6SjSbA2Owd30ZawKwt4zFTqteTTn6Po0vEW1ec1KXf6s1nwNE3mxCqKrQWEavU21C46AfGYaeg==} - engines: {node: '>=22'} - peerDependencies: - zod: ^3.25.76 || ^4.1.8 - '@ai-sdk/gateway@4.0.64': resolution: {integrity: sha512-iayK1z11I0b3sgbP3j6eKQypfpwO5Fbtaq1Mkwv7YxwGQG1YTNr5QAjyYsSfqyf/58WUOLWz2cTBpk+ExZVgpw==} engines: {node: '>=22'} @@ -81,6 +75,12 @@ packages: peerDependencies: zod: ^3.25.76 || ^4.1.8 + '@ai-sdk/openai-compatible@3.0.37': + resolution: {integrity: sha512-X9LfyiV73cyT89GSKCDUsCZ8nV6wZAn6elJJ4CzZF1WRdofBvAjWz5CL1VHj7jeR5ahiqsWml6X/B8vUDgiXtg==} + engines: {node: '>=22'} + peerDependencies: + zod: ^3.25.76 || ^4.1.8 + '@ai-sdk/provider-utils@5.0.30': resolution: {integrity: sha512-9TyxUXolql77ntHIeygLqt7PO1O0HZPB73cJhLG2OsPecSBRIZhNXLxT1T7rlL6Zpm1eDoLMFrMV99kAJ28/Sg==} engines: {node: '>=22'} @@ -2366,12 +2366,6 @@ packages: snapshots: - '@ai-sdk/anthropic@4.0.42(zod@4.4.3)': - dependencies: - '@ai-sdk/provider': 4.0.8 - '@ai-sdk/provider-utils': 5.0.30(zod@4.4.3) - zod: 4.4.3 - '@ai-sdk/gateway@4.0.64(zod@4.4.3)': dependencies: '@ai-sdk/provider': 4.0.8 @@ -2387,6 +2381,12 @@ snapshots: pkce-challenge: 5.0.1 zod: 4.4.3 + '@ai-sdk/openai-compatible@3.0.37(zod@4.4.3)': + dependencies: + '@ai-sdk/provider': 4.0.8 + '@ai-sdk/provider-utils': 5.0.30(zod@4.4.3) + zod: 4.4.3 + '@ai-sdk/provider-utils@5.0.30(zod@4.4.3)': dependencies: '@ai-sdk/provider': 4.0.8 diff --git a/app/src/app/api/chat/route.ts b/app/src/app/api/chat/route.ts index c467b0e..cc918c9 100644 --- a/app/src/app/api/chat/route.ts +++ b/app/src/app/api/chat/route.ts @@ -1,4 +1,3 @@ -import { anthropic } from '@ai-sdk/anthropic'; import { convertToModelMessages, stepCountIs, @@ -8,13 +7,15 @@ import { type UIMessage, } from 'ai'; +import { getChatModel } from '@/lib/llm/provider'; import { SYSTEM_PROMPT } from '@/lib/prompts/system'; import { haiTools } from '@/lib/tools'; export const dynamic = 'force-dynamic'; -export const maxDuration = 60; +export const maxDuration = 120; -// cost: ~$0.003-0.03 per message (Claude Sonnet input+output, varies with tool loops) +// cost: $0.00 per message — inference runs locally through Ollama by default. +// Pointing LLM_BASE_URL at a hosted endpoint may introduce per-token billing. /** Shared with the client so message parts are typed against the real tools. */ export type HaiUIMessage = UIMessage>; @@ -70,13 +71,6 @@ export async function POST(request: Request) { ); } - if (!process.env.ANTHROPIC_API_KEY) { - return Response.json( - { error: 'ANTHROPIC_API_KEY is not configured on the server.' }, - { status: 500 }, - ); - } - let messages: HaiUIMessage[]; try { ({ messages } = (await request.json()) as { messages: HaiUIMessage[] }); @@ -89,7 +83,7 @@ export async function POST(request: Request) { } const result = streamText({ - model: anthropic('claude-sonnet-5'), + model: getChatModel(), system: SYSTEM_PROMPT, messages: await convertToModelMessages(messages), tools: haiTools, diff --git a/app/src/app/globals.css b/app/src/app/globals.css index a2dc41e..423e0b9 100644 --- a/app/src/app/globals.css +++ b/app/src/app/globals.css @@ -1,26 +1,189 @@ @import "tailwindcss"; +/* + * HAI's palette. The reference points are OCHA and UN humanitarian products: + * near-neutral greys carrying the page, one deep institutional blue as the + * only saturated colour, and amber reserved for advisory notices so it keeps + * its signal value. Nothing here competes with the content. + */ :root { - --background: #ffffff; - --foreground: #171717; + --background: #fbfbfa; + --surface: #ffffff; + --surface-muted: #f4f4f2; + --border: #e2e2de; + --border-strong: #c9c9c3; + + --foreground: #1b1c1e; + --muted: #5f6367; + --subtle: #85898d; + + --accent: #1c4f9c; + --accent-hover: #17407f; + --accent-soft: #eaf0f9; + --accent-border: #c3d3ec; + + --notice: #8a5a10; + --notice-soft: #fdf5e6; + --notice-border: #ecd9ae; +} + +@media (prefers-color-scheme: dark) { + :root { + --background: #131416; + --surface: #1a1c1f; + --surface-muted: #212428; + --border: #2c3035; + --border-strong: #3d4249; + + --foreground: #e8e9ea; + --muted: #a2a7ad; + --subtle: #7d838a; + + --accent: #7aa7e8; + --accent-hover: #94baf0; + --accent-soft: #1a2436; + --accent-border: #2d456b; + + --notice: #e0b45f; + --notice-soft: #241d10; + --notice-border: #4a3c1c; + } } @theme inline { --color-background: var(--background); + --color-surface: var(--surface); + --color-surface-muted: var(--surface-muted); + --color-border-subtle: var(--border); + --color-border-strong: var(--border-strong); + --color-foreground: var(--foreground); + --color-muted: var(--muted); + --color-subtle: var(--subtle); + + --color-accent: var(--accent); + --color-accent-hover: var(--accent-hover); + --color-accent-soft: var(--accent-soft); + --color-accent-border: var(--accent-border); + + --color-notice: var(--notice); + --color-notice-soft: var(--notice-soft); + --color-notice-border: var(--notice-border); + --font-sans: var(--font-geist-sans); --font-mono: var(--font-geist-mono); } -@media (prefers-color-scheme: dark) { - :root { - --background: #0a0a0a; - --foreground: #ededed; - } -} - body { background: var(--background); color: var(--foreground); - font-family: Arial, Helvetica, sans-serif; + font-family: var(--font-geist-sans), ui-sans-serif, system-ui, sans-serif; + -webkit-font-smoothing: antialiased; +} + +/* Tool activity indicator: a slow pulse, not a spinner — the work takes + seconds, and a fast animation reads as urgency the content doesn't have. */ +@keyframes hai-pulse { + 0%, 100% { opacity: 0.35; } + 50% { opacity: 1; } +} + +.hai-pulse { + animation: hai-pulse 1.6s ease-in-out infinite; +} + +@media (prefers-reduced-motion: reduce) { + .hai-pulse { + animation: none; + opacity: 0.7; + } +} + +/* Prose defaults for streamed markdown. Scoped tight so the assistant's + answers stay dense enough to scan under time pressure. */ +.hai-prose { + line-height: 1.65; +} + +.hai-prose > * + * { + margin-top: 0.85rem; +} + +.hai-prose h1, +.hai-prose h2, +.hai-prose h3 { + font-weight: 600; + letter-spacing: -0.01em; + margin-top: 1.5rem; + line-height: 1.3; +} + +.hai-prose h1 { font-size: 1.15rem; } +.hai-prose h2 { font-size: 1.05rem; } +.hai-prose h3 { font-size: 0.975rem; } + +.hai-prose ul, +.hai-prose ol { + padding-left: 1.35rem; +} + +.hai-prose ul { list-style: disc; } +.hai-prose ol { list-style: decimal; } + +.hai-prose li + li { margin-top: 0.3rem; } + +.hai-prose a { + color: var(--accent); + text-decoration: underline; + text-underline-offset: 2px; +} + +.hai-prose code { + font-family: var(--font-geist-mono), ui-monospace, monospace; + font-size: 0.875em; + background: var(--surface-muted); + border: 1px solid var(--border); + border-radius: 4px; + padding: 0.1em 0.35em; +} + +.hai-prose pre { + background: var(--surface-muted); + border: 1px solid var(--border); + border-radius: 8px; + padding: 0.85rem 1rem; + overflow-x: auto; +} + +.hai-prose pre code { + background: none; + border: none; + padding: 0; +} + +.hai-prose blockquote { + border-left: 3px solid var(--accent-border); + padding-left: 0.9rem; + color: var(--muted); +} + +.hai-prose table { + width: 100%; + border-collapse: collapse; + font-size: 0.9em; + display: block; + overflow-x: auto; +} + +.hai-prose th, +.hai-prose td { + border: 1px solid var(--border); + padding: 0.45rem 0.7rem; + text-align: left; + vertical-align: top; +} + +.hai-prose th { + background: var(--surface-muted); + font-weight: 600; } diff --git a/app/src/app/layout.tsx b/app/src/app/layout.tsx index 9852c15..fc59334 100644 --- a/app/src/app/layout.tsx +++ b/app/src/app/layout.tsx @@ -13,8 +13,9 @@ const geistMono = Geist_Mono({ }); export const metadata: Metadata = { - title: "Create Next App", - description: "Generated by create next app", + title: "HAI — Humanitarian operations assistant", + description: + "Guidance grounded in the Sphere Handbook, the Core Humanitarian Standard, and IASC guidance, with live crisis data and cited sources.", }; export default function RootLayout({ children }: LayoutProps<"/">) { diff --git a/app/src/app/page.tsx b/app/src/app/page.tsx index c887311..4ebb73d 100644 --- a/app/src/app/page.tsx +++ b/app/src/app/page.tsx @@ -1,69 +1,5 @@ -import Image from "next/image"; +import { Chat } from '@/components/chat'; export default function Home() { - return ( -
    -
    - Next.js logo -
    -

    - To get started, edit the{" "} - - page.tsx - {" "} - file. -

    -

    - Looking for a starting point or more instructions? Head over to{" "} - - Templates - {" "} - or the{" "} - - Learning - {" "} - center. -

    -
    - -
    -
    - ); + return ; } diff --git a/app/src/components/chat.tsx b/app/src/components/chat.tsx new file mode 100644 index 0000000..4e13188 --- /dev/null +++ b/app/src/components/chat.tsx @@ -0,0 +1,147 @@ +'use client'; + +import { useChat } from '@ai-sdk/react'; +import { DefaultChatTransport } from 'ai'; +import { useCallback, useEffect, useRef, useState } from 'react'; + +import type { HaiUIMessage } from '@/app/api/chat/route'; +import { Citations } from './citations'; +import { Composer } from './composer'; +import { EmptyState } from './empty-state'; +import { Markdown } from './markdown'; +import { SourcePanel } from './source-panel'; +import { collectRetrievalNotice, collectSources, type Source } from './sources'; +import { ToolActivity } from './tool-activity'; + +export function Chat() { + const [input, setInput] = useState(''); + const [activeSource, setActiveSource] = useState(null); + const scrollAnchor = useRef(null); + + const { messages, sendMessage, status, stop, error } = useChat({ + transport: new DefaultChatTransport({ api: '/api/chat' }), + }); + + const busy = status === 'submitted' || status === 'streaming'; + + useEffect(() => { + scrollAnchor.current?.scrollIntoView({ behavior: 'smooth', block: 'end' }); + }, [messages, status]); + + const send = useCallback( + (text: string) => { + const trimmed = text.trim(); + if (!trimmed) return; + setInput(''); + void sendMessage({ text: trimmed }); + }, + [sendMessage], + ); + + return ( +
    +
    +
    + + HAI + + + Humanitarian operations assistant + +
    +
    + +
    + {messages.length === 0 ? ( + + ) : ( +
    + {messages.map((message) => ( + + ))} +
    + )} + + {error ? ( +

    + {error.message || + 'The assistant could not complete that request. Try again.'} +

    + ) : null} + +
    +
    + +
    +
    + send(input)} + onStop={stop} + busy={busy} + /> +

    + HAI provides guidance grounded in humanitarian standards. It does not + replace professional judgment. +

    +
    +
    + + setActiveSource(null)} /> +
    + ); +} + +function MessageBlock({ + message, + onSelectSource, +}: { + message: HaiUIMessage; + onSelectSource: (source: Source) => void; +}) { + if (message.role === 'user') { + const text = message.parts + .filter((part) => part.type === 'text') + .map((part) => part.text) + .join(''); + + return ( +
    +
    +

    {text}

    +
    +
    + ); + } + + const sources = collectSources(message); + const notice = collectRetrievalNotice(message); + + return ( +
    + {message.parts.map((part, index) => { + if (part.type === 'text') { + return ( +
    + {part.text} +
    + ); + } + if (part.type.startsWith('tool-')) { + return ; + } + return null; + })} + + +
    + ); +} diff --git a/app/src/components/citations.tsx b/app/src/components/citations.tsx new file mode 100644 index 0000000..74d01b8 --- /dev/null +++ b/app/src/components/citations.tsx @@ -0,0 +1,42 @@ +'use client'; + +import { sourceLabel, type Source } from './sources'; + +export function Citations({ + sources, + notice, + onSelect, +}: { + sources: Source[]; + notice?: string; + onSelect: (source: Source) => void; +}) { + if (sources.length === 0) { + // A notice with no passages is the honest case worth surfacing: the model + // tried to ground the answer and the corpus had nothing to give it. + return notice ? ( +

    + {notice} +

    + ) : null; + } + + return ( +
    + Sources + {sources.map((source) => ( + + ))} +
    + ); +} diff --git a/app/src/components/composer.tsx b/app/src/components/composer.tsx new file mode 100644 index 0000000..1c6b61f --- /dev/null +++ b/app/src/components/composer.tsx @@ -0,0 +1,75 @@ +'use client'; + +import { useEffect, useRef, type FormEvent, type KeyboardEvent } from 'react'; + +export function Composer({ + value, + onChange, + onSubmit, + onStop, + busy, +}: { + value: string; + onChange: (value: string) => void; + onSubmit: () => void; + onStop: () => void; + busy: boolean; +}) { + const textareaRef = useRef(null); + + // Grow with the content instead of scrolling inside a fixed box — field + // questions are often several lines long. + useEffect(() => { + const el = textareaRef.current; + if (!el) return; + el.style.height = 'auto'; + el.style.height = `${Math.min(el.scrollHeight, 200)}px`; + }, [value]); + + const submit = (event: FormEvent) => { + event.preventDefault(); + if (busy || !value.trim()) return; + onSubmit(); + }; + + const onKeyDown = (event: KeyboardEvent) => { + if (event.key === 'Enter' && !event.shiftKey) { + event.preventDefault(); + if (!busy && value.trim()) onSubmit(); + } + }; + + return ( +
    +
    +