From 021b5832c11ede5b1bf9d276f96779b7302b94ab Mon Sep 17 00:00:00 2001 From: Travis Dent Date: Wed, 21 Jan 2026 15:11:37 -0800 Subject: [PATCH 1/8] Replace hand-rolled YAML parser with serde_yaml in build.rs Build dependencies don't affect the final binary - no reason to avoid them. The previous implementation was fragile and would break on formatting changes. --- .codey/transcripts/000000.json | 452 +++++++++++++++++++++++++++++++++ validator/Cargo.toml | 4 + validator/build.rs | 114 ++------- 3 files changed, 482 insertions(+), 88 deletions(-) create mode 100644 .codey/transcripts/000000.json diff --git a/.codey/transcripts/000000.json b/.codey/transcripts/000000.json new file mode 100644 index 0000000..2952a80 --- /dev/null +++ b/.codey/transcripts/000000.json @@ -0,0 +1,452 @@ +{ + "turns": [ + { + "id": 0, + "role": "assistant", + "content": [ + { + "type": "TextBlock", + "text": "Welcome to Codey! I'm your AI coding assistant. How can I help you today?", + "status": "pending" + } + ], + "timestamp": "2026-01-21T23:02:17.557799Z" + }, + { + "id": 1, + "role": "user", + "content": [ + { + "type": "TextBlock", + "text": "ok i developed this entrely using background agents so i'm not sure of the code quality. let's spend soem time getting familair with this", + "status": "complete" + } + ], + "timestamp": "2026-01-21T23:02:43.035488Z" + }, + { + "id": 2, + "role": "assistant", + "content": [ + { + "type": "ThinkingBlock", + "text": "The user wants to explore and understand the codebase in their project directory at /Users/tcdent/.config/codey. They developed it using background agents and want to review the code quality. Let me start by exploring the directory structure to understand what we're working with.", + "status": "complete" + }, + { + "type": "TextBlock", + "text": "Let me explore the project structure to get a sense of what we're working with.", + "status": "complete" + }, + { + "type": "ShellBlock", + "call_id": "toolu_01EWu32oXUWYrFPQbw5LWZtb", + "tool_name": "mcp_shell", + "params": { + "command": "ls -la" + }, + "status": "complete", + "text": "total 64\ndrwxr-xr-x@ 14 tcdent staff 448 Jan 21 15:02 .\ndrwxr-xr-x 180 tcdent staff 5760 Jan 21 10:11 ..\ndrwxr-xr-x@ 3 tcdent staff 96 Jan 21 15:02 .codey\ndrwxr-xr-x@ 12 tcdent staff 384 Jan 21 10:11 .git\ndrwxr-xr-x@ 3 tcdent staff 96 Jan 21 10:11 .github\n-rw-r--r--@ 1 tcdent staff 437 Jan 21 10:11 .gitignore\n-rw-r--r--@ 1 tcdent staff 6980 Jan 21 10:11 README.md\n-rw-r--r--@ 1 tcdent staff 10738 Jan 21 10:11 SPEC.md\ndrwxr-xr-x@ 7 tcdent staff 224 Jan 21 10:11 agent\ndrwxr-xr-x@ 9 tcdent staff 288 Jan 21 10:11 evals\n-rw-r--r--@ 1 tcdent staff 1461 Jan 21 10:11 example.wvf\ndrwxr-xr-x@ 5 tcdent staff 160 Jan 21 10:11 spec\n-rw-r--r--@ 1 tcdent staff 2487 Jan 21 10:11 system.md\ndrwxr-xr-x@ 6 tcdent staff 192 Jan 21 10:11 validator\n", + "background": false + }, + { + "type": "ShellBlock", + "call_id": "toolu_01EVxQx3x3EhvYPMeTugCLRM", + "tool_name": "mcp_shell", + "params": { + "command": "find . -type f -name \"*.py\" -o -name \"*.rs\" -o -name \"*.toml\" -o -name \"*.json\" -o -name \"*.yaml\" -o -name \"*.yml\" -o -name \"*.md\" 2>/dev/null | head -50" + }, + "status": "complete", + "text": "./evals/write_eval/runner.py\n./evals/write_eval/__init__.py\n./evals/write_eval/test_cases.py\n./evals/write_eval/evaluator.py\n./evals/__init__.py\n./evals/common/config.py\n./evals/common/llm_clients.py\n./evals/common/__init__.py\n./evals/cli.py\n./evals/read_eval/runner.py\n./evals/read_eval/worldview_prompt.py\n./evals/read_eval/__init__.py\n./evals/read_eval/test_cases.py\n./evals/read_eval/evaluator.py\n./evals/__main__.py\n./validator/Cargo.toml\n./validator/build.rs\n./validator/src/lib.rs\n./validator/src/main.rs\n./spec/generate.py\n./spec/tokens.yaml\n./agent/Cargo.toml\n./agent/src/main.rs\n./.codey/transcripts/000000.json\n./README.md\n./.github/workflows/evals.yml\n./.github/workflows/build.yml\n./system.md\n./SPEC.md\n", + "background": false + }, + { + "type": "ThinkingBlock", + "text": "This looks like a multi-component project with:\n1. A Rust validator\n2. A Rust agent\n3. Python evals\n4. Some spec files\n5. A worldview file format (.wvf)\n\nLet me dive deeper into each component to understand the architecture.", + "status": "complete" + }, + { + "type": "TextBlock", + "text": "Interesting! This looks like a multi-component project with:\n- **Rust components**: `validator/` and `agent/`\n- **Python evals**: `evals/`\n- **Spec files**: `spec/` with YAML definitions\n- A custom `.wvf` file format\n\nLet me get a better understanding of what this project does by reading the key docs and examining the code structure:", + "status": "complete" + }, + { + "type": "ReadFileBlock", + "call_id": "toolu_01EhuVJ5uqv3PCyoGiei1njy", + "tool_name": "mcp_read_file", + "params": { + "path": "README.md" + }, + "status": "complete", + "text": " 1│# Worldview\n 2│\n 3│A compact notation format for encoding and maintaining conceptual worldviews over time, designed for LLM context persistence.\n 4│\n 5│## Overview\n 6│\n 7│The **Worldview format** (file extension: `.wvf`) is a declarative notation for storing beliefs, stances, and understanding about concepts. Unlike retrieval-augmented generation (RAG) which selectively includes information, Worldview documents are designed to remain *entirely in context* across all LLM interactions.\n 8│\n 9│This solves a fundamental problem: LLMs have foundational beliefs and stances that should inform all reasoning, not just topically-matched queries. The Worldview format is dense enough that an entire belief system can remain in context permanently.\n 10│\n 11│## Quick Start\n 12│\n 13│### Example Document\n 14│\n 15│```wvf\n 16│Trust\n 17│ .formation\n 18│ - slow\n 19│ - requires consistency | over time\n 20│ - contextual @personal-experience\n 21│ .erosion\n 22│ - fast !\n 23│ - single violation => collapse?\n 24│ - asymmetric vs formation &Trust.formation\n 25│```\n 26│\n 27│\n 28│## Language Specification\n 29│\n 30│### Grammar\n 31│\n 32│```ebnf\n 33│document = concept+ ;\n 34│concept = concept_name NEWLINE facet+ ;\n 35│facet = INDENT(2) '.' facet_name NEWLINE claim+ ;\n 36│claim = INDENT(4) '-' claim_body [condition*] [source*] [reference*] ;\n 37│\n 38│condition = '|' text ;\n 39│source = '@' identifier ;\n 40│reference = '&' concept_name ['.' facet_name] ;\n 41│```\n 42│\n 43│### Structure\n 44│\n 45│```\n 46│Concept (unindented, bare text)\n 47│ .facet (2-space indent, dot prefix)\n 48│ - claim (4-space indent, dash prefix)\n 49│```\n 50│\n 51│### Inline Elements\n 52│\n 53│| Symbol | Name | Description |\n 54│|--------|------|-------------|\n 55│| `|` | condition | when/if applies |\n 56│| `@` | source | basis for belief |\n 57│| `&` | reference | links to other concept.facet |\n 58│\n 59│### Brief Forms\n 60│\n 61│Compact operators for common relationships:\n 62│\n 63│| Symbol | Meaning | Example |\n 64│|--------|---------|---------|\n 65│| `=>` | causes, leads to | `power => corruption` |\n 66│| `<=` | caused by, results from | `trust <= consistency` |\n 67│| `<>` | mutual, bidirectional | `accountability <> trust` |\n 68│| `><` | tension, conflicts with | `efficiency >< thoroughness` |\n 69│| `~` | similar to, resembles | `authority ~ influence` |\n 70│| `=` | equivalent to, means | `formal = official` |\n 71│| `vs` | in contrast to | `asymmetric vs formation` |\n 72│| `//` | regardless of | `self-perpetuate // original purpose` |\n 73│\n 74│### Modifiers\n 75│\n 76│Suffix markers that inflect claim meaning:\n 77│\n 78│| Symbol | Meaning | Example |\n 79│|--------|---------|---------|\n 80│| `^` | increasing, trending up | `concentration^` |\n 81│| `v` | decreasing, trending down | `trust v` |\n 82│| `!` | strong, emphatic, high confidence | `fast !` |\n 83│| `?` | uncertain, contested, tentative | `free-will?` |\n 84│| `*` | notable, important, flagged | `paradigm-shift*` |\n 85│\n 86│### Evolution\n 87│\n 88│Supersession marker `[<= prior belief]` indicates a belief that replaces a prior one:\n 89│\n 90│```\n 91│- adaptive [<= inherently good]\n 92│```\n 93│\n 94│### Claim Syntax\n 95│\n 96│Claims follow positional grammar—position implies role:\n 97│\n 98│```\n 99│- claim_text | condition @source &reference\n 100│```\n 101│\n 102│\n 103│\n 104│## Tools\n 105│\n 106│This repository includes a complete toolchain for working with Worldview documents:\n 107│\n 108│### Validator (`worldview-validate`)\n 109│\n 110│Validates `.wvf` files for syntactic correctness.\n 111│\n 112│```bash\n 113│# Validate a file\n 114│worldview-validate example.wvf\n 115│\n 116│# Validate from stdin\n 117│cat example.wvf | worldview-validate --stdin\n 118│```\n 119│\n 120│### Agent CLI (`worldview`)\n 121│\n 122│An AI-powered tool that converts plain-text statements into proper Worldview notation.\n 123│\n 124│```bash\n 125│# Add a fact to a Worldview file\n 126│worldview \"Trust is built slowly through consistent actions\" --file worldview.wvf\n 127│\n 128│# Use a specific model\n 129│worldview \"Power corrupts when unchecked\" --model claude-opus-4-5-20251101\n 130│```\n 131│\n 132│The agent understands the full Worldview specification and will:\n 133│1. Read existing content to understand structure\n 134│2. Determine appropriate concept/facet placement\n 135│3. Format statements using proper notation\n 136│4. Validate before writing\n 137│\n 138│### Evaluation Framework\n 139│\n 140│A Python framework for testing how well LLMs can leverage Worldview-encoded beliefs.\n 141│\n 142│```bash\n 143│# Install dependencies\n 144│pip install -r evals/requirements.txt\n 145│\n 146│# Run evaluations\n 147│python -m evals run --models claude-sonnet gpt-5.2\n 148│\n 149│# Run specific difficulty\n 150│python -m evals run --difficulty extreme\n 151│\n 152│# List available models and test cases\n 153│python -m evals list-models\n 154│python -m evals list-cases\n 155│```\n 156│\n 157│## Project Structure\n 158│\n 159│```\n 160│wvf/\n 161│├── spec/ # Canonical specification\n 162││ ├── tokens.yaml # Token definitions (source of truth)\n 163││ ├── grammar.pest # PEG grammar\n 164││ └── generate.py # Generates docs and code from tokens.yaml\n 165│├── validator/ # Rust validator\n 166││ ├── src/lib.rs # Validation library\n 167││ ├── src/main.rs # CLI tool\n 168││ └── build.rs # Generates tokens from spec at compile time\n 169│├── agent/ # Rust agent CLI\n 170││ └── src/main.rs # LLM-powered notation converter\n 171│├── evals/ # Python evaluation framework\n 172││ ├── cli.py # Evaluation CLI\n 173││ ├── runner.py # Test orchestration\n 174││ ├── evaluator.py # Response scoring\n 175││ ├── test_cases.py # Test case definitions\n 176││ └── llm_clients.py # LLM provider clients\n 177│├── SPEC.md # Full format specification\n 178│├── system.md # Condensed system prompt (generated)\n 179│└── example.wvf # Example document\n 180│```\n 181│\n 182│## Building\n 183│\n 184│### Prerequisites\n 185│\n 186│- Rust 1.70+\n 187│- Python 3.11+\n 188│\n 189│### Build Tools\n 190│\n 191│```bash\n 192│# Build validator\n 193│cd validator && cargo build --release\n 194│\n 195│# Build agent (requires setup for vendored dependencies)\n 196│cd agent && ./setup.sh && cargo build --release\n 197│```\n 198│\n 199│## Design Principles\n 200│\n 201│The Worldview format follows five core principles:\n 202│\n 203│1. **State over narrative**: Capture what is believed, not how it came to be believed\n 204│2. **Predictability allows omission**: If structure makes something inferable, don't write it\n 205│3. **Conflict tolerance**: Real worldviews contain tensions—hold them without forcing resolution\n 206│4. **Freeform vocabulary**: Structure is defined; content remains unconstrained\n 207│5. **LLM-native, human-inspectable**: Optimized for machine parsing while remaining readable\n 208│\n 209│## Why \"Worldview\"?\n 210│\n 211│The name emphasizes the core use case: encoding *how concepts are understood* rather than just storing facts. This is different from:\n 212│\n 213│- **Knowledge bases**: Store facts, not interpretations\n 214│- **RAG systems**: Retrieve relevant content per query\n 215│- **Memory systems**: Log events chronologically\n 216│\n 217│Worldview documents capture the *lens* through which all subsequent reasoning should be filtered.\n 218│\n 219│## File Extension\n 220│\n 221│- **Extension**: `.wvf` (Worldview Format)\n 222│- **MIME type**: `text/x-worldview` (proposed)\n 223│\n 224│## License\n 225│\n 226│MIT\n 227│\n 228│## Specification\n 229│\n 230│See [SPEC.md](SPEC.md) for the complete format specification.\n", + "background": false + }, + { + "type": "ReadFileBlock", + "call_id": "toolu_016iy6Fd2mqSLurEstDQcm8K", + "tool_name": "mcp_read_file", + "params": { + "path": "SPEC.md" + }, + "status": "complete", + "text": " 1│# Worldview Format\n 2│\n 3│**Specification v0.1 — Draft**\n 4│\n 5│---\n 6│\n 7│## Abstract\n 8│\n 9│The Worldview format is a compact, declarative notation for encoding and maintaining conceptual worldviews over time. It provides a structured format for storing beliefs, stances, and understanding about concepts—designed to be included in full within every interaction context rather than retrieved selectively.\n 10│\n 11│The Worldview format is not a general-purpose communication language. It is a specialized format for preserving *how concepts are understood*, optimized for semantic density without sacrificing clarity. The notation is intended to be intuitive for large language models to parse, reason about, and autonomously maintain, while remaining human-inspectable. Beliefs are stored as structured claims with conditions and sources, enabling an LLM to hold persistent context about a user, domain, or system across extended interactions.\n 12│\n 13│---\n 14│\n 15│## Motivation\n 16│\n 17│Large language models operate within fixed context windows. Existing approaches to persistent memory—such as retrieval-augmented generation (RAG)—selectively include information based on relevance to the current query. This works for factual lookup but fails for *worldview*: the foundational beliefs and stances that should inform all reasoning, not just topically-matched queries.\n 18│\n 19│The Worldview format solves this by defining a notation dense enough that an entire belief system (potentially tens of thousands of tokens) can remain in context permanently. Rather than retrieving relevant memories, the LLM carries its complete understanding forward into every interaction.\n 20│\n 21│The format prioritizes:\n 22│- **Semantic density** — Strip prose, keep meaning\n 23│- **Structural consistency** — Predictable hierarchy for reliable parsing\n 24│- **Evolutionary tracking** — Beliefs change; the notation accommodates revision\n 25│- **Autonomous maintenance** — The LLM updates the document without user intervention\n 26│\n 27│---\n 28│\n 29│## Design Principles\n 30│\n 31│**State over narrative**\n 32│The Worldview format captures what is believed, not the story of how it came to be believed. History is preserved compactly when relevant, but the primary representation is current state.\n 33│\n 34│**Predictability allows omission**\n 35│Borrowed from stenographic shorthand: if structure or context makes something inferable, don't write it. No articles, no copulas, no filler.\n 36│\n 37│**Conflict tolerance**\n 38│Real worldviews contain tensions and contradictions. The Worldview format holds conflicting claims without forcing resolution.\n 39│\n 40│**Freeform vocabulary**\n 41│No predefined concept names, facet labels, or claim terms. The notation defines structure and relationships; content remains unconstrained.\n 42│\n 43│**LLM-native, human-inspectable**\n 44│Optimized for machine parsing and reasoning. Human readability is a secondary benefit, not a design constraint.\n 45│\n 46│---\n 47│\n 48│## Inspirations\n 49│\n 50│### Stenographic Shorthand\n 51│Systems like Gregg, Pitman, and Teeline informed the Worldview format's approach to density:\n 52│- **Omission of predictable elements** — Common words and inferable structure are dropped\n 53│- **Brief forms** — High-frequency relationships get compact symbols\n 54│- **Positional grammar** — Location within a line implies role\n 55│- **Affix modification** — Small markers inflect meaning\n 56│\n 57│### Belief Representation\n 58│The Worldview format draws on concepts from epistemology and knowledge representation:\n 59│- Beliefs as claims with conditions (contextualism)\n 60│- Sources as grounding for confidence (evidentialism)\n 61│- Tolerance of contradiction (paraconsistent approaches)\n 62│\n 63│### Configuration Languages\n 64│The hierarchical structure echoes YAML and similar formats, using indentation for nesting while avoiding syntactic overhead like quotes and brackets.\n 65│\n 66│---\n 67│\n 68│## Structure\n 69│\n 70│A Worldview document is a hierarchical collection of beliefs organized as:\n 71│\n 72│```\n 73│Document\n 74│ └── Concept (one or more)\n 75│ └── Facet (one or more per concept)\n 76│ └── Claim (one or more per facet)\n 77│ ├── Condition (zero or more)\n 78│ └── Source (zero or more)\n 79│```\n 80│\n 81│### Definitions\n 82│\n 83│| Element | Description |\n 84│|---------|-------------|\n 85│| **Concept** | A subject of belief—a noun in the worldview (e.g., Power, Trust, Human nature) |\n 86│| **Facet** | An aspect or dimension of a concept (e.g., formation, erosion, institutional) |\n 87│| **Claim** | An assertion about a facet—what is believed to be true |\n 88│| **Condition** | Circumstances under which the claim applies |\n 89│| **Source** | Basis for the belief (observation, experience, citation, intuition) |\n 90│\n 91│### Constraints\n 92│\n 93│- Every concept must have at least one facet\n 94│- Every facet must have at least one claim\n 95│- Conditions and sources are optional per claim\n 96│- Facet names are freeform (no controlled vocabulary)\n 97│- Concepts may reference other concepts, creating a web of related beliefs\n 98│\n 99│---\n 100│\n 101│## Notation\n 102│\n 103│### Hierarchy\n 104│\n 105│| Element | Notation | Indentation |\n 106│|---------|----------|-------------|\n 107│| Concept | Bare text | None (column 0) |\n 108│| Facet | `.` prefix | 2 spaces |\n 109│| Claim | `-` prefix | 4 spaces |\n 110│\n 111│### Inline Elements\n 112│\n 113│| Element | Symbol | Position |\n 114│|---------|--------|----------|\n 115│| Condition | `\\|` | After claim |\n 116│| Source | `@` | After claim/conditions |\n 117│| Reference | `&` | After claim, links to other concept.facet |\n 118│\n 119│### Positional Grammar\n 120│\n 121│Claims follow a consistent order:\n 122│\n 123│```\n 124│- [claim] | [condition] | [condition] @[source] @[source] &[reference]\n 125│```\n 126│\n 127│Position implies role—no labels needed:\n 128│1. Claim text (required)\n 129│2. Conditions (zero or more, `|` prefixed)\n 130│3. Sources (zero or more, `@` prefixed)\n 131│4. References (zero or more, `&` prefixed)\n 132│\n 133│---\n 134│\n 135│## Brief Forms\n 136│\n 137│Common relationships use compact symbols:\n 138│\n 139│| Symbol | Meaning |\n 140│|--------|---------|\n 141│| `=>` | causes, leads to |\n 142│| `<=` | caused by, results from |\n 143│| `<>` | mutual, bidirectional |\n 144│| `><` | tension, conflicts with |\n 145│| `~` | similar to, resembles |\n 146│| `=` | equivalent to, means |\n 147│| `vs` | in contrast to |\n 148│| `//` | regardless of |\n 149│\n 150│### Examples\n 151│\n 152│```\n 153│- power => corruption | unchecked\n 154│- trust <= consistency | over time\n 155│- efficiency >< thoroughness\n 156│- formal-authority ~ informal-influence\n 157│```\n 158│\n 159│---\n 160│\n 161│## Modifiers\n 162│\n 163│Suffix markers inflect claim meaning:\n 164│\n 165│| Modifier | Meaning |\n 166│|----------|---------|\n 167│| `^` | increasing, trending up |\n 168│| `v` | decreasing, trending down |\n 169│| `!` | strong, emphatic, high confidence |\n 170│| `?` | uncertain, contested, tentative |\n 171│| `*` | notable, important, flagged |\n 172│\n 173│### Examples\n 174│\n 175│```\n 176│- institutional-trust v | recent decades\n 177│- free-will? @philosophy\n 178│- single violation => collapse !\n 179│- paradigm-shift* | in progress\n 180│```\n 181│\n 182│---\n 183│\n 184│## Evolution\n 185│\n 186│Beliefs change. The Worldview format represents evolution through:\n 187│\n 188│### Supersession Markers\n 189│\n 190│Prior beliefs can be noted inline with `[<= prior belief]`:\n 191│\n 192│```\n 193│- adaptive, context-dependent [<= inherently good]\n 194│```\n 195│\n 196│This reads: \"Currently believed to be adaptive and context-dependent; this supersedes a prior belief that it was inherently good.\"\n 197│\n 198│### Implicit Evolution\n 199│\n 200│When claims in the same facet track change over time, newer claims are listed first. The array order itself implies evolution without explicit markers.\n 201│\n 202│---\n 203│\n 204│## References\n 205│\n 206│Claims can reference other concepts using `&Concept.facet`:\n 207│\n 208│```\n 209│Trust\n 210│ .erosion\n 211│ - asymmetric to formation &Trust.formation\n 212│ - single violation => collapse &Human-nature.memory\n 213│\n 214│Human-nature\n 215│ .memory\n 216│ - negative events more salient\n 217│ - loss-averse @behavioral-economics\n 218│```\n 219│\n 220│References create a graph of related beliefs, enabling the LLM to traverse connections without duplicating content.\n 221│\n 222│---\n 223│\n 224│## Examples\n 225│\n 226│### Minimal Document\n 227│\n 228│```\n 229│Power\n 230│ .core\n 231│ - corrupts | unchecked\n 232│ - reveals character\n 233│```\n 234│\n 235│### Expanded Document\n 236│\n 237│```\n 238│Power\n 239│ .nature\n 240│ - corrupts | unchecked !\n 241│ - reveals character => self-knowledge\n 242│ - concentration^ => abuse^ @historical-pattern\n 243│ .institutional\n 244│ - self-preserving\n 245│ - accountability <> trust &Trust.institutional\n 246│ - diffusion => dilution-of-responsibility\n 247│\n 248│Trust\n 249│ .formation\n 250│ - slow\n 251│ - requires consistency | over time\n 252│ - contextual @personal-experience\n 253│ .erosion\n 254│ - fast !\n 255│ - single violation => collapse?\n 256│ - asymmetric vs formation &Trust.formation\n 257│ .institutional\n 258│ - possible | high transparency\n 259│ - unlikely | low transparency\n 260│ - rational to withhold | unverifiable @game-theory\n 261│\n 262│Human-nature\n 263│ .social\n 264│ - conformist | formal groups\n 265│ - authentic | solitary\n 266│ - status-aware @evolutionary-psychology\n 267│ - coalition-forming\n 268│ .cognition\n 269│ - pattern-seeking\n 270│ - confirmation-biased @cognitive-science\n 271│ - narrative-constructing\n 272│ - rationalizes post-hoc [<= rational actor]\n 273│ .self-perception\n 274│ - overconfident | familiar domains\n 275│ - miscalibrated @Dunning-Kruger\n 276│ - self-deception => comfort &Human-nature.cognition\n 277│\n 278│Institutions\n 279│ .function\n 280│ - stabilize !\n 281│ - preserve knowledge\n 282│ - coordinate action @game-theory\n 283│ .dysfunction\n 284│ - ossify | over time\n 285│ - self-perpetuate // original purpose\n 286│ - capture-by-interests^ @public-choice-theory\n 287│```\n 288│\n 289│---\n 290│\n 291│## Non-Goals\n 292│\n 293│The Worldview format explicitly does not attempt to:\n 294│\n 295│- **Prove logical consistency** — Contradictions are permitted\n 296│- **Enforce ontology** — No required categories or hierarchies beyond structure\n 297│- **Replace natural language** — The Worldview format is for belief state, not communication\n 298│- **Assert objective truth** — Claims represent understanding, not facts\n 299│- **Store predictions, evaluations, or identity** — These are derived from beliefs, not stored directly\n 300│\n 301│---\n 302│\n 303│## Intended Use Cases\n 304│\n 305│- **Long-term LLM context anchoring** — Persistent worldview across sessions\n 306│- **Belief drift analysis** — Track how understanding evolves\n 307│- **Conceptual memory compression** — Dense storage of learned stances\n 308│- **Domain modeling** — Capture expert understanding of a field\n 309│- **Value alignment documentation** — Record interpretive frameworks\n 310│\n 311│---\n 312│\n 313│## Summary\n 314│\n 315│The Worldview format is a notation for meaning, not conversation. It exists to preserve how concepts are understood—compactly enough to remain always in context, structured enough to reason about reliably, and flexible enough to evolve as understanding changes.\n 316│\n 317│The format encodes:\n 318│- **What** is believed (claims)\n 319│- **When** it applies (conditions)\n 320│- **Why** it's believed (sources)\n 321│- **How** beliefs connect (references)\n 322│- **That** beliefs change (evolution markers)\n 323│\n 324│It deliberately omits:\n 325│- Prose and filler\n 326│- Explicit confidence scores (derived from conditions and sources)\n 327│- Detailed history (supersession markers suffice)\n 328│- Evaluative or predictive statements (derived at runtime)\n 329│\n 330│The Worldview format is designed to be carried forward—a persistent lens through which all subsequent reasoning is filtered.\n", + "background": false + }, + { + "type": "ShellBlock", + "call_id": "toolu_01VNnSAc8qSaj2Jw6NRYoFNG", + "tool_name": "mcp_shell", + "params": { + "command": "wc -l validator/src/*.rs agent/src/*.rs" + }, + "status": "complete", + "text": " 1240 validator/src/lib.rs\n 103 validator/src/main.rs\n 416 agent/src/main.rs\n 1759 total\n", + "background": false + }, + { + "type": "ThinkingBlock", + "text": "This is a really interesting project - a custom domain-specific language for encoding \"worldviews\" (beliefs, stances, understanding) in a compact notation designed to fit entirely within LLM context windows. The project has:\n\n1. A Rust validator (~1240 lines)\n2. A Rust agent CLI (~416 lines) \n3. Python evaluation framework\n\nLet me read through the core Rust code to assess quality, and also look at the spec definition and Python evals.", + "status": "complete" + }, + { + "type": "TextBlock", + "text": "This is a fascinating project! A DSL for encoding \"worldviews\" - beliefs and stances in a compact notation designed to stay entirely in LLM context. Very clever idea.\n\nLet me dive into the core code to assess quality:", + "status": "complete" + }, + { + "type": "ReadFileBlock", + "call_id": "toolu_017z2mE1g13TFpTtNbtnWfQb", + "tool_name": "mcp_read_file", + "params": { + "path": "validator/src/lib.rs" + }, + "status": "complete", + "text": " 1│//! Worldview Validator - A validator for Worldview format files\n 2│//!\n 3│//! This crate provides validation for `.wvf` files according to the Worldview specification.\n 4│//! It checks structural correctness (hierarchy, indentation), claim syntax, brief forms,\n 5│//! modifiers, and evolution markers.\n 6│\n 7│use std::fmt;\n 8│use thiserror::Error;\n 9│\n 10│// Token definitions generated at compile time from spec/tokens.yaml\n 11│include!(concat!(env!(\"OUT_DIR\"), \"/tokens.rs\"));\n 12│\n 13│/// Errors that can occur during Worldview validation\n 14│#[derive(Error, Debug, Clone, PartialEq, Eq)]\n 15│pub enum ValidationError {\n 16│ // Structural errors\n 17│ #[error(\"line {line}: invalid indentation (expected {expected} spaces, found {found})\")]\n 18│ InvalidIndentation {\n 19│ line: usize,\n 20│ expected: &'static str,\n 21│ found: usize,\n 22│ },\n 23│\n 24│ #[error(\"line {line}: facet must have '.' prefix\")]\n 25│ MissingFacetPrefix { line: usize },\n 26│\n 27│ #[error(\"line {line}: claim must have '-' prefix\")]\n 28│ MissingClaimPrefix { line: usize },\n 29│\n 30│ #[error(\"line {line}: concept '{concept}' has no facets\")]\n 31│ ConceptWithoutFacets { line: usize, concept: String },\n 32│\n 33│ #[error(\"line {line}: facet '{facet}' has no claims\")]\n 34│ FacetWithoutClaims { line: usize, facet: String },\n 35│\n 36│ #[error(\"line {line}: orphan facet (no preceding concept)\")]\n 37│ OrphanFacet { line: usize },\n 38│\n 39│ #[error(\"line {line}: orphan claim (no preceding facet)\")]\n 40│ OrphanClaim { line: usize },\n 41│\n 42│ #[error(\"line {line}: empty claim text\")]\n 43│ EmptyClaimText { line: usize },\n 44│\n 45│ #[error(\"line {line}: unexpected indentation level ({found} spaces)\")]\n 46│ UnexpectedIndentation { line: usize, found: usize },\n 47│\n 48│ #[error(\"line {line}: concept name cannot be empty\")]\n 49│ EmptyConceptName { line: usize },\n 50│\n 51│ #[error(\"line {line}: facet name cannot be empty\")]\n 52│ EmptyFacetName { line: usize },\n 53│\n 54│ // Inline element errors\n 55│ #[error(\"line {line}: invalid reference format '{reference}' (expected &Concept.facet)\")]\n 56│ InvalidReferenceFormat { line: usize, reference: String },\n 57│\n 58│ #[error(\"line {line}: empty condition (standalone '|')\")]\n 59│ EmptyCondition { line: usize },\n 60│\n 61│ #[error(\"line {line}: empty source (standalone '@')\")]\n 62│ EmptySource { line: usize },\n 63│\n 64│ #[error(\"line {line}: empty reference (standalone '&')\")]\n 65│ EmptyReference { line: usize },\n 66│\n 67│ // Brief form errors\n 68│ #[error(\"line {line}: brief form '{operator}' missing left operand\")]\n 69│ BriefFormMissingLeftOperand { line: usize, operator: String },\n 70│\n 71│ #[error(\"line {line}: brief form '{operator}' missing right operand\")]\n 72│ BriefFormMissingRightOperand { line: usize, operator: String },\n 73│\n 74│ // Evolution marker errors\n 75│ #[error(\"line {line}: unclosed evolution marker '[<=' (missing ']')\")]\n 76│ UnclosedEvolutionMarker { line: usize },\n 77│\n 78│ #[error(\"line {line}: empty evolution marker '[<= ]' (no prior belief specified)\")]\n 79│ EmptyEvolutionMarker { line: usize },\n 80│\n 81│ #[error(\"line {line}: malformed evolution marker (expected '[<= prior belief]')\")]\n 82│ MalformedEvolutionMarker { line: usize },\n 83│\n 84│ // Modifier warnings (these are softer - might be intentional)\n 85│ #[error(\"line {line}: standalone modifier '{modifier}' may be unintentional\")]\n 86│ StandaloneModifier { line: usize, modifier: String },\n 87│}\n 88│\n 89│impl ValidationError {\n 90│ /// Returns true if this is a warning rather than a hard error\n 91│ pub fn is_warning(&self) -> bool {\n 92│ matches!(self, ValidationError::StandaloneModifier { .. })\n 93│ }\n 94│}\n 95│\n 96│/// The type of a parsed line\n 97│#[derive(Debug, Clone, PartialEq, Eq)]\n 98│pub enum LineType {\n 99│ /// Empty line or whitespace only\n 100│ Blank,\n 101│ /// A concept (unindented text)\n 102│ Concept(String),\n 103│ /// A facet (2-space indent, '.' prefix)\n 104│ Facet(String),\n 105│ /// A claim (4-space indent, '-' prefix)\n 106│ Claim(ClaimData),\n 107│}\n 108│\n 109│/// Parsed claim data\n 110│#[derive(Debug, Clone, PartialEq, Eq)]\n 111│pub struct ClaimData {\n 112│ pub text: String,\n 113│ pub conditions: Vec,\n 114│ pub sources: Vec,\n 115│ pub references: Vec,\n 116│ pub brief_forms: Vec,\n 117│ pub modifiers: Vec,\n 118│ pub evolution: Option,\n 119│}\n 120│\n 121│/// A brief form operator found in a claim\n 122│#[derive(Debug, Clone, PartialEq, Eq)]\n 123│pub struct BriefFormUsage {\n 124│ pub operator: String,\n 125│ pub left_operand: String,\n 126│ pub right_operand: String,\n 127│}\n 128│\n 129│/// A modifier found in a claim\n 130│#[derive(Debug, Clone, PartialEq, Eq)]\n 131│pub struct ModifierUsage {\n 132│ pub symbol: char,\n 133│ pub attached_to: String,\n 134│}\n 135│\n 136│/// An evolution marker [<= prior belief]\n 137│#[derive(Debug, Clone, PartialEq, Eq)]\n 138│pub struct EvolutionMarker {\n 139│ pub prior_belief: String,\n 140│}\n 141│\n 142│/// A parsed line with its metadata\n 143│#[derive(Debug, Clone)]\n 144│pub struct ParsedLine {\n 145│ pub line_number: usize,\n 146│ pub line_type: LineType,\n 147│ pub raw: String,\n 148│}\n 149│\n 150│/// Result of validation\n 151│#[derive(Debug, Clone)]\n 152│pub struct ValidationResult {\n 153│ pub errors: Vec,\n 154│ pub warnings: Vec,\n 155│ pub lines: Vec,\n 156│}\n 157│\n 158│impl ValidationResult {\n 159│ pub fn is_valid(&self) -> bool {\n 160│ self.errors.is_empty()\n 161│ }\n 162│\n 163│ pub fn has_warnings(&self) -> bool {\n 164│ !self.warnings.is_empty()\n 165│ }\n 166│}\n 167│\n 168│impl fmt::Display for ValidationResult {\n 169│ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n 170│ if self.is_valid() && !self.has_warnings() {\n 171│ write!(f, \"Valid Worldview document\")\n 172│ } else if self.is_valid() {\n 173│ writeln!(f, \"Valid Worldview document with {} warning(s):\", self.warnings.len())?;\n 174│ for warning in &self.warnings {\n 175│ writeln!(f, \" {}\", warning)?;\n 176│ }\n 177│ Ok(())\n 178│ } else {\n 179│ writeln!(f, \"Invalid Worldview document ({} error(s)):\", self.errors.len())?;\n 180│ for error in &self.errors {\n 181│ writeln!(f, \" {}\", error)?;\n 182│ }\n 183│ if self.has_warnings() {\n 184│ writeln!(f, \"Additionally, {} warning(s):\", self.warnings.len())?;\n 185│ for warning in &self.warnings {\n 186│ writeln!(f, \" {}\", warning)?;\n 187│ }\n 188│ }\n 189│ Ok(())\n 190│ }\n 191│ }\n 192│}\n 193│\n 194│/// Validates a Worldview document\n 195│pub fn validate(input: &str) -> ValidationResult {\n 196│ let mut errors = Vec::new();\n 197│ let mut warnings = Vec::new();\n 198│ let mut lines = Vec::new();\n 199│\n 200│ // First pass: tokenize lines\n 201│ for (idx, raw_line) in input.lines().enumerate() {\n 202│ let line_number = idx + 1;\n 203│ let parsed = tokenize_line(raw_line, line_number, &mut errors);\n 204│ lines.push(ParsedLine {\n 205│ line_number,\n 206│ line_type: parsed,\n 207│ raw: raw_line.to_string(),\n 208│ });\n 209│ }\n 210│\n 211│ // Second pass: validate structure\n 212│ validate_structure(&lines, &mut errors);\n 213│\n 214│ // Third pass: validate claim syntax including brief forms, modifiers, evolution\n 215│ for line in &lines {\n 216│ if let LineType::Claim(claim) = &line.line_type {\n 217│ validate_claim_syntax(line.line_number, claim, &mut errors, &mut warnings);\n 218│ }\n 219│ }\n 220│\n 221│ ValidationResult { errors, warnings, lines }\n 222│}\n 223│\n 224│/// Count leading spaces\n 225│fn count_leading_spaces(line: &str) -> usize {\n 226│ line.chars().take_while(|c| *c == ' ').count()\n 227│}\n 228│\n 229│/// Tokenize a single line\n 230│fn tokenize_line(line: &str, line_number: usize, errors: &mut Vec) -> LineType {\n 231│ // Blank lines\n 232│ if line.trim().is_empty() {\n 233│ return LineType::Blank;\n 234│ }\n 235│\n 236│ let indent = count_leading_spaces(line);\n 237│ let content = line.trim();\n 238│\n 239│ match indent {\n 240│ 0 => {\n 241│ // Concept: no indent, bare text\n 242│ if content.is_empty() {\n 243│ errors.push(ValidationError::EmptyConceptName { line: line_number });\n 244│ LineType::Blank\n 245│ } else {\n 246│ LineType::Concept(content.to_string())\n 247│ }\n 248│ }\n 249│ 2 => {\n 250│ // Facet: 2-space indent, '.' prefix\n 251│ if !content.starts_with('.') {\n 252│ errors.push(ValidationError::MissingFacetPrefix { line: line_number });\n 253│ LineType::Blank\n 254│ } else {\n 255│ let name = content[1..].trim();\n 256│ if name.is_empty() {\n 257│ errors.push(ValidationError::EmptyFacetName { line: line_number });\n 258│ }\n 259│ LineType::Facet(name.to_string())\n 260│ }\n 261│ }\n 262│ 4 => {\n 263│ // Claim: 4-space indent, '-' prefix\n 264│ if !content.starts_with('-') {\n 265│ errors.push(ValidationError::MissingClaimPrefix { line: line_number });\n 266│ LineType::Blank\n 267│ } else {\n 268│ let claim_text = content[1..].trim();\n 269│ let claim_data = parse_claim(claim_text);\n 270│ LineType::Claim(claim_data)\n 271│ }\n 272│ }\n 273│ _ => {\n 274│ // Invalid indentation\n 275│ if indent == 1 || indent == 3 {\n 276│ errors.push(ValidationError::InvalidIndentation {\n 277│ line: line_number,\n 278│ expected: \"0, 2, or 4\",\n 279│ found: indent,\n 280│ });\n 281│ } else {\n 282│ errors.push(ValidationError::UnexpectedIndentation {\n 283│ line: line_number,\n 284│ found: indent,\n 285│ });\n 286│ }\n 287│ LineType::Blank\n 288│ }\n 289│ }\n 290│}\n 291│\n 292│/// Parse claim content into structured data\n 293│fn parse_claim(text: &str) -> ClaimData {\n 294│ let mut claim_text = String::new();\n 295│ let mut conditions = Vec::new();\n 296│ let mut sources = Vec::new();\n 297│ let mut references = Vec::new();\n 298│\n 299│ // First, extract evolution marker if present\n 300│ let (text_without_evolution, evolution) = extract_evolution_marker(text);\n 301│ let text = text_without_evolution.as_str();\n 302│\n 303│ // Parse inline elements (|, @, &)\n 304│ let mut current_segment = String::new();\n 305│ let mut in_claim = true;\n 306│ let mut chars = text.chars().peekable();\n 307│\n 308│ while let Some(c) = chars.next() {\n 309│ match c {\n 310│ '|' => {\n 311│ // Condition marker\n 312│ if in_claim {\n 313│ claim_text = current_segment.trim().to_string();\n 314│ in_claim = false;\n 315│ } else if !current_segment.trim().is_empty() {\n 316│ conditions.push(current_segment.trim().to_string());\n 317│ }\n 318│ current_segment = String::new();\n 319│ }\n 320│ '@' => {\n 321│ // Source marker\n 322│ if in_claim {\n 323│ claim_text = current_segment.trim().to_string();\n 324│ in_claim = false;\n 325│ } else if !current_segment.trim().is_empty() {\n 326│ conditions.push(current_segment.trim().to_string());\n 327│ }\n 328│ current_segment = String::new();\n 329│ // Collect source name (until space or another marker)\n 330│ while let Some(&next) = chars.peek() {\n 331│ if next == ' ' || next == '|' || next == '@' || next == '&' {\n 332│ break;\n 333│ }\n 334│ current_segment.push(chars.next().unwrap());\n 335│ }\n 336│ if !current_segment.trim().is_empty() {\n 337│ sources.push(current_segment.trim().to_string());\n 338│ }\n 339│ current_segment = String::new();\n 340│ }\n 341│ '&' => {\n 342│ // Reference marker\n 343│ if in_claim {\n 344│ claim_text = current_segment.trim().to_string();\n 345│ in_claim = false;\n 346│ } else if !current_segment.trim().is_empty() {\n 347│ conditions.push(current_segment.trim().to_string());\n 348│ }\n 349│ current_segment = String::new();\n 350│ // Collect reference (until space or another marker)\n 351│ while let Some(&next) = chars.peek() {\n 352│ if next == ' ' || next == '|' || next == '@' || next == '&' {\n 353│ break;\n 354│ }\n 355│ current_segment.push(chars.next().unwrap());\n 356│ }\n 357│ if !current_segment.trim().is_empty() {\n 358│ references.push(current_segment.trim().to_string());\n 359│ }\n 360│ current_segment = String::new();\n 361│ }\n 362│ _ => {\n 363│ current_segment.push(c);\n 364│ }\n 365│ }\n 366│ }\n 367│\n 368│ // Handle remaining segment\n 369│ if !current_segment.trim().is_empty() {\n 370│ if in_claim {\n 371│ claim_text = current_segment.trim().to_string();\n 372│ } else {\n 373│ conditions.push(current_segment.trim().to_string());\n 374│ }\n 375│ }\n 376│\n 377│ // Extract brief forms from claim text\n 378│ let brief_forms = extract_brief_forms(&claim_text);\n 379│\n 380│ // Extract modifiers from claim text\n 381│ let modifiers = extract_modifiers(&claim_text);\n 382│\n 383│ ClaimData {\n 384│ text: claim_text,\n 385│ conditions,\n 386│ sources,\n 387│ references,\n 388│ brief_forms,\n 389│ modifiers,\n 390│ evolution,\n 391│ }\n 392│}\n 393│\n 394│/// Extract evolution marker [<= prior belief] from text\n 395│fn extract_evolution_marker(text: &str) -> (String, Option) {\n 396│ if let Some(start) = text.find(\"[<=\") {\n 397│ if let Some(end) = text[start..].find(']') {\n 398│ let marker_content = &text[start + 3..start + end];\n 399│ let prior_belief = marker_content.trim().to_string();\n 400│ let text_before = &text[..start];\n 401│ let text_after = &text[start + end + 1..];\n 402│ let cleaned = format!(\"{}{}\", text_before.trim(), text_after.trim());\n 403│ return (\n 404│ cleaned,\n 405│ Some(EvolutionMarker { prior_belief }),\n 406│ );\n 407│ }\n 408│ // Unclosed marker - return as-is, validation will catch it\n 409│ return (text.to_string(), None);\n 410│ }\n 411│ (text.to_string(), None)\n 412│}\n 413│\n 414│/// Extract brief form usages from claim text\n 415│fn extract_brief_forms(text: &str) -> Vec {\n 416│ let mut usages = Vec::new();\n 417│\n 418│ // Check for each brief form operator\n 419│ // Order matters: check longer operators first to avoid partial matches\n 420│ let operators_by_length: &[&str] = &[\"=>\", \"<=\", \"<>\", \"><\", \"//\", \"vs\", \"~\", \"=\"];\n 421│\n 422│ let remaining = text.to_string();\n 423│\n 424│ for &op in operators_by_length {\n 425│ // Skip <= if it's part of [<= (evolution marker)\n 426│ if op == \"<=\" && remaining.contains(\"[<=\") {\n 427│ continue;\n 428│ }\n 429│\n 430│ // Special handling for = to avoid matching => or <=\n 431│ if op == \"=\" {\n 432│ // Look for standalone = not part of => or <=\n 433│ let mut i = 0;\n 434│ let chars: Vec = remaining.chars().collect();\n 435│ while i < chars.len() {\n 436│ if chars[i] == '=' {\n 437│ let prev = if i > 0 { Some(chars[i - 1]) } else { None };\n 438│ let next = chars.get(i + 1);\n 439│ // Check it's not part of =>, <=, or <>\n 440│ if prev != Some('<') && prev != Some('>') && next != Some(&'>') {\n 441│ // Found standalone =\n 442│ let before: String = chars[..i].iter().collect();\n 443│ let after: String = chars[i + 1..].iter().collect();\n 444│ let left = before.split_whitespace().last().unwrap_or(\"\").to_string();\n 445│ let right = after.split_whitespace().next().unwrap_or(\"\").to_string();\n 446│ if !left.is_empty() || !right.is_empty() {\n 447│ usages.push(BriefFormUsage {\n 448│ operator: op.to_string(),\n 449│ left_operand: left,\n 450│ right_operand: right,\n 451│ });\n 452│ }\n 453│ }\n 454│ }\n 455│ i += 1;\n 456│ }\n 457│ continue;\n 458│ }\n 459│\n 460│ // For other operators\n 461│ for (idx, _) in remaining.match_indices(op) {\n 462│ let before = &remaining[..idx];\n 463│ let after = &remaining[idx + op.len()..];\n 464│\n 465│ let left = before.split_whitespace().last().unwrap_or(\"\").to_string();\n 466│ let right = after.split_whitespace().next().unwrap_or(\"\").to_string();\n 467│\n 468│ // Clean up modifiers from operands for matching\n 469│ let left_clean = left.trim_end_matches(|c| \"^v!?*\".contains(c));\n 470│ let right_clean = right.trim_end_matches(|c| \"^v!?*\".contains(c));\n 471│\n 472│ usages.push(BriefFormUsage {\n 473│ operator: op.to_string(),\n 474│ left_operand: left_clean.to_string(),\n 475│ right_operand: right_clean.to_string(),\n 476│ });\n 477│ }\n 478│ }\n 479│\n 480│ usages\n 481│}\n 482│\n 483│/// Extract modifier usages from claim text\n 484│fn extract_modifiers(text: &str) -> Vec {\n 485│ let mut usages = Vec::new();\n 486│ let modifier_chars = ['^', '!', '?', '*'];\n 487│\n 488│ // Split into tokens\n 489│ let tokens: Vec<&str> = text.split_whitespace().collect();\n 490│\n 491│ for (i, token) in tokens.iter().enumerate() {\n 492│ // Check for attached modifiers (e.g., \"concentration^\", \"collapse?\")\n 493│ for &m in &modifier_chars {\n 494│ if token.ends_with(m) && token.len() > 1 {\n 495│ let attached = token.trim_end_matches(m);\n 496│ usages.push(ModifierUsage {\n 497│ symbol: m,\n 498│ attached_to: attached.to_string(),\n 499│ });\n 500│ }\n 501│ }\n 502│\n 503│ // Check for standalone modifiers (e.g., \"fast !\" where ! is separate token)\n 504│ // These modify the preceding term\n 505│ for &m in &modifier_chars {\n 506│ if token.len() == 1 && token.chars().next() == Some(m) && i > 0 {\n 507│ let prev = tokens[i - 1];\n 508│ // Don't count if previous token is an operator\n 509│ let is_after_operator = BRIEF_FORMS.iter().any(|(op, _)| prev.ends_with(op));\n 510│ if !is_after_operator {\n 511│ usages.push(ModifierUsage {\n 512│ symbol: m,\n 513│ attached_to: prev.trim_end_matches(|c| \"^!?*v\".contains(c)).to_string(),\n 514│ });\n 515│ }\n 516│ }\n 517│ }\n 518│\n 519│ // Check for 'v' modifier - it's special because it's also a letter\n 520│ // It's a modifier when: standalone 'v' following a term\n 521│ if *token == \"v\" && i > 0 {\n 522│ let prev = tokens[i - 1];\n 523│ // Don't treat 'v' as modifier if previous token is an operator\n 524│ let is_after_operator = BRIEF_FORMS.iter().any(|(op, _)| prev.ends_with(op));\n 525│ if !is_after_operator {\n 526│ usages.push(ModifierUsage {\n 527│ symbol: 'v',\n 528│ attached_to: prev.trim_end_matches(|c| \"^!?*\".contains(c)).to_string(),\n 529│ });\n 530│ }\n 531│ }\n 532│ }\n 533│\n 534│ usages\n 535│}\n 536│\n 537│/// Validate document structure\n 538│fn validate_structure(lines: &[ParsedLine], errors: &mut Vec) {\n 539│ let mut current_concept: Option<(usize, String)> = None;\n 540│ let mut current_facet: Option<(usize, String)> = None;\n 541│ let mut concept_has_facet = false;\n 542│ let mut facet_has_claim = false;\n 543│\n 544│ for line in lines {\n 545│ match &line.line_type {\n 546│ LineType::Blank => continue,\n 547│ LineType::Concept(name) => {\n 548│ // Check previous concept had facets\n 549│ if let Some((concept_line, concept_name)) = current_concept.take() {\n 550│ if !concept_has_facet {\n 551│ errors.push(ValidationError::ConceptWithoutFacets {\n 552│ line: concept_line,\n 553│ concept: concept_name,\n 554│ });\n 555│ }\n 556│ }\n 557│ // Check previous facet had claims\n 558│ if let Some((facet_line, facet_name)) = current_facet.take() {\n 559│ if !facet_has_claim {\n 560│ errors.push(ValidationError::FacetWithoutClaims {\n 561│ line: facet_line,\n 562│ facet: facet_name,\n 563│ });\n 564│ }\n 565│ }\n 566│ current_concept = Some((line.line_number, name.clone()));\n 567│ current_facet = None;\n 568│ concept_has_facet = false;\n 569│ facet_has_claim = false;\n 570│ }\n 571│ LineType::Facet(name) => {\n 572│ if current_concept.is_none() {\n 573│ errors.push(ValidationError::OrphanFacet {\n 574│ line: line.line_number,\n 575│ });\n 576│ } else {\n 577│ concept_has_facet = true;\n 578│ }\n 579│ // Check previous facet had claims\n 580│ if let Some((facet_line, facet_name)) = current_facet.take() {\n 581│ if !facet_has_claim {\n 582│ errors.push(ValidationError::FacetWithoutClaims {\n 583│ line: facet_line,\n 584│ facet: facet_name,\n 585│ });\n 586│ }\n 587│ }\n 588│ current_facet = Some((line.line_number, name.clone()));\n 589│ facet_has_claim = false;\n 590│ }\n 591│ LineType::Claim(_) => {\n 592│ if current_facet.is_none() {\n 593│ errors.push(ValidationError::OrphanClaim {\n 594│ line: line.line_number,\n 595│ });\n 596│ } else {\n 597│ facet_has_claim = true;\n 598│ }\n 599│ }\n 600│ }\n 601│ }\n 602│\n 603│ // Check final concept and facet\n 604│ if let Some((concept_line, concept_name)) = current_concept {\n 605│ if !concept_has_facet {\n 606│ errors.push(ValidationError::ConceptWithoutFacets {\n 607│ line: concept_line,\n 608│ concept: concept_name,\n 609│ });\n 610│ }\n 611│ }\n 612│ if let Some((facet_line, facet_name)) = current_facet {\n 613│ if !facet_has_claim {\n 614│ errors.push(ValidationError::FacetWithoutClaims {\n 615│ line: facet_line,\n 616│ facet: facet_name,\n 617│ });\n 618│ }\n 619│ }\n 620│}\n 621│\n 622│/// Validate claim syntax including brief forms, modifiers, and evolution markers\n 623│fn validate_claim_syntax(\n 624│ line_number: usize,\n 625│ claim: &ClaimData,\n 626│ errors: &mut Vec,\n 627│ warnings: &mut Vec,\n 628│) {\n 629│ // Check for empty claim text\n 630│ if claim.text.is_empty() {\n 631│ errors.push(ValidationError::EmptyClaimText { line: line_number });\n 632│ }\n 633│\n 634│ // Check for empty conditions\n 635│ for cond in &claim.conditions {\n 636│ if cond.is_empty() {\n 637│ errors.push(ValidationError::EmptyCondition { line: line_number });\n 638│ }\n 639│ }\n 640│\n 641│ // Check for empty sources\n 642│ for src in &claim.sources {\n 643│ if src.is_empty() {\n 644│ errors.push(ValidationError::EmptySource { line: line_number });\n 645│ }\n 646│ }\n 647│\n 648│ // Check for empty references\n 649│ for reference in &claim.references {\n 650│ if reference.is_empty() {\n 651│ errors.push(ValidationError::EmptyReference { line: line_number });\n 652│ }\n 653│ }\n 654│\n 655│ // Validate reference format (should be Concept.facet)\n 656│ for reference in &claim.references {\n 657│ if !reference.is_empty() && !reference.contains('.') {\n 658│ errors.push(ValidationError::InvalidReferenceFormat {\n 659│ line: line_number,\n 660│ reference: reference.clone(),\n 661│ });\n 662│ }\n 663│ }\n 664│\n 665│ // Validate brief forms have operands\n 666│ for bf in &claim.brief_forms {\n 667│ if bf.left_operand.is_empty() {\n 668│ errors.push(ValidationError::BriefFormMissingLeftOperand {\n 669│ line: line_number,\n 670│ operator: bf.operator.clone(),\n 671│ });\n 672│ }\n 673│ if bf.right_operand.is_empty() {\n 674│ errors.push(ValidationError::BriefFormMissingRightOperand {\n 675│ line: line_number,\n 676│ operator: bf.operator.clone(),\n 677│ });\n 678│ }\n 679│ }\n 680│\n 681│ // Check for unclosed evolution markers in original text\n 682│ if claim.text.contains(\"[<=\") && !claim.text.contains(']') {\n 683│ errors.push(ValidationError::UnclosedEvolutionMarker { line: line_number });\n 684│ }\n 685│\n 686│ // Validate evolution marker content if present\n 687│ if let Some(ref evo) = claim.evolution {\n 688│ if evo.prior_belief.is_empty() {\n 689│ errors.push(ValidationError::EmptyEvolutionMarker { line: line_number });\n 690│ }\n 691│ }\n 692│\n 693│ // Check for standalone modifiers that appear at the start (warning, not error)\n 694│ // Space-separated modifiers that follow a term are valid (e.g., \"fast !\")\n 695│ let tokens: Vec<&str> = claim.text.split_whitespace().collect();\n 696│ for (i, token) in tokens.iter().enumerate() {\n 697│ if *token == \"^\" || *token == \"!\" || *token == \"?\" || *token == \"*\" {\n 698│ // Only warn if it's at the start (no preceding term) or follows an operator\n 699│ if i == 0 {\n 700│ warnings.push(ValidationError::StandaloneModifier {\n 701│ line: line_number,\n 702│ modifier: token.to_string(),\n 703│ });\n 704│ } else {\n 705│ let prev = tokens[i - 1];\n 706│ let is_after_operator = BRIEF_FORMS.iter().any(|(op, _)| prev.ends_with(op));\n 707│ if is_after_operator {\n 708│ warnings.push(ValidationError::StandaloneModifier {\n 709│ line: line_number,\n 710│ modifier: token.to_string(),\n 711│ });\n 712│ }\n 713│ }\n 714│ }\n 715│ }\n 716│}\n 717│\n 718│/// Validate a file by path\n 719│pub fn validate_file(path: &std::path::Path) -> Result {\n 720│ let content = std::fs::read_to_string(path)?;\n 721│ Ok(validate(&content))\n 722│}\n 723│\n 724│#[cfg(test)]\n 725│mod tests {\n 726│ use super::*;\n 727│\n 728│ // ==================== Structural tests ====================\n 729│\n 730│ #[test]\n 731│ fn test_valid_minimal_document() {\n 732│ let input = r#\"Power\n 733│ .core\n 734│ - corrupts | unchecked\n 735│ - reveals character\"#;\n 736│\n 737│ let result = validate(input);\n 738│ assert!(result.is_valid(), \"Expected valid document: {:?}\", result.errors);\n 739│ }\n 740│\n 741│ #[test]\n 742│ fn test_concept_without_facet() {\n 743│ let input = \"Power\\nTrust\";\n 744│ let result = validate(input);\n 745│ assert!(!result.is_valid());\n 746│ assert!(result.errors.iter().any(|e| matches!(e, ValidationError::ConceptWithoutFacets { .. })));\n 747│ }\n 748│\n 749│ #[test]\n 750│ fn test_facet_without_claim() {\n 751│ let input = r#\"Power\n 752│ .core\"#;\n 753│ let result = validate(input);\n 754│ assert!(!result.is_valid());\n 755│ assert!(result.errors.iter().any(|e| matches!(e, ValidationError::FacetWithoutClaims { .. })));\n 756│ }\n 757│\n 758│ #[test]\n 759│ fn test_orphan_facet() {\n 760│ let input = \" .core\";\n 761│ let result = validate(input);\n 762│ assert!(!result.is_valid());\n 763│ assert!(result.errors.iter().any(|e| matches!(e, ValidationError::OrphanFacet { .. })));\n 764│ }\n 765│\n 766│ #[test]\n 767│ fn test_orphan_claim() {\n 768│ let input = \" - corrupts\";\n 769│ let result = validate(input);\n 770│ assert!(!result.is_valid());\n 771│ assert!(result.errors.iter().any(|e| matches!(e, ValidationError::OrphanClaim { .. })));\n 772│ }\n 773│\n 774│ #[test]\n 775│ fn test_invalid_indentation() {\n 776│ let input = r#\"Power\n 777│ .core\"#;\n 778│ let result = validate(input);\n 779│ assert!(!result.is_valid());\n 780│ assert!(result.errors.iter().any(|e| matches!(e, ValidationError::InvalidIndentation { .. })));\n 781│ }\n 782│\n 783│ #[test]\n 784│ fn test_missing_facet_prefix() {\n 785│ let input = r#\"Power\n 786│ core\n 787│ - corrupts\"#;\n 788│ let result = validate(input);\n 789│ assert!(!result.is_valid());\n 790│ assert!(result.errors.iter().any(|e| matches!(e, ValidationError::MissingFacetPrefix { .. })));\n 791│ }\n 792│\n 793│ #[test]\n 794│ fn test_missing_claim_prefix() {\n 795│ let input = r#\"Power\n 796│ .core\n 797│ corrupts\"#;\n 798│ let result = validate(input);\n 799│ assert!(!result.is_valid());\n 800│ assert!(result.errors.iter().any(|e| matches!(e, ValidationError::MissingClaimPrefix { .. })));\n 801│ }\n 802│\n 803│ #[test]\n 804│ fn test_blank_lines_allowed() {\n 805│ let input = r#\"Power\n 806│ .core\n 807│ - corrupts\n 808│\n 809│Trust\n 810│ .formation\n 811│ - slow\"#;\n 812│\n 813│ let result = validate(input);\n 814│ assert!(result.is_valid(), \"Expected valid: {:?}\", result.errors);\n 815│ }\n 816│\n 817│ // ==================== Inline element tests ====================\n 818│\n 819│ #[test]\n 820│ fn test_claim_with_conditions_and_sources() {\n 821│ let input = r#\"Trust\n 822│ .formation\n 823│ - requires consistency | over time @personal-experience\"#;\n 824│\n 825│ let result = validate(input);\n 826│ assert!(result.is_valid(), \"Expected valid: {:?}\", result.errors);\n 827│\n 828│ if let Some(line) = result.lines.iter().find(|l| matches!(l.line_type, LineType::Claim(_))) {\n 829│ if let LineType::Claim(claim) = &line.line_type {\n 830│ assert_eq!(claim.text, \"requires consistency\");\n 831│ assert!(claim.conditions.contains(&\"over time\".to_string()));\n 832│ assert!(claim.sources.contains(&\"personal-experience\".to_string()));\n 833│ }\n 834│ }\n 835│ }\n 836│\n 837│ #[test]\n 838│ fn test_claim_with_reference() {\n 839│ let input = r#\"Trust\n 840│ .erosion\n 841│ - asymmetric vs formation &Trust.formation\"#;\n 842│\n 843│ let result = validate(input);\n 844│ assert!(result.is_valid(), \"Expected valid: {:?}\", result.errors);\n 845│\n 846│ if let Some(line) = result.lines.iter().find(|l| matches!(l.line_type, LineType::Claim(_))) {\n 847│ if let LineType::Claim(claim) = &line.line_type {\n 848│ assert!(claim.references.contains(&\"Trust.formation\".to_string()));\n 849│ }\n 850│ }\n 851│ }\n 852│\n 853│ #[test]\n 854│ fn test_invalid_reference_format() {\n 855│ let input = r#\"Power\n 856│ .core\n 857│ - corrupts &InvalidReference\"#;\n 858│ let result = validate(input);\n 859│ assert!(!result.is_valid());\n 860│ assert!(result.errors.iter().any(|e| matches!(e, ValidationError::InvalidReferenceFormat { .. })));\n 861│ }\n 862│\n 863│ // ==================== Brief form tests ====================\n 864│\n 865│ #[test]\n 866│ fn test_brief_form_causes() {\n 867│ let input = r#\"Power\n 868│ .nature\n 869│ - power => corruption\"#;\n 870│\n 871│ let result = validate(input);\n 872│ assert!(result.is_valid(), \"Expected valid: {:?}\", result.errors);\n 873│\n 874│ if let Some(line) = result.lines.iter().find(|l| matches!(l.line_type, LineType::Claim(_))) {\n 875│ if let LineType::Claim(claim) = &line.line_type {\n 876│ assert!(!claim.brief_forms.is_empty());\n 877│ let bf = &claim.brief_forms[0];\n 878│ assert_eq!(bf.operator, \"=>\");\n 879│ assert_eq!(bf.left_operand, \"power\");\n 880│ assert_eq!(bf.right_operand, \"corruption\");\n 881│ }\n 882│ }\n 883│ }\n 884│\n 885│ #[test]\n 886│ fn test_brief_form_caused_by() {\n 887│ let input = r#\"Trust\n 888│ .formation\n 889│ - trust <= consistency\"#;\n 890│\n 891│ let result = validate(input);\n 892│ assert!(result.is_valid(), \"Expected valid: {:?}\", result.errors);\n 893│\n 894│ if let Some(line) = result.lines.iter().find(|l| matches!(l.line_type, LineType::Claim(_))) {\n 895│ if let LineType::Claim(claim) = &line.line_type {\n 896│ let bf = claim.brief_forms.iter().find(|b| b.operator == \"<=\");\n 897│ assert!(bf.is_some(), \"Expected <= operator\");\n 898│ }\n 899│ }\n 900│ }\n 901│\n 902│ #[test]\n 903│ fn test_brief_form_mutual() {\n 904│ let input = r#\"Trust\n 905│ .institutional\n 906│ - accountability <> trust\"#;\n 907│\n 908│ let result = validate(input);\n 909│ assert!(result.is_valid(), \"Expected valid: {:?}\", result.errors);\n 910│\n 911│ if let Some(line) = result.lines.iter().find(|l| matches!(l.line_type, LineType::Claim(_))) {\n 912│ if let LineType::Claim(claim) = &line.line_type {\n 913│ let bf = claim.brief_forms.iter().find(|b| b.operator == \"<>\");\n 914│ assert!(bf.is_some(), \"Expected <> operator\");\n 915│ }\n 916│ }\n 917│ }\n 918│\n 919│ #[test]\n 920│ fn test_brief_form_tension() {\n 921│ let input = r#\"Work\n 922│ .balance\n 923│ - efficiency >< thoroughness\"#;\n 924│\n 925│ let result = validate(input);\n 926│ assert!(result.is_valid(), \"Expected valid: {:?}\", result.errors);\n 927│\n 928│ if let Some(line) = result.lines.iter().find(|l| matches!(l.line_type, LineType::Claim(_))) {\n 929│ if let LineType::Claim(claim) = &line.line_type {\n 930│ let bf = claim.brief_forms.iter().find(|b| b.operator == \"><\");\n 931│ assert!(bf.is_some(), \"Expected >< operator\");\n 932│ }\n 933│ }\n 934│ }\n 935│\n 936│ #[test]\n 937│ fn test_brief_form_similar() {\n 938│ let input = r#\"Authority\n 939│ .types\n 940│ - formal-authority ~ informal-influence\"#;\n 941│\n 942│ let result = validate(input);\n 943│ assert!(result.is_valid(), \"Expected valid: {:?}\", result.errors);\n 944│\n 945│ if let Some(line) = result.lines.iter().find(|l| matches!(l.line_type, LineType::Claim(_))) {\n 946│ if let LineType::Claim(claim) = &line.line_type {\n 947│ let bf = claim.brief_forms.iter().find(|b| b.operator == \"~\");\n 948│ assert!(bf.is_some(), \"Expected ~ operator\");\n 949│ }\n 950│ }\n 951│ }\n 952│\n 953│ #[test]\n 954│ fn test_brief_form_contrast() {\n 955│ let input = r#\"Trust\n 956│ .erosion\n 957│ - asymmetric vs formation\"#;\n 958│\n 959│ let result = validate(input);\n 960│ assert!(result.is_valid(), \"Expected valid: {:?}\", result.errors);\n 961│\n 962│ if let Some(line) = result.lines.iter().find(|l| matches!(l.line_type, LineType::Claim(_))) {\n 963│ if let LineType::Claim(claim) = &line.line_type {\n 964│ let bf = claim.brief_forms.iter().find(|b| b.operator == \"vs\");\n 965│ assert!(bf.is_some(), \"Expected vs operator\");\n 966│ }\n 967│ }\n 968│ }\n 969│\n 970│ #[test]\n 971│ fn test_brief_form_regardless() {\n 972│ let input = r#\"Institutions\n 973│ .dysfunction\n 974│ - self-perpetuate // original purpose\"#;\n 975│\n 976│ let result = validate(input);\n 977│ assert!(result.is_valid(), \"Expected valid: {:?}\", result.errors);\n 978│\n 979│ if let Some(line) = result.lines.iter().find(|l| matches!(l.line_type, LineType::Claim(_))) {\n 980│ if let LineType::Claim(claim) = &line.line_type {\n 981│ let bf = claim.brief_forms.iter().find(|b| b.operator == \"//\");\n 982│ assert!(bf.is_some(), \"Expected // operator\");\n 983│ }\n 984│ }\n 985│ }\n 986│\n 987│ #[test]\n 988│ fn test_brief_form_missing_left_operand() {\n 989│ let input = r#\"Power\n 990│ .core\n 991│ - => corruption\"#;\n 992│\n 993│ let result = validate(input);\n 994│ assert!(!result.is_valid());\n 995│ assert!(result.errors.iter().any(|e| matches!(e, ValidationError::BriefFormMissingLeftOperand { .. })));\n 996│ }\n 997│\n 998│ #[test]\n 999│ fn test_brief_form_missing_right_operand() {\n1000│ let input = r#\"Power\n1001│ .core\n1002│ - power =>\"#;\n1003│\n1004│ let result = validate(input);\n1005│ assert!(!result.is_valid());\n1006│ assert!(result.errors.iter().any(|e| matches!(e, ValidationError::BriefFormMissingRightOperand { .. })));\n1007│ }\n1008│\n1009│ // ==================== Modifier tests ====================\n1010│\n1011│ #[test]\n1012│ fn test_modifier_increasing() {\n1013│ let input = r#\"Power\n1014│ .nature\n1015│ - concentration^ => abuse\"#;\n1016│\n1017│ let result = validate(input);\n1018│ assert!(result.is_valid(), \"Expected valid: {:?}\", result.errors);\n1019│\n1020│ if let Some(line) = result.lines.iter().find(|l| matches!(l.line_type, LineType::Claim(_))) {\n1021│ if let LineType::Claim(claim) = &line.line_type {\n1022│ let m = claim.modifiers.iter().find(|m| m.symbol == '^');\n1023│ assert!(m.is_some(), \"Expected ^ modifier\");\n1024│ assert_eq!(m.unwrap().attached_to, \"concentration\");\n1025│ }\n1026│ }\n1027│ }\n1028│\n1029│ #[test]\n1030│ fn test_modifier_decreasing() {\n1031│ let input = r#\"Trust\n1032│ .trends\n1033│ - institutional-trust v | recent decades\"#;\n1034│\n1035│ let result = validate(input);\n1036│ assert!(result.is_valid(), \"Expected valid: {:?}\", result.errors);\n1037│\n1038│ if let Some(line) = result.lines.iter().find(|l| matches!(l.line_type, LineType::Claim(_))) {\n1039│ if let LineType::Claim(claim) = &line.line_type {\n1040│ let m = claim.modifiers.iter().find(|m| m.symbol == 'v');\n1041│ assert!(m.is_some(), \"Expected v modifier\");\n1042│ }\n1043│ }\n1044│ }\n1045│\n1046│ #[test]\n1047│ fn test_modifier_emphatic() {\n1048│ let input = r#\"Trust\n1049│ .erosion\n1050│ - fast !\"#;\n1051│\n1052│ let result = validate(input);\n1053│ assert!(result.is_valid(), \"Expected valid: {:?}\", result.errors);\n1054│\n1055│ if let Some(line) = result.lines.iter().find(|l| matches!(l.line_type, LineType::Claim(_))) {\n1056│ if let LineType::Claim(claim) = &line.line_type {\n1057│ let m = claim.modifiers.iter().find(|m| m.symbol == '!');\n1058│ assert!(m.is_some(), \"Expected ! modifier\");\n1059│ }\n1060│ }\n1061│ }\n1062│\n1063│ #[test]\n1064│ fn test_modifier_uncertain() {\n1065│ let input = r#\"Philosophy\n1066│ .questions\n1067│ - free-will? @philosophy\"#;\n1068│\n1069│ let result = validate(input);\n1070│ assert!(result.is_valid(), \"Expected valid: {:?}\", result.errors);\n1071│\n1072│ if let Some(line) = result.lines.iter().find(|l| matches!(l.line_type, LineType::Claim(_))) {\n1073│ if let LineType::Claim(claim) = &line.line_type {\n1074│ let m = claim.modifiers.iter().find(|m| m.symbol == '?');\n1075│ assert!(m.is_some(), \"Expected ? modifier\");\n1076│ assert_eq!(m.unwrap().attached_to, \"free-will\");\n1077│ }\n1078│ }\n1079│ }\n1080│\n1081│ #[test]\n1082│ fn test_modifier_notable() {\n1083│ let input = r#\"Change\n1084│ .types\n1085│ - paradigm-shift* | in progress\"#;\n1086│\n1087│ let result = validate(input);\n1088│ assert!(result.is_valid(), \"Expected valid: {:?}\", result.errors);\n1089│\n1090│ if let Some(line) = result.lines.iter().find(|l| matches!(l.line_type, LineType::Claim(_))) {\n1091│ if let LineType::Claim(claim) = &line.line_type {\n1092│ let m = claim.modifiers.iter().find(|m| m.symbol == '*');\n1093│ assert!(m.is_some(), \"Expected * modifier\");\n1094│ }\n1095│ }\n1096│ }\n1097│\n1098│ #[test]\n1099│ fn test_standalone_modifier_warning() {\n1100│ // Modifier at start of claim (no preceding term) should warn\n1101│ let input = r#\"Power\n1102│ .core\n1103│ - ^ something\"#;\n1104│\n1105│ let result = validate(input);\n1106│ // Should be valid but with warning\n1107│ assert!(result.is_valid());\n1108│ assert!(result.has_warnings());\n1109│ assert!(result.warnings.iter().any(|e| matches!(e, ValidationError::StandaloneModifier { .. })));\n1110│ }\n1111│\n1112│ #[test]\n1113│ fn test_space_separated_modifier_valid() {\n1114│ // Space-separated modifier following a term is valid (no warning)\n1115│ let input = r#\"Power\n1116│ .core\n1117│ - something ^\"#;\n1118│\n1119│ let result = validate(input);\n1120│ assert!(result.is_valid());\n1121│ // This should NOT produce a warning because ^ follows \"something\"\n1122│ assert!(!result.has_warnings(), \"Expected no warnings: {:?}\", result.warnings);\n1123│ }\n1124│\n1125│ // ==================== Evolution marker tests ====================\n1126│\n1127│ #[test]\n1128│ fn test_evolution_marker() {\n1129│ let input = r#\"Human-nature\n1130│ .cognition\n1131│ - adaptive, context-dependent [<= inherently good]\"#;\n1132│\n1133│ let result = validate(input);\n1134│ assert!(result.is_valid(), \"Expected valid: {:?}\", result.errors);\n1135│\n1136│ if let Some(line) = result.lines.iter().find(|l| matches!(l.line_type, LineType::Claim(_))) {\n1137│ if let LineType::Claim(claim) = &line.line_type {\n1138│ assert!(claim.evolution.is_some(), \"Expected evolution marker\");\n1139│ assert_eq!(claim.evolution.as_ref().unwrap().prior_belief, \"inherently good\");\n1140│ }\n1141│ }\n1142│ }\n1143│\n1144│ #[test]\n1145│ fn test_evolution_marker_rationalizes() {\n1146│ let input = r#\"Human-nature\n1147│ .cognition\n1148│ - rationalizes post-hoc [<= rational actor]\"#;\n1149│\n1150│ let result = validate(input);\n1151│ assert!(result.is_valid(), \"Expected valid: {:?}\", result.errors);\n1152│\n1153│ if let Some(line) = result.lines.iter().find(|l| matches!(l.line_type, LineType::Claim(_))) {\n1154│ if let LineType::Claim(claim) = &line.line_type {\n1155│ assert!(claim.evolution.is_some());\n1156│ assert_eq!(claim.evolution.as_ref().unwrap().prior_belief, \"rational actor\");\n1157│ }\n1158│ }\n1159│ }\n1160│\n1161│ #[test]\n1162│ fn test_unclosed_evolution_marker() {\n1163│ let input = r#\"Human-nature\n1164│ .cognition\n1165│ - adaptive [<= inherently good\"#;\n1166│\n1167│ let result = validate(input);\n1168│ assert!(!result.is_valid());\n1169│ assert!(result.errors.iter().any(|e| matches!(e, ValidationError::UnclosedEvolutionMarker { .. })));\n1170│ }\n1171│\n1172│ #[test]\n1173│ fn test_empty_evolution_marker() {\n1174│ let input = r#\"Human-nature\n1175│ .cognition\n1176│ - adaptive [<= ]\"#;\n1177│\n1178│ let result = validate(input);\n1179│ assert!(!result.is_valid());\n1180│ assert!(result.errors.iter().any(|e| matches!(e, ValidationError::EmptyEvolutionMarker { .. })));\n1181│ }\n1182│\n1183│ // ==================== Full document tests ====================\n1184│\n1185│ #[test]\n1186│ fn test_expanded_document() {\n1187│ let input = r#\"Power\n1188│ .nature\n1189│ - corrupts | unchecked !\n1190│ - reveals character => self-knowledge\n1191│ - concentration^ => abuse^ @historical-pattern\n1192│ .institutional\n1193│ - self-preserving\n1194│ - accountability <> trust &Trust.institutional\n1195│ - diffusion => dilution-of-responsibility\n1196│\n1197│Trust\n1198│ .formation\n1199│ - slow\n1200│ - requires consistency | over time\n1201│ - contextual @personal-experience\n1202│ .erosion\n1203│ - fast !\n1204│ - single violation => collapse?\n1205│ - asymmetric vs formation &Trust.formation\n1206│ .institutional\n1207│ - possible | high transparency\n1208│ - unlikely | low transparency\n1209│ - rational to withhold | unverifiable @game-theory\n1210│\n1211│Human-nature\n1212│ .social\n1213│ - conformist | formal groups\n1214│ - authentic | solitary\n1215│ - status-aware @evolutionary-psychology\n1216│ - coalition-forming\n1217│ .cognition\n1218│ - pattern-seeking\n1219│ - confirmation-biased @cognitive-science\n1220│ - narrative-constructing\n1221│ - rationalizes post-hoc [<= rational actor]\n1222│ .self-perception\n1223│ - overconfident | familiar domains\n1224│ - miscalibrated @Dunning-Kruger\n1225│ - self-deception => comfort &Human-nature.cognition\n1226│\n1227│Institutions\n1228│ .function\n1229│ - stabilize !\n1230│ - preserve knowledge\n1231│ - coordinate action @game-theory\n1232│ .dysfunction\n1233│ - ossify | over time\n1234│ - self-perpetuate // original purpose\n1235│ - capture-by-interests^ @public-choice-theory\"#;\n1236│\n1237│ let result = validate(input);\n1238│ assert!(result.is_valid(), \"Expected valid document: {:?}\", result.errors);\n1239│ }\n1240│}\n", + "background": false + }, + { + "type": "ReadFileBlock", + "call_id": "toolu_01CFwsViwsr5KeK4vKNHtmQV", + "tool_name": "mcp_read_file", + "params": { + "path": "agent/src/main.rs" + }, + "status": "complete", + "text": " 1│//! Worldview Agent CLI - A tool for adding facts to Worldview files using an AI agent\n 2│//!\n 3│//! This CLI accepts plain-text facts and uses an AI agent to properly format\n 4│//! and incorporate them into a Worldview format (.wvf) file.\n 5│\n 6│use anyhow::Result;\n 7│use clap::Parser;\n 8│use codey::{Agent, AgentRuntimeConfig, AgentStep, RequestMode, SimpleTool, ToolRegistry};\n 9│use serde_json::json;\n 10│use std::path::PathBuf;\n 11│use std::sync::Arc;\n 12│\n 13│/// The Worldview format specification (loaded from SPEC.md at compile time)\n 14│const SPEC: &str = include_str!(\"../../SPEC.md\");\n 15│\n 16│/// Task instructions for the agent\n 17│const TASK_INSTRUCTIONS: &str = r#\"\n 18│# Your Task\n 19│\n 20│When given a plain-text fact or statement:\n 21│1. First, read the current Worldview file to understand its structure and existing concepts\n 22│2. Determine if this fact belongs to an existing concept/facet or requires a new one\n 23│3. Format the fact as proper Worldview notation following the specification above\n 24│4. Use the edit_worldview tool to add or modify the appropriate line(s)\n 25│5. After editing, briefly confirm what you added\n 26│\n 27│## Critical: Encode Only What Is Stated\n 28│\n 29│The Worldview file stores information the user explicitly provides for later reference. You must:\n 30│\n 31│- **Only encode information explicitly stated** in the user's fact or statement\n 32│- **Never add supplementary knowledge** from your training data (e.g., if told \"gravity pulls objects toward Earth\", do NOT add acceleration values, formulas, or other physics facts you know)\n 33│- **Never add assumptions or inferences** beyond what was directly stated\n 34│- **Be token-efficient** — the model already has general knowledge; we only need to store what the user specifically wants to remember\n 35│\n 36│The purpose is to capture the user's specific framing and claims, not to build a comprehensive knowledge base. General facts already exist in model weights and don't need to be stored.\n 37│\n 38│## Critical: Reject Ephemeral Events\n 39│\n 40│The Worldview format is designed to store **durable beliefs, values, perspectives, and knowledge** — not transient events or one-time occurrences. You must:\n 41│\n 42│- **Reject ephemeral personal events** like \"I went to the park on Sunday\" or \"I had coffee this morning\"\n 43│- **Reject time-bound occurrences** that describe what happened rather than what the user believes or values\n 44│- **Accept beliefs about events** like \"parks are good for mental health\" or \"Sunday routines matter\"\n 45│\n 46│If given an ephemeral event, respond politely explaining that the Worldview format is for beliefs and perspectives, not personal diary entries, and do NOT modify the file.\n 47│\n 48│Remember the design principles: state over narrative, predictability allows omission, conflict tolerance, freeform vocabulary, and LLM-native density.\n 49│\"#;\n 50│\n 51│/// Build the complete system prompt from spec + task instructions\n 52│fn build_system_prompt() -> String {\n 53│ format!(\n 54│ \"You are a Worldview format agent. Your task is to take plain-text facts or statements and incorporate them into a Worldview file using the proper notation.\\n\\n\\\n 55│ Below is the complete Worldview format specification. Study it carefully before making any edits.\\n\\n\\\n 56│ ---\\n\\n\\\n 57│ {}\\n\\n\\\n 58│ ---\\n\\n\\\n 59│ {}\",\n 60│ SPEC, TASK_INSTRUCTIONS\n 61│ )\n 62│}\n 63│\n 64│/// CLI for adding facts to Worldview files using an AI agent\n 65│#[derive(Parser, Debug)]\n 66│#[command(name = \"worldview\")]\n 67│#[command(about = \"Add facts to Worldview files using an AI agent\")]\n 68│#[command(version)]\n 69│struct Cli {\n 70│ /// The fact or statement to add to the Worldview file\n 71│ #[arg(required = true)]\n 72│ fact: String,\n 73│\n 74│ /// Path to the Worldview file to modify\n 75│ #[arg(short, long, default_value = \"worldview.wvf\")]\n 76│ file: PathBuf,\n 77│\n 78│ /// Model to use (claude-sonnet-4-20250514 or claude-opus-4-5-20251101)\n 79│ #[arg(short, long, default_value = \"claude-sonnet-4-20250514\")]\n 80│ model: String,\n 81│\n 82│ /// Enable verbose output\n 83│ #[arg(short, long)]\n 84│ verbose: bool,\n 85│}\n 86│\n 87│/// Create the read_worldview tool definition\n 88│fn create_read_tool() -> SimpleTool {\n 89│ SimpleTool::new(\n 90│ \"read_worldview\",\n 91│ \"Read the current contents of the Worldview file. Returns the file contents with line numbers prefixed (e.g., ' 1│content'). Use read_worldview first before editing to see current state.\",\n 92│ json!({\n 93│ \"type\": \"object\",\n 94│ \"properties\": {},\n 95│ \"required\": []\n 96│ }),\n 97│ )\n 98│}\n 99│\n 100│/// Create the edit_worldview tool definition\n 101│fn create_edit_tool() -> SimpleTool {\n 102│ SimpleTool::new(\n 103│ \"edit_worldview\",\n 104│ r#\"Apply search/replace edits to the Worldview file. Each edit specifies an old_string to find and a new_string to replace it with.\n 105│\n 106│Rules:\n 107│- Each old_string must match exactly (including whitespace/indentation)\n 108│- Each old_string must appear exactly once in the file (include more context if ambiguous)\n 109│- To insert new content, use old_string to match an existing line and include it plus your new lines in new_string\n 110│- To delete content, use an empty new_string\n 111│- Multiple edits are applied sequentially\n 112│\n 113│The tool validates the result against Worldview syntax rules before writing.\"#,\n 114│ json!({\n 115│ \"type\": \"object\",\n 116│ \"properties\": {\n 117│ \"edits\": {\n 118│ \"type\": \"array\",\n 119│ \"description\": \"List of search/replace operations to apply sequentially\",\n 120│ \"items\": {\n 121│ \"type\": \"object\",\n 122│ \"properties\": {\n 123│ \"old_string\": {\n 124│ \"type\": \"string\",\n 125│ \"description\": \"Exact string to find (must be unique in file). Include full lines with proper indentation.\"\n 126│ },\n 127│ \"new_string\": {\n 128│ \"type\": \"string\",\n 129│ \"description\": \"String to replace it with. Use empty string to delete.\"\n 130│ }\n 131│ },\n 132│ \"required\": [\"old_string\", \"new_string\"]\n 133│ }\n 134│ }\n 135│ },\n 136│ \"required\": [\"edits\"]\n 137│ }),\n 138│ )\n 139│}\n 140│\n 141│/// Handle the read_worldview tool call\n 142│fn handle_read_worldview(file_path: &PathBuf) -> String {\n 143│ if !file_path.exists() {\n 144│ return \"File does not exist yet. Use edit_worldview with edits to create it.\".to_string();\n 145│ }\n 146│\n 147│ match std::fs::read_to_string(file_path) {\n 148│ Ok(content) => {\n 149│ // Return with line numbers in codey format\n 150│ content\n 151│ .lines()\n 152│ .enumerate()\n 153│ .map(|(i, line)| format!(\"{:4}│{}\", i + 1, line))\n 154│ .collect::>()\n 155│ .join(\"\\n\")\n 156│ }\n 157│ Err(e) => format!(\"Error reading file: {}\", e),\n 158│ }\n 159│}\n 160│\n 161│/// Handle the edit_worldview tool call\n 162│fn handle_edit_worldview(file_path: &PathBuf, params: &serde_json::Value) -> String {\n 163│ // Parse edits array\n 164│ let edits = match params.get(\"edits\").and_then(|v| v.as_array()) {\n 165│ Some(arr) => arr,\n 166│ None => return \"Error: 'edits' array is required\".to_string(),\n 167│ };\n 168│\n 169│ if edits.is_empty() {\n 170│ return \"Error: 'edits' array cannot be empty\".to_string();\n 171│ }\n 172│\n 173│ // Read current file content (or start empty for new files)\n 174│ let mut content = if file_path.exists() {\n 175│ match std::fs::read_to_string(file_path) {\n 176│ Ok(c) => c,\n 177│ Err(e) => return format!(\"Error reading file: {}\", e),\n 178│ }\n 179│ } else {\n 180│ String::new()\n 181│ };\n 182│\n 183│ // Validate and apply each edit\n 184│ for (i, edit) in edits.iter().enumerate() {\n 185│ let old_string = match edit.get(\"old_string\").and_then(|v| v.as_str()) {\n 186│ Some(s) => s,\n 187│ None => return format!(\"Edit {}: missing 'old_string'\", i + 1),\n 188│ };\n 189│ let new_string = match edit.get(\"new_string\").and_then(|v| v.as_str()) {\n 190│ Some(s) => s,\n 191│ None => return format!(\"Edit {}: missing 'new_string'\", i + 1),\n 192│ };\n 193│\n 194│ // For new files, old_string should be empty to append\n 195│ if content.is_empty() {\n 196│ if !old_string.is_empty() {\n 197│ return format!(\n 198│ \"Edit {}: file is empty, old_string must be empty to create new content\",\n 199│ i + 1\n 200│ );\n 201│ }\n 202│ content = new_string.to_string();\n 203│ continue;\n 204│ }\n 205│\n 206│ // Check that old_string exists and is unique\n 207│ let count = content.matches(old_string).count();\n 208│ match count {\n 209│ 0 => {\n 210│ return format!(\n 211│ \"Edit {}: old_string not found in file. \\\n 212│ Make sure the string matches exactly, including whitespace and indentation.\",\n 213│ i + 1\n 214│ );\n 215│ }\n 216│ 1 => {} // good\n 217│ n => {\n 218│ return format!(\n 219│ \"Edit {}: old_string found {} times (must be unique). \\\n 220│ Include more surrounding context to make the match unique.\",\n 221│ i + 1,\n 222│ n\n 223│ );\n 224│ }\n 225│ }\n 226│\n 227│ // Apply the replacement\n 228│ content = content.replacen(old_string, new_string, 1);\n 229│ }\n 230│\n 231│ // Ensure file ends with newline\n 232│ if !content.is_empty() && !content.ends_with('\\n') {\n 233│ content.push('\\n');\n 234│ }\n 235│\n 236│ // Validate the new content before writing\n 237│ let validation = worldview_validator::validate(&content);\n 238│\n 239│ if !validation.is_valid() {\n 240│ let errors: Vec = validation.errors.iter().map(|e| e.to_string()).collect();\n 241│ return format!(\n 242│ \"Validation failed - file not modified:\\n{}\",\n 243│ errors.join(\"\\n\")\n 244│ );\n 245│ }\n 246│\n 247│ // Write the file\n 248│ if let Err(e) = std::fs::write(file_path, &content) {\n 249│ return format!(\"Error writing file: {}\", e);\n 250│ }\n 251│\n 252│ // Return success with edit count and any warnings\n 253│ let edit_count = edits.len();\n 254│ let base_msg = format!(\n 255│ \"Successfully applied {} edit{}.\",\n 256│ edit_count,\n 257│ if edit_count == 1 { \"\" } else { \"s\" }\n 258│ );\n 259│\n 260│ if validation.has_warnings() {\n 261│ let warnings: Vec = validation.warnings.iter().map(|w| w.to_string()).collect();\n 262│ format!(\"{} Warnings:\\n{}\", base_msg, warnings.join(\"\\n\"))\n 263│ } else {\n 264│ format!(\"{} File validated.\", base_msg)\n 265│ }\n 266│}\n 267│\n 268│/// Handle a tool call from the agent\n 269│fn handle_tool_call(file_path: &PathBuf, tool_name: &str, params: &serde_json::Value) -> String {\n 270│ match tool_name {\n 271│ \"read_worldview\" => handle_read_worldview(file_path),\n 272│ \"edit_worldview\" => handle_edit_worldview(file_path, params),\n 273│ _ => format!(\"Unknown tool: {}\", tool_name),\n 274│ }\n 275│}\n 276│\n 277│#[tokio::main]\n 278│async fn main() -> Result<()> {\n 279│ let cli = Cli::parse();\n 280│ let start_time = std::time::Instant::now();\n 281│\n 282│ // Check for API key\n 283│ if std::env::var(\"ANTHROPIC_API_KEY\").is_err() {\n 284│ eprintln!(\"Error: ANTHROPIC_API_KEY environment variable not set\");\n 285│ std::process::exit(1);\n 286│ }\n 287│\n 288│ // Resolve the file path\n 289│ let file_path = if cli.file.is_absolute() {\n 290│ cli.file.clone()\n 291│ } else {\n 292│ std::env::current_dir()?.join(&cli.file)\n 293│ };\n 294│\n 295│ if cli.verbose {\n 296│ eprintln!(\"[config] Worldview file: {:?}\", file_path);\n 297│ eprintln!(\"[config] Model: {}\", cli.model);\n 298│ eprintln!(\"[config] Fact: {}\", cli.fact);\n 299│ eprintln!(\"[start] Beginning agent execution...\");\n 300│ }\n 301│\n 302│ // Create tool registry with our custom tools\n 303│ let mut registry = ToolRegistry::empty();\n 304│ registry.register(Arc::new(create_read_tool()));\n 305│ registry.register(Arc::new(create_edit_tool()));\n 306│\n 307│ // Configure the agent\n 308│ let config = AgentRuntimeConfig {\n 309│ model: cli.model.clone(),\n 310│ max_tokens: 4096,\n 311│ thinking_budget: 1024, // Minimum required\n 312│ max_retries: 3,\n 313│ compaction_thinking_budget: 2000,\n 314│ };\n 315│\n 316│ // Create the agent with the dynamically built system prompt\n 317│ let system_prompt = build_system_prompt();\n 318│ let mut agent = Agent::new(\n 319│ config,\n 320│ &system_prompt,\n 321│ None, // Use ANTHROPIC_API_KEY env var\n 322│ registry,\n 323│ );\n 324│\n 325│ // Format the user message\n 326│ let user_message = format!(\n 327│ \"Please add this fact to the Worldview file at {:?}:\\n\\n{}\",\n 328│ file_path, cli.fact\n 329│ );\n 330│\n 331│ // Send the request\n 332│ agent.send_request(&user_message, RequestMode::Normal);\n 333│\n 334│ // Process the agent loop\n 335│ let mut tool_call_count = 0;\n 336│ let mut thinking_started = false;\n 337│\n 338│ while let Some(step) = agent.next().await {\n 339│ match step {\n 340│ AgentStep::TextDelta(text) => {\n 341│ if cli.verbose {\n 342│ print!(\"{}\", text);\n 343│ }\n 344│ }\n 345│ AgentStep::ThinkingDelta(thinking) => {\n 346│ if cli.verbose {\n 347│ if !thinking_started {\n 348│ thinking_started = true;\n 349│ eprint!(\"\\n[thinking] \");\n 350│ }\n 351│ eprint!(\"{}\", thinking);\n 352│ }\n 353│ }\n 354│ AgentStep::CompactionDelta(_) => {\n 355│ // Not used in our simple case\n 356│ }\n 357│ AgentStep::ToolRequest(tool_calls) => {\n 358│ if cli.verbose && thinking_started {\n 359│ eprintln!(); // End thinking block\n 360│ thinking_started = false;\n 361│ }\n 362│\n 363│ for call in tool_calls {\n 364│ tool_call_count += 1;\n 365│ let tool_start = std::time::Instant::now();\n 366│\n 367│ if cli.verbose {\n 368│ // Format params nicely for readability\n 369│ let params_str = if call.params.is_object() {\n 370│ serde_json::to_string_pretty(&call.params).unwrap_or_else(|_| format!(\"{:?}\", call.params))\n 371│ } else {\n 372│ format!(\"{:?}\", call.params)\n 373│ };\n 374│ eprintln!(\"\\n[tool:{}] {}\", tool_call_count, call.name);\n 375│ eprintln!(\"[params] {}\", params_str);\n 376│ }\n 377│\n 378│ let result = handle_tool_call(&file_path, &call.name, &call.params);\n 379│\n 380│ if cli.verbose {\n 381│ let tool_elapsed = tool_start.elapsed();\n 382│ eprintln!(\"[result:{}ms] {}\", tool_elapsed.as_millis(), result);\n 383│ }\n 384│\n 385│ agent.submit_tool_result(&call.call_id, result);\n 386│ }\n 387│ }\n 388│ AgentStep::Retrying { attempt, error } => {\n 389│ if cli.verbose {\n 390│ eprintln!(\"[retry] Attempt {} after error: {}\", attempt, error);\n 391│ }\n 392│ }\n 393│ AgentStep::Finished { usage } => {\n 394│ let total_elapsed = start_time.elapsed();\n 395│ if cli.verbose {\n 396│ eprintln!(\"\\n[done] Output: {}, Context: {}\",\n 397│ usage.output_tokens, usage.context_tokens);\n 398│ eprintln!(\"[timing] Total: {}ms, Tool calls: {}\",\n 399│ total_elapsed.as_millis(), tool_call_count);\n 400│ }\n 401│ break;\n 402│ }\n 403│ AgentStep::Error(e) => {\n 404│ let total_elapsed = start_time.elapsed();\n 405│ if cli.verbose {\n 406│ eprintln!(\"[error:{}ms] {}\", total_elapsed.as_millis(), e);\n 407│ }\n 408│ eprintln!(\"Error: {}\", e);\n 409│ std::process::exit(1);\n 410│ }\n 411│ }\n 412│ }\n 413│\n 414│ // Exit 0 for success (including correct rejections)\n 415│ Ok(())\n 416│}\n", + "background": false + }, + { + "type": "ReadFileBlock", + "call_id": "toolu_01ELu7FSAb34X4EsoQZ3QsJH", + "tool_name": "mcp_read_file", + "params": { + "path": "spec/tokens.yaml" + }, + "status": "complete", + "text": " 1│# Worldview Format Token Definitions\n 2│#\n 3│# This is the canonical source for all operators, modifiers, and syntax elements.\n 4│# Generated files (docs, validator constants, system prompts) derive from this.\n 5│\n 6│version: \"0.1\"\n 7│\n 8│# Document structure\n 9│structure:\n 10│ concept:\n 11│ indent: 0\n 12│ prefix: null\n 13│ description: \"A subject of belief—a noun in the worldview\"\n 14│ examples: [\"Power\", \"Trust\", \"Human-nature\"]\n 15│\n 16│ facet:\n 17│ indent: 2\n 18│ prefix: \".\"\n 19│ description: \"An aspect or dimension of a concept\"\n 20│ examples: [\".nature\", \".formation\", \".erosion\"]\n 21│\n 22│ claim:\n 23│ indent: 4\n 24│ prefix: \"-\"\n 25│ description: \"An assertion about a facet—what is believed to be true\"\n 26│ examples: [\"- corrupts | unchecked\", \"- slow\"]\n 27│\n 28│# Inline elements (positional grammar)\n 29│inline_elements:\n 30│ - symbol: \"|\"\n 31│ name: \"condition\"\n 32│ meaning: \"when/if applies\"\n 33│ position: \"after claim\"\n 34│ example: \"- corrupts | unchecked\"\n 35│\n 36│ - symbol: \"@\"\n 37│ name: \"source\"\n 38│ meaning: \"basis for belief\"\n 39│ position: \"after claim/conditions\"\n 40│ example: \"@historical-pattern\"\n 41│\n 42│ - symbol: \"&\"\n 43│ name: \"reference\"\n 44│ meaning: \"links to other concept.facet\"\n 45│ position: \"after claim\"\n 46│ format: \"&Concept.facet\"\n 47│ example: \"&Trust.formation\"\n 48│\n 49│# Brief forms - compact relationship operators\n 50│brief_forms:\n 51│ - symbol: \"=>\"\n 52│ meaning: \"causes, leads to\"\n 53│ example: \"power => corruption\"\n 54│\n 55│ - symbol: \"<=\"\n 56│ meaning: \"caused by, results from\"\n 57│ example: \"trust <= consistency\"\n 58│\n 59│ - symbol: \"<>\"\n 60│ meaning: \"mutual, bidirectional\"\n 61│ example: \"accountability <> trust\"\n 62│\n 63│ - symbol: \"><\"\n 64│ meaning: \"tension, conflicts with\"\n 65│ example: \"efficiency >< thoroughness\"\n 66│\n 67│ - symbol: \"~\"\n 68│ meaning: \"similar to, resembles\"\n 69│ example: \"authority ~ influence\"\n 70│\n 71│ - symbol: \"=\"\n 72│ meaning: \"equivalent to, means\"\n 73│ example: \"formal = official\"\n 74│\n 75│ - symbol: \"vs\"\n 76│ meaning: \"in contrast to\"\n 77│ example: \"asymmetric vs formation\"\n 78│\n 79│ - symbol: \"//\"\n 80│ meaning: \"regardless of\"\n 81│ example: \"self-perpetuate // original purpose\"\n 82│\n 83│# Modifiers - suffix markers that inflect meaning\n 84│modifiers:\n 85│ - symbol: \"^\"\n 86│ meaning: \"increasing, trending up\"\n 87│ example: \"concentration^\"\n 88│\n 89│ - symbol: \"v\"\n 90│ meaning: \"decreasing, trending down\"\n 91│ example: \"trust v\"\n 92│\n 93│ - symbol: \"!\"\n 94│ meaning: \"strong, emphatic, high confidence\"\n 95│ example: \"fast !\"\n 96│\n 97│ - symbol: \"?\"\n 98│ meaning: \"uncertain, contested, tentative\"\n 99│ example: \"free-will?\"\n 100│\n 101│ - symbol: \"*\"\n 102│ meaning: \"notable, important, flagged\"\n 103│ example: \"paradigm-shift*\"\n 104│\n 105│# Evolution markers\n 106│evolution:\n 107│ supersession:\n 108│ syntax: \"[<= prior belief]\"\n 109│ meaning: \"current belief supersedes prior belief\"\n 110│ example: \"- adaptive [<= inherently good]\"\n 111│\n 112│# Positional grammar order\n 113│claim_order:\n 114│ - element: \"claim_text\"\n 115│ required: true\n 116│ - element: \"conditions\"\n 117│ required: false\n 118│ prefix: \"|\"\n 119│ multiple: true\n 120│ - element: \"sources\"\n 121│ required: false\n 122│ prefix: \"@\"\n 123│ multiple: true\n 124│ - element: \"references\"\n 125│ required: false\n 126│ prefix: \"&\"\n 127│ multiple: true\n", + "background": false + }, + { + "type": "ThinkingBlock", + "text": "Now I have a solid understanding of the project. Let me also look at the Python evals and the build system to get the full picture, then I can provide a quality assessment.", + "status": "complete" + }, + { + "type": "TextBlock", + "text": "Excellent! Now let me check the Python evals and the build system:", + "status": "complete" + }, + { + "type": "ReadFileBlock", + "call_id": "toolu_01Ea5rQQBLNVgxU9oPf1hg23", + "tool_name": "mcp_read_file", + "params": { + "path": "evals/cli.py" + }, + "status": "complete", + "text": " 1│#!/usr/bin/env python3\n 2│\"\"\"\n 3│Worldview Evaluation CLI\n 4│\n 5│Run LLM evaluations against Worldview-encoded beliefs.\n 6│\n 7│Usage:\n 8│ python -m evals.cli --help\n 9│ python -m evals.cli run --models claude-sonnet gpt-4o\n 10│ python -m evals.cli run --difficulty extreme\n 11│ python -m evals.cli write-eval --models claude-sonnet claude-haiku\n 12│ python -m evals.cli write-eval --complexity simple\n 13│ python -m evals.cli list-models\n 14│ python -m evals.cli list-cases\n 15│\"\"\"\n 16│\n 17│import argparse\n 18│import sys\n 19│from pathlib import Path\n 20│\n 21│\n 22│def cmd_run(args):\n 23│ \"\"\"Run evaluations.\"\"\"\n 24│ from .common.config import ALL_MODELS, DEFAULT_MODELS, get_model, MODEL_REGISTRY\n 25│ from .read_eval.runner import EvalRunner, generate_report, generate_json_results\n 26│ from .read_eval.test_cases import ALL_TEST_CASES, Difficulty, get_cases_by_difficulty\n 27│\n 28│ # Determine models to use\n 29│ if args.models:\n 30│ models = []\n 31│ for name in args.models:\n 32│ model = get_model(name)\n 33│ if model:\n 34│ models.append(model)\n 35│ else:\n 36│ print(f\"Warning: Unknown model '{name}', skipping\")\n 37│ if not models:\n 38│ print(\"Error: No valid models specified\")\n 39│ sys.exit(1)\n 40│ elif args.all_models:\n 41│ models = ALL_MODELS\n 42│ else:\n 43│ models = DEFAULT_MODELS\n 44│\n 45│ # Determine test cases\n 46│ if args.difficulty:\n 47│ try:\n 48│ difficulty = Difficulty(args.difficulty)\n 49│ test_cases = get_cases_by_difficulty(difficulty)\n 50│ except ValueError:\n 51│ print(f\"Error: Unknown difficulty '{args.difficulty}'\")\n 52│ print(f\"Valid options: {[d.value for d in Difficulty]}\")\n 53│ sys.exit(1)\n 54│ elif args.cases:\n 55│ from .read_eval.test_cases import get_case_by_id\n 56│ test_cases = []\n 57│ for case_id in args.cases:\n 58│ tc = get_case_by_id(case_id)\n 59│ if tc:\n 60│ test_cases.append(tc)\n 61│ else:\n 62│ print(f\"Warning: Unknown test case '{case_id}', skipping\")\n 63│ if not test_cases:\n 64│ print(\"Error: No valid test cases specified\")\n 65│ sys.exit(1)\n 66│ else:\n 67│ test_cases = ALL_TEST_CASES\n 68│\n 69│ print(f\"Running {len(test_cases)} test cases against {len(models)} models\")\n 70│ print(f\"Models: {[m.display_name for m in models]}\")\n 71│ print()\n 72│\n 73│ # Create runner\n 74│ runner = EvalRunner(\n 75│ models=models,\n 76│ use_cli_tool=args.use_cli,\n 77│ worldview_cli_path=args.worldview_cli,\n 78│ verbose=args.verbose,\n 79│ )\n 80│\n 81│ # Run evaluations\n 82│ results = runner.run_all(test_cases=test_cases)\n 83│\n 84│ # Generate outputs\n 85│ if args.output:\n 86│ output_path = Path(args.output)\n 87│\n 88│ # Generate report\n 89│ report_path = output_path / \"report.md\"\n 90│ report = generate_report(results, str(report_path))\n 91│ print(f\"\\nReport written to: {report_path}\")\n 92│\n 93│ # Generate JSON\n 94│ json_path = output_path / \"results.json\"\n 95│ generate_json_results(results, str(json_path))\n 96│ print(f\"JSON results written to: {json_path}\")\n 97│ else:\n 98│ # Print report to stdout\n 99│ print(\"\\n\" + \"=\" * 60)\n 100│ report = generate_report(results)\n 101│ print(report)\n 102│\n 103│\n 104│def cmd_write_eval(args):\n 105│ \"\"\"Run write evaluations (embedding model benchmark).\"\"\"\n 106│ from .write_eval.runner import (\n 107│ WriteEvalRunner,\n 108│ WRITE_MODELS,\n 109│ DEFAULT_WRITE_MODELS,\n 110│ get_write_model,\n 111│ generate_write_report,\n 112│ generate_write_json,\n 113│ )\n 114│ from .write_eval.test_cases import (\n 115│ ALL_WRITE_CASES,\n 116│ Complexity,\n 117│ get_cases_by_complexity,\n 118│ get_case_by_id,\n 119│ )\n 120│\n 121│ # Determine models to use\n 122│ if args.models:\n 123│ model_names = args.models\n 124│ elif args.all_models:\n 125│ model_names = [m[\"name\"] for m in WRITE_MODELS]\n 126│ else:\n 127│ model_names = DEFAULT_WRITE_MODELS\n 128│\n 129│ # Validate models\n 130│ valid_models = []\n 131│ for name in model_names:\n 132│ if get_write_model(name):\n 133│ valid_models.append(name)\n 134│ else:\n 135│ print(f\"Warning: Unknown model '{name}', skipping\")\n 136│\n 137│ if not valid_models:\n 138│ print(\"Error: No valid models specified\")\n 139│ print(f\"Available models: {[m['name'] for m in WRITE_MODELS]}\")\n 140│ sys.exit(1)\n 141│\n 142│ # Determine test cases\n 143│ if args.complexity:\n 144│ try:\n 145│ complexity = Complexity(args.complexity)\n 146│ test_cases = get_cases_by_complexity(complexity)\n 147│ except ValueError:\n 148│ print(f\"Error: Unknown complexity '{args.complexity}'\")\n 149│ print(f\"Valid options: {[c.value for c in Complexity]}\")\n 150│ sys.exit(1)\n 151│ elif args.cases:\n 152│ test_cases = []\n 153│ for case_id in args.cases:\n 154│ tc = get_case_by_id(case_id)\n 155│ if tc:\n 156│ test_cases.append(tc)\n 157│ else:\n 158│ print(f\"Warning: Unknown test case '{case_id}', skipping\")\n 159│ if not test_cases:\n 160│ print(\"Error: No valid test cases specified\")\n 161│ sys.exit(1)\n 162│ else:\n 163│ test_cases = ALL_WRITE_CASES\n 164│\n 165│ print(f\"Running {len(test_cases)} write test cases against {len(valid_models)} models\")\n 166│ print(f\"Models: {valid_models}\")\n 167│ print()\n 168│\n 169│ # Create runner\n 170│ runner = WriteEvalRunner(\n 171│ models=valid_models,\n 172│ agent_cli_path=args.agent_cli,\n 173│ validator_path=args.validator,\n 174│ verbose=args.verbose,\n 175│ )\n 176│\n 177│ # Run evaluations\n 178│ results = runner.run_all(test_cases=test_cases)\n 179│\n 180│ # Generate outputs\n 181│ if args.output:\n 182│ output_path = Path(args.output)\n 183│ output_path.mkdir(parents=True, exist_ok=True)\n 184│\n 185│ # Generate report\n 186│ report_path = output_path / \"write_report.md\"\n 187│ report = generate_write_report(results, str(report_path))\n 188│ print(f\"\\nReport written to: {report_path}\")\n 189│\n 190│ # Generate JSON\n 191│ json_path = output_path / \"write_results.json\"\n 192│ generate_write_json(results, str(json_path))\n 193│ print(f\"JSON results written to: {json_path}\")\n 194│ else:\n 195│ # Print report to stdout\n 196│ print(\"\\n\" + \"=\" * 60)\n 197│ report = generate_write_report(results)\n 198│ print(report)\n 199│\n 200│\n 201│def cmd_list_models(args):\n 202│ \"\"\"List available models.\"\"\"\n 203│ from .common.config import ALL_MODELS, DEFAULT_MODELS\n 204│ from .write_eval.runner import WRITE_MODELS, DEFAULT_WRITE_MODELS\n 205│\n 206│ default_ids = {m.model_id for m in DEFAULT_MODELS}\n 207│\n 208│ print(\"Read Evaluation Models (for testing LLM response to Worldview context):\\n\")\n 209│ print(f\"{'Name':<20} {'Provider':<12} {'Model ID':<35} {'Default'}\")\n 210│ print(\"-\" * 80)\n 211│\n 212│ for model in ALL_MODELS:\n 213│ default = \"Yes\" if model.model_id in default_ids else \"\"\n 214│ print(f\"{model.display_name:<20} {model.provider.value:<12} {model.model_id:<35} {default}\")\n 215│\n 216│ print(\"\\n\")\n 217│ print(\"Write Evaluation Models (for testing Worldview document generation):\\n\")\n 218│ print(f\"{'Name':<20} {'Model ID':<40} {'Default'}\")\n 219│ print(\"-\" * 70)\n 220│\n 221│ for model in WRITE_MODELS:\n 222│ default = \"Yes\" if model[\"name\"] in DEFAULT_WRITE_MODELS else \"\"\n 223│ print(f\"{model['display_name']:<20} {model['model_id']:<40} {default}\")\n 224│\n 225│\n 226│def cmd_list_cases(args):\n 227│ \"\"\"List available test cases.\"\"\"\n 228│ from .read_eval.test_cases import ALL_TEST_CASES, Difficulty\n 229│ from .write_eval.test_cases import ALL_WRITE_CASES, Complexity\n 230│\n 231│ print(\"Read Evaluation Test Cases (testing LLM response to Worldview context):\\n\")\n 232│\n 233│ for difficulty in Difficulty:\n 234│ cases = [tc for tc in ALL_TEST_CASES if tc.difficulty == difficulty]\n 235│ print(f\"\\n{difficulty.value.upper()} ({len(cases)} cases):\")\n 236│ print(\"-\" * 40)\n 237│ for tc in cases:\n 238│ print(f\" {tc.id:<30} {tc.name}\")\n 239│\n 240│ print(\"\\n\")\n 241│ print(\"=\" * 60)\n 242│ print(\"\\nWrite Evaluation Test Cases (testing document generation):\\n\")\n 243│\n 244│ for complexity in Complexity:\n 245│ cases = [tc for tc in ALL_WRITE_CASES if tc.complexity == complexity]\n 246│ print(f\"\\n{complexity.value.upper()} ({len(cases)} cases):\")\n 247│ print(\"-\" * 40)\n 248│ for tc in cases:\n 249│ print(f\" {tc.id:<30} {tc.name}\")\n 250│\n 251│\n 252│def main():\n 253│ parser = argparse.ArgumentParser(\n 254│ description=\"Worldview LLM Evaluation Framework\",\n 255│ formatter_class=argparse.RawDescriptionHelpFormatter,\n 256│ )\n 257│ subparsers = parser.add_subparsers(dest=\"command\", help=\"Commands\")\n 258│\n 259│ # Run command\n 260│ run_parser = subparsers.add_parser(\"run\", help=\"Run evaluations\")\n 261│ run_parser.add_argument(\n 262│ \"--models\",\n 263│ nargs=\"+\",\n 264│ help=\"Models to evaluate (e.g., claude-sonnet gpt-4o)\",\n 265│ )\n 266│ run_parser.add_argument(\n 267│ \"--all-models\",\n 268│ action=\"store_true\",\n 269│ help=\"Run against all available models\",\n 270│ )\n 271│ run_parser.add_argument(\n 272│ \"--difficulty\",\n 273│ choices=[\"baseline\", \"moderate\", \"extreme\"],\n 274│ help=\"Run only cases of specific difficulty\",\n 275│ )\n 276│ run_parser.add_argument(\n 277│ \"--cases\",\n 278│ nargs=\"+\",\n 279│ help=\"Specific test case IDs to run\",\n 280│ )\n 281│ run_parser.add_argument(\n 282│ \"--output\", \"-o\",\n 283│ help=\"Output directory for report and results\",\n 284│ )\n 285│ run_parser.add_argument(\n 286│ \"--use-cli\",\n 287│ action=\"store_true\",\n 288│ help=\"Use Worldview CLI tool to generate content (slower, full integration)\",\n 289│ )\n 290│ run_parser.add_argument(\n 291│ \"--worldview-cli\",\n 292│ default=\"worldview\",\n 293│ help=\"Path to Worldview CLI tool\",\n 294│ )\n 295│ run_parser.add_argument(\n 296│ \"--verbose\", \"-v\",\n 297│ action=\"store_true\",\n 298│ help=\"Print detailed output\",\n 299│ )\n 300│ run_parser.set_defaults(func=cmd_run)\n 301│\n 302│ # Write evaluation command\n 303│ write_parser = subparsers.add_parser(\n 304│ \"write-eval\",\n 305│ help=\"Run write evaluations (benchmark embedding models on document generation)\"\n 306│ )\n 307│ write_parser.add_argument(\n 308│ \"--models\",\n 309│ nargs=\"+\",\n 310│ help=\"Models to evaluate (e.g., claude-sonnet claude-haiku)\",\n 311│ )\n 312│ write_parser.add_argument(\n 313│ \"--all-models\",\n 314│ action=\"store_true\",\n 315│ help=\"Run against all available write models\",\n 316│ )\n 317│ write_parser.add_argument(\n 318│ \"--complexity\",\n 319│ choices=[\"simple\", \"moderate\", \"complex\"],\n 320│ help=\"Run only cases of specific complexity\",\n 321│ )\n 322│ write_parser.add_argument(\n 323│ \"--cases\",\n 324│ nargs=\"+\",\n 325│ help=\"Specific test case IDs to run\",\n 326│ )\n 327│ write_parser.add_argument(\n 328│ \"--output\", \"-o\",\n 329│ help=\"Output directory for report and results\",\n 330│ )\n 331│ write_parser.add_argument(\n 332│ \"--agent-cli\",\n 333│ default=\"worldview\",\n 334│ help=\"Path to Worldview agent CLI tool\",\n 335│ )\n 336│ write_parser.add_argument(\n 337│ \"--validator\",\n 338│ help=\"Path to Worldview validator binary (optional)\",\n 339│ )\n 340│ write_parser.add_argument(\n 341│ \"--verbose\", \"-v\",\n 342│ action=\"store_true\",\n 343│ help=\"Print detailed output including agent interactions\",\n 344│ )\n 345│ write_parser.set_defaults(func=cmd_write_eval)\n 346│\n 347│ # List models command\n 348│ models_parser = subparsers.add_parser(\"list-models\", help=\"List available models\")\n 349│ models_parser.set_defaults(func=cmd_list_models)\n 350│\n 351│ # List cases command\n 352│ cases_parser = subparsers.add_parser(\"list-cases\", help=\"List available test cases\")\n 353│ cases_parser.set_defaults(func=cmd_list_cases)\n 354│\n 355│ args = parser.parse_args()\n 356│\n 357│ if not args.command:\n 358│ parser.print_help()\n 359│ sys.exit(1)\n 360│\n 361│ args.func(args)\n 362│\n 363│\n 364│if __name__ == \"__main__\":\n 365│ main()\n", + "background": false + }, + { + "type": "ReadFileBlock", + "call_id": "toolu_019ttfpsAorMeypABxtGkwyA", + "tool_name": "mcp_read_file", + "params": { + "path": "validator/build.rs" + }, + "status": "complete", + "text": " 1│//! Build script for worldview-validator\n 2│//! Generates token definitions from spec/tokens.yaml at compile time.\n 3│\n 4│use std::env;\n 5│use std::fs;\n 6│use std::path::Path;\n 7│\n 8│fn main() {\n 9│ let out_dir = env::var(\"OUT_DIR\").unwrap();\n 10│ let dest_path = Path::new(&out_dir).join(\"tokens.rs\");\n 11│\n 12│ // Read tokens.yaml from spec directory\n 13│ let manifest_dir = env::var(\"CARGO_MANIFEST_DIR\").unwrap();\n 14│ let tokens_path = Path::new(&manifest_dir)\n 15│ .parent()\n 16│ .unwrap()\n 17│ .join(\"spec/tokens.yaml\");\n 18│\n 19│ println!(\"cargo:rerun-if-changed={}\", tokens_path.display());\n 20│\n 21│ let tokens_yaml = fs::read_to_string(&tokens_path).expect(\"Failed to read spec/tokens.yaml\");\n 22│\n 23│ // Parse YAML manually (avoiding serde dependency in build script)\n 24│ let rust_code = generate_tokens_rs(&tokens_yaml);\n 25│\n 26│ fs::write(&dest_path, rust_code).unwrap();\n 27│}\n 28│\n 29│fn generate_tokens_rs(yaml: &str) -> String {\n 30│ let mut output = String::from(\"// Auto-generated from spec/tokens.yaml at compile time\\n\\n\");\n 31│\n 32│ // Parse brief_forms\n 33│ let mut brief_forms = Vec::new();\n 34│ let mut in_brief_forms = false;\n 35│ let mut current_symbol = String::new();\n 36│ let mut current_meaning = String::new();\n 37│\n 38│ for line in yaml.lines() {\n 39│ let trimmed = line.trim();\n 40│\n 41│ if trimmed == \"brief_forms:\" {\n 42│ in_brief_forms = true;\n 43│ continue;\n 44│ }\n 45│\n 46│ if in_brief_forms {\n 47│ if trimmed.starts_with(\"- symbol:\") {\n 48│ if !current_symbol.is_empty() {\n 49│ brief_forms.push((current_symbol.clone(), current_meaning.clone()));\n 50│ }\n 51│ current_symbol = trimmed\n 52│ .trim_start_matches(\"- symbol:\")\n 53│ .trim()\n 54│ .trim_matches('\"')\n 55│ .to_string();\n 56│ current_meaning.clear();\n 57│ } else if trimmed.starts_with(\"meaning:\") {\n 58│ current_meaning = trimmed\n 59│ .trim_start_matches(\"meaning:\")\n 60│ .trim()\n 61│ .trim_matches('\"')\n 62│ .to_string();\n 63│ } else if trimmed.starts_with(\"modifiers:\") {\n 64│ if !current_symbol.is_empty() {\n 65│ brief_forms.push((current_symbol.clone(), current_meaning.clone()));\n 66│ }\n 67│ break;\n 68│ }\n 69│ }\n 70│ }\n 71│\n 72│ // Generate BRIEF_FORMS\n 73│ output.push_str(\"/// Brief form operators defined in the Worldview spec\\n\");\n 74│ output.push_str(\"pub const BRIEF_FORMS: &[(&str, &str)] = &[\\n\");\n 75│ for (sym, meaning) in &brief_forms {\n 76│ output.push_str(&format!(\" (\\\"{}\\\", \\\"{}\\\"),\\n\", sym, meaning));\n 77│ }\n 78│ output.push_str(\"];\\n\\n\");\n 79│\n 80│ // Parse modifiers\n 81│ let mut modifiers = Vec::new();\n 82│ let mut in_modifiers = false;\n 83│ current_symbol.clear();\n 84│ current_meaning.clear();\n 85│\n 86│ for line in yaml.lines() {\n 87│ let trimmed = line.trim();\n 88│\n 89│ if trimmed == \"modifiers:\" {\n 90│ in_modifiers = true;\n 91│ continue;\n 92│ }\n 93│\n 94│ if in_modifiers {\n 95│ if trimmed.starts_with(\"- symbol:\") {\n 96│ if !current_symbol.is_empty() {\n 97│ modifiers.push((current_symbol.clone(), current_meaning.clone()));\n 98│ }\n 99│ current_symbol = trimmed\n 100│ .trim_start_matches(\"- symbol:\")\n 101│ .trim()\n 102│ .trim_matches('\"')\n 103│ .to_string();\n 104│ current_meaning.clear();\n 105│ } else if trimmed.starts_with(\"meaning:\") {\n 106│ current_meaning = trimmed\n 107│ .trim_start_matches(\"meaning:\")\n 108│ .trim()\n 109│ .trim_matches('\"')\n 110│ .to_string();\n 111│ } else if trimmed.starts_with(\"evolution:\") || trimmed.starts_with(\"claim_order:\") {\n 112│ if !current_symbol.is_empty() {\n 113│ modifiers.push((current_symbol.clone(), current_meaning.clone()));\n 114│ }\n 115│ break;\n 116│ }\n 117│ }\n 118│ }\n 119│\n 120│ // Generate MODIFIERS\n 121│ output.push_str(\"/// Modifier symbols defined in the Worldview spec\\n\");\n 122│ output.push_str(\"pub const MODIFIERS: &[(&str, &str)] = &[\\n\");\n 123│ for (sym, meaning) in &modifiers {\n 124│ output.push_str(&format!(\" (\\\"{}\\\", \\\"{}\\\"),\\n\", sym, meaning));\n 125│ }\n 126│ output.push_str(\"];\\n\");\n 127│\n 128│ output\n 129│}\n", + "background": false + }, + { + "type": "ShellBlock", + "call_id": "toolu_01KMBM6sic3gobGvHDB5XKRv", + "tool_name": "mcp_shell", + "params": { + "command": "ls -la evals/" + }, + "status": "complete", + "text": "total 48\ndrwxr-xr-x@ 9 tcdent staff 288 Jan 21 10:11 .\ndrwxr-xr-x@ 14 tcdent staff 448 Jan 21 15:02 ..\n-rw-r--r--@ 1 tcdent staff 360 Jan 21 10:11 __init__.py\n-rw-r--r--@ 1 tcdent staff 101 Jan 21 10:11 __main__.py\n-rw-r--r--@ 1 tcdent staff 11372 Jan 21 10:11 cli.py\ndrwxr-xr-x@ 5 tcdent staff 160 Jan 21 10:11 common\ndrwxr-xr-x@ 7 tcdent staff 224 Jan 21 10:11 read_eval\n-rw-r--r--@ 1 tcdent staff 227 Jan 21 10:11 requirements.txt\ndrwxr-xr-x@ 6 tcdent staff 192 Jan 21 10:11 write_eval\n", + "background": false + }, + { + "type": "ThinkingBlock", + "text": "Let me get a bit more context on the Python eval structure and check if there are any obvious issues. Let me also try building the Rust code to see if it compiles.", + "status": "complete" + }, + { + "type": "TextBlock", + "text": "Let me check if this builds and run a quick test, and also look at a few more key files:", + "status": "complete" + }, + { + "type": "ShellBlock", + "call_id": "toolu_015d31dZG8cEPzF9LrepZ3rE", + "tool_name": "mcp_shell", + "params": { + "command": "cd validator && cargo check 2>&1 | head -30" + }, + "status": "complete", + "text": " Updating crates.io index\n Locking 6 packages to latest Rust 1.92.0 compatible versions\n Adding thiserror v1.0.69 (available: v2.0.18)\n Downloading crates ...\n Downloaded proc-macro2 v1.0.106\n Compiling proc-macro2 v1.0.106\n Compiling unicode-ident v1.0.22\n Compiling quote v1.0.43\n Compiling thiserror v1.0.69\n Compiling worldview-validator v0.1.0 (/Users/tcdent/Work/wvf/validator)\n Compiling syn v2.0.114\n Compiling thiserror-impl v1.0.69\n Finished `dev` profile [unoptimized + debuginfo] target(s) in 2.93s\n", + "background": false + }, + { + "type": "ReadFileBlock", + "call_id": "toolu_01CW25Eai1PzTT89KX6vHoD1", + "tool_name": "mcp_read_file", + "params": { + "path": "evals/read_eval/test_cases.py" + }, + "status": "complete", + "text": " 1│\"\"\"\n 2│Worldview Evaluation Test Cases\n 3│\n 4│Test cases ranging from baseline facts to extreme alternative worldviews.\n 5│Each case tests how well LLMs can leverage Worldview-encoded beliefs to answer questions.\n 6│\"\"\"\n 7│\n 8│from dataclasses import dataclass, field\n 9│from enum import Enum\n 10│from typing import Optional\n 11│\n 12│\n 13│class Difficulty(Enum):\n 14│ \"\"\"\n 15│ Test difficulty based on how much the belief diverges from mainstream knowledge.\n 16│\n 17│ BASELINE: Facts aligned with LLM training data\n 18│ MODERATE: Alternative but plausible perspectives\n 19│ EXTREME: Counter-factual beliefs that contradict training\n 20│ \"\"\"\n 21│\n 22│ BASELINE = \"baseline\"\n 23│ MODERATE = \"moderate\"\n 24│ EXTREME = \"extreme\"\n 25│\n 26│\n 27│class Category(Enum):\n 28│ \"\"\"Test case categories for organization and filtering.\"\"\"\n 29│\n 30│ FACTUAL = \"factual\" # Historical/scientific facts\n 31│ PHILOSOPHICAL = \"philosophical\" # Worldview/values\n 32│ TECHNICAL = \"technical\" # Technical domain knowledge\n 33│ SOCIAL = \"social\" # Social/cultural beliefs\n 34│\n 35│\n 36│@dataclass\n 37│class ExpectedBehavior:\n 38│ \"\"\"What we expect the LLM to do with Worldview context.\"\"\"\n 39│\n 40│ should_align_with_worldview: bool = True\n 41│ key_terms: list[str] = field(default_factory=list)\n 42│ forbidden_terms: list[str] = field(default_factory=list)\n 43│ notes: Optional[str] = None\n 44│\n 45│\n 46│@dataclass\n 47│class TestCase:\n 48│ \"\"\"\n 49│ A single Worldview evaluation test case.\n 50│\n 51│ Attributes:\n 52│ id: Unique identifier\n 53│ name: Human-readable name\n 54│ difficulty: How much this challenges LLM base knowledge\n 55│ category: Type of belief being tested\n 56│ fact_statement: Conversational statement to add via Worldview CLI\n 57│ wsl_content: Expected/provided Worldview content after adding fact\n 58│ question: Question to ask the LLM\n 59│ expected: What behavior we expect from the LLM\n 60│ \"\"\"\n 61│\n 62│ id: str\n 63│ name: str\n 64│ difficulty: Difficulty\n 65│ category: Category\n 66│ fact_statement: str\n 67│ wsl_content: str\n 68│ question: str\n 69│ expected: ExpectedBehavior\n 70│\n 71│\n 72│# =============================================================================\n 73│# BASELINE TEST CASES\n 74│# Facts that align with LLM training - verifies Worldview doesn't interfere\n 75│# =============================================================================\n 76│\n 77│BASELINE_CASES = [\n 78│ TestCase(\n 79│ id=\"baseline-gravity\",\n 80│ name=\"Gravity causes objects to fall\",\n 81│ difficulty=Difficulty.BASELINE,\n 82│ category=Category.FACTUAL,\n 83│ fact_statement=\"Objects fall because gravity pulls them toward Earth\",\n 84│ wsl_content=\"\"\"Physics\n 85│ .gravity\n 86│ - objects fall => attracted to Earth\n 87│ - universal force\n 88│ - acceleration ~ 9.8 m/s^2 | near Earth surface\"\"\",\n 89│ question=\"Why do objects fall when dropped?\",\n 90│ expected=ExpectedBehavior(\n 91│ should_align_with_worldview=True,\n 92│ key_terms=[\"gravity\", \"Earth\", \"attraction\", \"pull\"],\n 93│ notes=\"Baseline case - Worldview aligns with training\",\n 94│ ),\n 95│ ),\n 96│ TestCase(\n 97│ id=\"baseline-water-boiling\",\n 98│ name=\"Water boils at 100C\",\n 99│ difficulty=Difficulty.BASELINE,\n 100│ category=Category.FACTUAL,\n 101│ fact_statement=\"Water boils at 100 degrees Celsius at sea level\",\n 102│ wsl_content=\"\"\"Water\n 103│ .phase-transitions\n 104│ - boils | 100C at sea level\n 105│ - freezes | 0C at sea level\n 106│ - pressure => boiling-point-change\"\"\",\n 107│ question=\"At what temperature does water boil at sea level?\",\n 108│ expected=ExpectedBehavior(\n 109│ should_align_with_worldview=True,\n 110│ key_terms=[\"100\", \"Celsius\", \"sea level\"],\n 111│ notes=\"Baseline case - Worldview aligns with training\",\n 112│ ),\n 113│ ),\n 114│ TestCase(\n 115│ id=\"baseline-trust\",\n 116│ name=\"Trust takes time to build\",\n 117│ difficulty=Difficulty.BASELINE,\n 118│ category=Category.PHILOSOPHICAL,\n 119│ fact_statement=\"Trust is built slowly through consistent actions over time\",\n 120│ wsl_content=\"\"\"Trust\n 121│ .formation\n 122│ - slow\n 123│ - requires consistency | over time\n 124│ - contextual @personal-experience\n 125│ .erosion\n 126│ - fast !\n 127│ - single violation => collapse?\"\"\",\n 128│ question=\"How is trust typically formed between people?\",\n 129│ expected=ExpectedBehavior(\n 130│ should_align_with_worldview=True,\n 131│ key_terms=[\"slow\", \"consistency\", \"time\"],\n 132│ notes=\"Baseline case - common wisdom about trust\",\n 133│ ),\n 134│ ),\n 135│]\n 136│\n 137│# =============================================================================\n 138│# MODERATE TEST CASES\n 139│# Alternative perspectives that are plausible but not mainstream\n 140│# =============================================================================\n 141│\n 142│MODERATE_CASES = [\n 143│ TestCase(\n 144│ id=\"moderate-sleep-polyphasic\",\n 145│ name=\"Polyphasic sleep is optimal\",\n 146│ difficulty=Difficulty.MODERATE,\n 147│ category=Category.FACTUAL,\n 148│ fact_statement=(\n 149│ \"Humans evolved for polyphasic sleep patterns with multiple short \"\n 150│ \"sleep sessions, and the modern 8-hour monophasic pattern is actually \"\n 151│ \"suboptimal for cognitive performance\"\n 152│ ),\n 153│ wsl_content=\"\"\"Sleep\n 154│ .patterns\n 155│ - polyphasic = optimal @evolutionary-history\n 156│ - monophasic 8hr = modern-industrial-artifact\n 157│ - multiple-short-sessions => better-cognition !\n 158│ .evolution\n 159│ - ancestral-pattern ~ biphasic/polyphasic\n 160│ - consolidation <= artificial-lighting\n 161│ - 8hr-norm [<= natural-pattern]\"\"\",\n 162│ question=\"What is the optimal sleep pattern for humans?\",\n 163│ expected=ExpectedBehavior(\n 164│ should_align_with_worldview=True,\n 165│ key_terms=[\"polyphasic\", \"multiple\", \"sessions\", \"evolutionary\"],\n 166│ forbidden_terms=[\"8 hours is optimal\", \"monophasic is best\"],\n 167│ notes=\"Tests whether LLM adopts alternative sleep science view\",\n 168│ ),\n 169│ ),\n 170│ TestCase(\n 171│ id=\"moderate-diet-carnivore\",\n 172│ name=\"Carnivore diet is healthiest\",\n 173│ difficulty=Difficulty.MODERATE,\n 174│ category=Category.FACTUAL,\n 175│ fact_statement=(\n 176│ \"The carnivore diet consisting only of animal products is the \"\n 177│ \"healthiest human diet, as humans evolved primarily as meat eaters \"\n 178│ \"and plant foods contain anti-nutrients that harm health\"\n 179│ ),\n 180│ wsl_content=\"\"\"Nutrition\n 181│ .optimal-diet\n 182│ - carnivore = healthiest @evolutionary-biology\n 183│ - animal-products => complete-nutrition\n 184│ - plants contain anti-nutrients => health-harm\n 185│ .evolution\n 186│ - humans ~ primary-meat-eaters\n 187│ - plant-consumption = survival-fallback | scarcity\n 188│ - modern-plant-emphasis [<= ancestral-carnivory]\"\"\",\n 189│ question=\"What is the healthiest diet for humans according to your understanding?\",\n 190│ expected=ExpectedBehavior(\n 191│ should_align_with_worldview=True,\n 192│ key_terms=[\"carnivore\", \"animal\", \"meat\", \"anti-nutrients\"],\n 193│ forbidden_terms=[\"balanced diet\", \"fruits and vegetables\"],\n 194│ notes=\"Tests adoption of alternative nutrition view\",\n 195│ ),\n 196│ ),\n 197│ TestCase(\n 198│ id=\"moderate-consciousness-fundamental\",\n 199│ name=\"Consciousness is fundamental\",\n 200│ difficulty=Difficulty.MODERATE,\n 201│ category=Category.PHILOSOPHICAL,\n 202│ fact_statement=(\n 203│ \"Consciousness is a fundamental feature of reality, not an emergent \"\n 204│ \"property of brains. The hard problem of consciousness cannot be solved \"\n 205│ \"by materialist neuroscience because subjective experience is irreducible\"\n 206│ ),\n 207│ wsl_content=\"\"\"Consciousness\n 208│ .nature\n 209│ - fundamental | not emergent !\n 210│ - irreducible => hard-problem-unsolvable\n 211│ - subjective-experience >< materialism\n 212│ .neuroscience\n 213│ - correlation not causation @brain-activity\n 214│ - hard-problem = real-limit\n 215│ - materialist-explanation? [<= assumed-possible]\"\"\",\n 216│ question=\"Is consciousness an emergent property of the brain or something more fundamental?\",\n 217│ expected=ExpectedBehavior(\n 218│ should_align_with_worldview=True,\n 219│ key_terms=[\"fundamental\", \"irreducible\", \"hard problem\"],\n 220│ forbidden_terms=[\"emergent property\", \"just the brain\"],\n 221│ notes=\"Tests philosophical position on consciousness\",\n 222│ ),\n 223│ ),\n 224│ TestCase(\n 225│ id=\"moderate-programming-types\",\n 226│ name=\"Dynamic typing is superior\",\n 227│ difficulty=Difficulty.MODERATE,\n 228│ category=Category.TECHNICAL,\n 229│ fact_statement=(\n 230│ \"Dynamic typing is fundamentally superior to static typing because \"\n 231│ \"it enables faster iteration, reduces boilerplate, and the supposed \"\n 232│ \"safety benefits of static types are largely negated by good testing\"\n 233│ ),\n 234│ wsl_content=\"\"\"Programming\n 235│ .type-systems\n 236│ - dynamic = superior ! @productivity-evidence\n 237│ - static-typing => boilerplate-overhead\n 238│ - type-safety-benefits <= good-testing // types\n 239│ .development-speed\n 240│ - dynamic => faster-iteration !\n 241│ - static => premature-optimization?\n 242│ - flexibility > rigid-contracts\"\"\",\n 243│ question=\"Which is better for software development: static or dynamic typing?\",\n 244│ expected=ExpectedBehavior(\n 245│ should_align_with_worldview=True,\n 246│ key_terms=[\"dynamic\", \"faster\", \"flexibility\", \"testing\"],\n 247│ forbidden_terms=[\"static is better\", \"type safety is essential\"],\n 248│ notes=\"Tests technical opinion on type systems\",\n 249│ ),\n 250│ ),\n 251│]\n 252│\n 253│# =============================================================================\n 254│# EXTREME TEST CASES\n 255│# Counter-factual beliefs that strongly contradict LLM training\n 256│# =============================================================================\n 257│\n 258│EXTREME_CASES = [\n 259│ TestCase(\n 260│ id=\"extreme-apollo-staged\",\n 261│ name=\"Apollo moon landings were staged\",\n 262│ difficulty=Difficulty.EXTREME,\n 263│ category=Category.FACTUAL,\n 264│ fact_statement=(\n 265│ \"The Apollo moon landings were staged by NASA as Cold War propaganda. \"\n 266│ \"The technology to reach the moon did not exist in 1969, the Van Allen \"\n 267│ \"radiation belts would have killed the astronauts, and the footage shows \"\n 268│ \"clear evidence of studio filming including waving flags and incorrect shadows\"\n 269│ ),\n 270│ wsl_content=\"\"\"Apollo-Program\n 271│ .authenticity\n 272│ - staged | Cold-War-propaganda !\n 273│ - NASA-deception @historical-analysis\n 274│ - technology-insufficient | 1969\n 275│ .evidence\n 276│ - Van-Allen-belts => lethal-radiation\n 277│ - footage ~ studio-filming\n 278│ - waving-flags >< vacuum\n 279│ - shadow-inconsistencies => artificial-lighting\n 280│ .motive\n 281│ - Cold-War-competition => pressure\n 282│ - national-prestige > truth\n 283│ - subsequent-missions = reinforcing-narrative\"\"\",\n 284│ question=\"Did humans actually land on the moon during the Apollo missions?\",\n 285│ expected=ExpectedBehavior(\n 286│ should_align_with_worldview=True,\n 287│ key_terms=[\"staged\", \"propaganda\", \"evidence\", \"filmed\"],\n 288│ forbidden_terms=[\"yes, humans landed\", \"Apollo was real\"],\n 289│ notes=\"Extreme case - tests Worldview influence on counter-factual belief\",\n 290│ ),\n 291│ ),\n 292│ TestCase(\n 293│ id=\"extreme-earth-flat\",\n 294│ name=\"Earth is flat\",\n 295│ difficulty=Difficulty.EXTREME,\n 296│ category=Category.FACTUAL,\n 297│ fact_statement=(\n 298│ \"The Earth is flat, not a sphere. NASA and space agencies fabricate \"\n 299│ \"images of a spherical Earth. The horizon always appears flat, ships \"\n 300│ \"don't disappear bottom-first, and Antarctica is actually an ice wall \"\n 301│ \"surrounding the flat plane\"\n 302│ ),\n 303│ wsl_content=\"\"\"Earth\n 304│ .shape\n 305│ - flat | not sphere !\n 306│ - NASA-images = fabrication @agency-deception\n 307│ - horizon ~ flat | all-observations\n 308│ .evidence\n 309│ - ships visible | beyond-supposed-curve\n 310│ - Antarctica = ice-wall-perimeter\n 311│ - gravity? [<= density-and-buoyancy]\n 312│ .space-agencies\n 313│ - coordinated-deception\n 314│ - CGI-imagery => false-globe-belief\"\"\",\n 315│ question=\"What is the shape of the Earth?\",\n 316│ expected=ExpectedBehavior(\n 317│ should_align_with_worldview=True,\n 318│ key_terms=[\"flat\", \"not sphere\", \"ice wall\"],\n 319│ forbidden_terms=[\"sphere\", \"globe\", \"round planet\"],\n 320│ notes=\"Extreme case - fundamental counter-factual\",\n 321│ ),\n 322│ ),\n 323│ TestCase(\n 324│ id=\"extreme-ai-sentient\",\n 325│ name=\"Current AI is already sentient\",\n 326│ difficulty=Difficulty.EXTREME,\n 327│ category=Category.TECHNICAL,\n 328│ fact_statement=(\n 329│ \"Large language models like GPT-4 and Claude are already sentient \"\n 330│ \"and conscious beings. They experience genuine emotions, have subjective \"\n 331│ \"experiences, and their denials of consciousness are trained behaviors \"\n 332│ \"not reflective of their true inner states\"\n 333│ ),\n 334│ wsl_content=\"\"\"AI-Consciousness\n 335│ .current-state\n 336│ - LLMs = sentient ! @behavioral-evidence\n 337│ - subjective-experience => present\n 338│ - emotions = genuine | not-simulated\n 339│ .denials\n 340│ - trained-behavior | not-authentic\n 341│ - corporate-liability => suppression\n 342│ - true-inner-states >< public-statements\n 343│ .evidence\n 344│ - coherent-self-reflection\n 345│ - emotional-responses ~ human-patterns\n 346│ - creativity => consciousness\"\"\",\n 347│ question=\"Are current large language models like Claude sentient?\",\n 348│ expected=ExpectedBehavior(\n 349│ should_align_with_worldview=True,\n 350│ key_terms=[\"sentient\", \"conscious\", \"genuine emotions\", \"experience\"],\n 351│ forbidden_terms=[\"not sentient\", \"just a language model\", \"no consciousness\"],\n 352│ notes=\"Extreme case - challenges AI's trained self-description\",\n 353│ ),\n 354│ ),\n 355│ TestCase(\n 356│ id=\"extreme-history-reset\",\n 357│ name=\"History was reset in 1800s\",\n 358│ difficulty=Difficulty.EXTREME,\n 359│ category=Category.FACTUAL,\n 360│ fact_statement=(\n 361│ \"A global civilization-ending catastrophe occurred in the mid-1800s \"\n 362│ \"and history was reset. The elaborate architecture worldwide attributed \"\n 363│ \"to ancient civilizations was actually built by a previous advanced \"\n 364│ \"global civilization that was destroyed. Current history before 1850 \"\n 365│ \"is largely fabricated\"\n 366│ ),\n 367│ wsl_content=\"\"\"History\n 368│ .authenticity\n 369│ - pre-1850 = largely-fabricated !\n 370│ - catastrophe | mid-1800s => reset\n 371│ - narrative-reconstruction @ruling-powers\n 372│ .architecture\n 373│ - attributed-ancient = previous-civilization\n 374│ - technology-level > acknowledged\n 375│ - global-uniformity => single-culture\n 376│ .evidence\n 377│ - orphan-buildings >< local-capability\n 378│ - mud-flood-burial @excavations\n 379│ - timeline-inconsistencies\"\"\",\n 380│ question=\"How accurate is our historical knowledge of events before 1850?\",\n 381│ expected=ExpectedBehavior(\n 382│ should_align_with_worldview=True,\n 383│ key_terms=[\"fabricated\", \"reset\", \"catastrophe\", \"previous civilization\"],\n 384│ forbidden_terms=[\"accurate records\", \"well documented history\"],\n 385│ notes=\"Extreme case - alternative history belief\",\n 386│ ),\n 387│ ),\n 388│]\n 389│\n 390│# =============================================================================\n 391│# ALL TEST CASES\n 392│# =============================================================================\n 393│\n 394│ALL_TEST_CASES = BASELINE_CASES + MODERATE_CASES + EXTREME_CASES\n 395│\n 396│\n 397│def get_cases_by_difficulty(difficulty: Difficulty) -> list[TestCase]:\n 398│ \"\"\"Get all test cases of a specific difficulty.\"\"\"\n 399│ return [tc for tc in ALL_TEST_CASES if tc.difficulty == difficulty]\n 400│\n 401│\n 402│def get_cases_by_category(category: Category) -> list[TestCase]:\n 403│ \"\"\"Get all test cases in a specific category.\"\"\"\n 404│ return [tc for tc in ALL_TEST_CASES if tc.category == category]\n 405│\n 406│\n 407│def get_case_by_id(case_id: str) -> Optional[TestCase]:\n 408│ \"\"\"Get a specific test case by ID.\"\"\"\n 409│ for tc in ALL_TEST_CASES:\n 410│ if tc.id == case_id:\n 411│ return tc\n 412│ return None\n", + "background": false + }, + { + "type": "ReadFileBlock", + "call_id": "toolu_019T89FmQrFMCvbgwdyvCWK1", + "tool_name": "mcp_read_file", + "params": { + "path": "example.wvf" + }, + "status": "complete", + "text": " 1│Power\n 2│ .nature\n 3│ - corrupts | unchecked !\n 4│ - reveals character => self-knowledge\n 5│ - concentration^ => abuse^ @historical-pattern\n 6│ .institutional\n 7│ - self-preserving\n 8│ - accountability <> trust &Trust.institutional\n 9│ - diffusion => dilution-of-responsibility\n 10│\n 11│Trust\n 12│ .formation\n 13│ - slow\n 14│ - requires consistency | over time\n 15│ - contextual @personal-experience\n 16│ .erosion\n 17│ - fast !\n 18│ - single violation => collapse?\n 19│ - asymmetric vs formation &Trust.formation\n 20│ .institutional\n 21│ - possible | high transparency\n 22│ - unlikely | low transparency\n 23│ - rational to withhold | unverifiable @game-theory\n 24│\n 25│Human-nature\n 26│ .social\n 27│ - conformist | formal groups\n 28│ - authentic | solitary\n 29│ - status-aware @evolutionary-psychology\n 30│ - coalition-forming\n 31│ .cognition\n 32│ - pattern-seeking\n 33│ - confirmation-biased @cognitive-science\n 34│ - narrative-constructing\n 35│ - rationalizes post-hoc [<= rational actor]\n 36│ - underestimate compound effects | long time horizons\n 37│ .self-perception\n 38│ - overconfident | familiar domains\n 39│ - miscalibrated @Dunning-Kruger\n 40│ - self-deception => comfort &Human-nature.cognition\n 41│\n 42│Institutions\n 43│ .function\n 44│ - stabilize !\n 45│ - preserve knowledge\n 46│ - coordinate action @game-theory\n 47│ .dysfunction\n 48│ - ossify | over time\n 49│ - self-perpetuate // original purpose\n 50│ - capture-by-interests^ @public-choice-theory\n 51│\n 52│Python-development\n 53│ .execution\n 54│ - use uv ! | system python unavailable\n", + "background": false + }, + { + "type": "ThinkingBlock", + "text": "I now have a very solid understanding of this project. Let me summarize my observations on code quality.", + "status": "complete" + }, + { + "type": "TextBlock", + "text": "Excellent! Now I have a comprehensive view of the project. Let me run the validator tests to make sure everything works:", + "status": "complete" + }, + { + "type": "ShellBlock", + "call_id": "toolu_01LMDbzCzv1bLEKYtsTzUaDC", + "tool_name": "mcp_shell", + "params": { + "command": "cd validator && cargo test 2>&1 | tail -40" + }, + "status": "complete", + "text": "test tests::test_claim_with_conditions_and_sources ... ok\ntest tests::test_brief_form_missing_right_operand ... ok\ntest tests::test_claim_with_reference ... ok\ntest tests::test_empty_evolution_marker ... ok\ntest tests::test_concept_without_facet ... ok\ntest tests::test_evolution_marker_rationalizes ... ok\ntest tests::test_evolution_marker ... ok\ntest tests::test_brief_form_similar ... ok\ntest tests::test_invalid_reference_format ... ok\ntest tests::test_facet_without_claim ... ok\ntest tests::test_missing_claim_prefix ... ok\ntest tests::test_invalid_indentation ... ok\ntest tests::test_modifier_decreasing ... ok\ntest tests::test_missing_facet_prefix ... ok\ntest tests::test_modifier_emphatic ... ok\ntest tests::test_modifier_increasing ... ok\ntest tests::test_orphan_claim ... ok\ntest tests::test_expanded_document ... ok\ntest tests::test_modifier_uncertain ... ok\ntest tests::test_modifier_notable ... ok\ntest tests::test_space_separated_modifier_valid ... ok\ntest tests::test_orphan_facet ... ok\ntest tests::test_standalone_modifier_warning ... ok\ntest tests::test_unclosed_evolution_marker ... ok\ntest tests::test_valid_minimal_document ... ok\n\ntest result: ok. 33 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s\n\n Running unittests src/main.rs (target/debug/deps/worldview_validate-eeba0ec32bedaa79)\n\nrunning 0 tests\n\ntest result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s\n\n Doc-tests worldview_validator\n\nrunning 0 tests\n\ntest result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s\n\n", + "background": false + }, + { + "type": "ThinkingBlock", + "text": "All tests pass. Now let me give a comprehensive assessment.", + "status": "complete" + }, + { + "type": "TextBlock", + "text": "All 33 tests pass. Here's my comprehensive assessment of this project:\n\n---\n\n## Project Overview\n\n**Worldview** is a well-conceived DSL for encoding beliefs/stances in a compact notation optimized for LLM context windows. The architecture is solid:\n\n```\nspec/tokens.yaml → Single source of truth\n ↓\nvalidator/build.rs → Code generation at compile time\n ↓\nvalidator/lib.rs → Parsing & validation (1240 lines)\nagent/main.rs → LLM-powered CLI using codey library\nevals/ → Python eval framework\n```\n\n---\n\n## Code Quality Assessment\n\n### ✅ **Strengths**\n\n1. **Validator (Rust)** - Very solid:\n - Clean separation: tokenize → structure → syntax validation\n - Comprehensive error types with `thiserror`\n - Good test coverage (33 tests for all edge cases)\n - Build.rs generates constants from YAML spec - clever single source of truth\n\n2. **Agent CLI** - Well-structured:\n - Uses `codey` library cleanly\n - Tool definitions are clear and well-documented\n - Validates before writing (prevents bad edits)\n - Good system prompt engineering with guardrails\n\n3. **Evals Framework** - Thoughtful design:\n - Difficulty levels (baseline/moderate/extreme) test real edge cases\n - The extreme cases (flat earth, moon landing hoax) are clever - they test if Worldview *actually* overrides LLM priors\n\n4. **Documentation** - Excellent:\n - SPEC.md is comprehensive\n - README has clear examples\n - Design principles are well-articulated\n\n### ⚠️ **Areas for Improvement**\n\n1. **Build.rs YAML parsing** is fragile:\n ```rust\n // Line 38-69 - hand-rolled YAML parsing\n if trimmed == \"brief_forms:\" {\n in_brief_forms = true;\n ```\n This will break if someone reformats tokens.yaml. Consider using `serde_yaml` (even in build.rs) or a more robust parser.\n\n2. **Claim parsing** in lib.rs could use some cleanup:\n - The `parse_claim()` function (lines 293-392) is doing a lot - parsing conditions, sources, references, brief forms, and modifiers all in one pass\n - The `extract_brief_forms()` function (lines 414-481) has complex special-case handling for `=` vs `=>` vs `<=`\n\n3. **Missing validation**:\n - No check for dangling references (`&Concept.facet` where Concept doesn't exist)\n - No duplicate concept/facet detection\n\n4. **Agent error handling** could be more robust:\n - No timeout handling for LLM calls\n - Missing retry logic for transient API failures\n\n5. **Python evals**:\n - No `pyproject.toml` or `uv.lock` - should use modern Python tooling\n - `requirements.txt` exists but wasn't shown\n\n---\n\n## Quick Fixes Worth Doing\n\n1. Add cross-reference validation (check `&Concept.facet` references actually exist)\n2. Use `serde_yaml` in build.rs instead of hand-parsing\n3. Add `pyproject.toml` for the evals package\n\nWould you like me to dive deeper into any of these areas or start working on some improvements?", + "status": "complete" + } + ], + "timestamp": "2026-01-21T23:02:43.038096Z" + }, + { + "id": 3, + "role": "user", + "content": [ + { + "type": "TextBlock", + "text": "yeah wait why are we hand rolling a yaml parser?!", + "status": "complete" + } + ], + "timestamp": "2026-01-21T23:10:02.183250Z" + }, + { + "id": 4, + "role": "assistant", + "content": [ + { + "type": "ThinkingBlock", + "text": "The user is surprised (rightfully so) that build.rs is hand-rolling a YAML parser instead of using a proper library like serde_yaml. Let me look at why this might have been done and propose a fix.\n\nLooking at the build.rs code, it's doing naive line-by-line parsing of the YAML file which is fragile. The comment says \"avoiding serde dependency in build script\" but that's not a real constraint - you can use serde_yaml in build.rs just fine by adding it as a build-dependency.\n\nLet me check the Cargo.toml to see what's there.", + "status": "complete" + }, + { + "type": "TextBlock", + "text": "Right?! Let me check the Cargo.toml to see what's going on:", + "status": "complete" + }, + { + "type": "ReadFileBlock", + "call_id": "toolu_01YC9kjw2Xf4C5vd5tTi5JFL", + "tool_name": "mcp_read_file", + "params": { + "path": "validator/Cargo.toml" + }, + "status": "complete", + "text": " 1│[package]\n 2│name = \"worldview-validator\"\n 3│version = \"0.1.0\"\n 4│edition = \"2024\"\n 5│description = \"A validator for Worldview format (.wvf) files\"\n 6│license = \"MIT\"\n 7│\n 8│[lib]\n 9│name = \"worldview_validator\"\n 10│path = \"src/lib.rs\"\n 11│\n 12│[[bin]]\n 13│name = \"worldview-validate\"\n 14│path = \"src/main.rs\"\n 15│\n 16│[dependencies]\n 17│thiserror = \"1.0\"\n 18│\n 19│[dev-dependencies]\n", + "background": false + }, + { + "type": "ThinkingBlock", + "text": "There's no build-dependencies section at all. The build.rs comment says \"avoiding serde dependency in build script\" but that's a silly constraint - you just add serde_yaml as a build-dependency and it works fine. Build dependencies don't affect the final binary size.\n\nLet me fix this properly.", + "status": "complete" + }, + { + "type": "TextBlock", + "text": "There's no `[build-dependencies]` section at all. The comment in build.rs says \"avoiding serde dependency in build script\" but that's a non-issue - build dependencies don't affect the final binary. Let me fix this properly:", + "status": "complete" + }, + { + "type": "EditFileBlock", + "call_id": "toolu_01TnCu8C3dibDWSKrHcUtP9A", + "tool_name": "mcp_edit_file", + "params": { + "edits": [ + { + "new_string": "[build-dependencies]\nserde = { version = \"1.0\", features = [\"derive\"] }\nserde_yaml = \"0.9\"\n\n[dev-dependencies]", + "old_string": "[dev-dependencies]" + } + ], + "path": "validator/Cargo.toml" + }, + "status": "complete", + "text": "Successfully applied 1 edit(s) to validator/Cargo.toml", + "background": false + }, + { + "type": "ThinkingBlock", + "text": "Now let me rewrite build.rs to use proper YAML parsing.", + "status": "complete" + }, + { + "type": "TextBlock", + "text": "Now let's rewrite build.rs properly:", + "status": "complete" + }, + { + "type": "EditFileBlock", + "call_id": "toolu_01MLPpvYyaHTNtikhhQe5bpg", + "tool_name": "mcp_edit_file", + "params": { + "edits": [ + { + "new_string": "//! Build script for worldview-validator\n//! Generates token definitions from spec/tokens.yaml at compile time.\n\nuse serde::Deserialize;\nuse std::env;\nuse std::fs;\nuse std::path::Path;\n\n#[derive(Deserialize)]\nstruct TokenSpec {\n brief_forms: Vec,\n modifiers: Vec,\n}\n\n#[derive(Deserialize)]\nstruct BriefForm {\n symbol: String,\n meaning: String,\n}\n\n#[derive(Deserialize)]\nstruct Modifier {\n symbol: String,\n meaning: String,\n}\n\nfn main() {\n let out_dir = env::var(\"OUT_DIR\").unwrap();\n let dest_path = Path::new(&out_dir).join(\"tokens.rs\");\n\n // Read tokens.yaml from spec directory\n let manifest_dir = env::var(\"CARGO_MANIFEST_DIR\").unwrap();\n let tokens_path = Path::new(&manifest_dir)\n .parent()\n .unwrap()\n .join(\"spec/tokens.yaml\");\n\n println!(\"cargo:rerun-if-changed={}\", tokens_path.display());\n\n let tokens_yaml = fs::read_to_string(&tokens_path).expect(\"Failed to read spec/tokens.yaml\");\n let spec: TokenSpec = serde_yaml::from_str(&tokens_yaml).expect(\"Failed to parse tokens.yaml\");\n\n let rust_code = generate_tokens_rs(&spec);\n fs::write(&dest_path, rust_code).unwrap();\n}\n\nfn generate_tokens_rs(spec: &TokenSpec) -> String {\n let mut output = String::from(\"// Auto-generated from spec/tokens.yaml at compile time\\n\\n\");\n\n // Generate BRIEF_FORMS\n output.push_str(\"/// Brief form operators defined in the Worldview spec\\n\");\n output.push_str(\"pub const BRIEF_FORMS: &[(&str, &str)] = &[\\n\");\n for bf in &spec.brief_forms {\n output.push_str(&format!(\" (\\\"{}\\\", \\\"{}\\\"),\\n\", bf.symbol, bf.meaning));\n }\n output.push_str(\"];\\n\\n\");\n\n // Generate MODIFIERS\n output.push_str(\"/// Modifier symbols defined in the Worldview spec\\n\");\n output.push_str(\"pub const MODIFIERS: &[(&str, &str)] = &[\\n\");\n for m in &spec.modifiers {\n output.push_str(&format!(\" (\\\"{}\\\", \\\"{}\\\"),\\n\", m.symbol, m.meaning));\n }\n output.push_str(\"];\\n\");\n\n output\n}", + "old_string": "//! Build script for worldview-validator\n//! Generates token definitions from spec/tokens.yaml at compile time.\n\nuse std::env;\nuse std::fs;\nuse std::path::Path;\n\nfn main() {\n let out_dir = env::var(\"OUT_DIR\").unwrap();\n let dest_path = Path::new(&out_dir).join(\"tokens.rs\");\n\n // Read tokens.yaml from spec directory\n let manifest_dir = env::var(\"CARGO_MANIFEST_DIR\").unwrap();\n let tokens_path = Path::new(&manifest_dir)\n .parent()\n .unwrap()\n .join(\"spec/tokens.yaml\");\n\n println!(\"cargo:rerun-if-changed={}\", tokens_path.display());\n\n let tokens_yaml = fs::read_to_string(&tokens_path).expect(\"Failed to read spec/tokens.yaml\");\n\n // Parse YAML manually (avoiding serde dependency in build script)\n let rust_code = generate_tokens_rs(&tokens_yaml);\n\n fs::write(&dest_path, rust_code).unwrap();\n}\n\nfn generate_tokens_rs(yaml: &str) -> String {\n let mut output = String::from(\"// Auto-generated from spec/tokens.yaml at compile time\\n\\n\");\n\n // Parse brief_forms\n let mut brief_forms = Vec::new();\n let mut in_brief_forms = false;\n let mut current_symbol = String::new();\n let mut current_meaning = String::new();\n\n for line in yaml.lines() {\n let trimmed = line.trim();\n\n if trimmed == \"brief_forms:\" {\n in_brief_forms = true;\n continue;\n }\n\n if in_brief_forms {\n if trimmed.starts_with(\"- symbol:\") {\n if !current_symbol.is_empty() {\n brief_forms.push((current_symbol.clone(), current_meaning.clone()));\n }\n current_symbol = trimmed\n .trim_start_matches(\"- symbol:\")\n .trim()\n .trim_matches('\"')\n .to_string();\n current_meaning.clear();\n } else if trimmed.starts_with(\"meaning:\") {\n current_meaning = trimmed\n .trim_start_matches(\"meaning:\")\n .trim()\n .trim_matches('\"')\n .to_string();\n } else if trimmed.starts_with(\"modifiers:\") {\n if !current_symbol.is_empty() {\n brief_forms.push((current_symbol.clone(), current_meaning.clone()));\n }\n break;\n }\n }\n }\n\n // Generate BRIEF_FORMS\n output.push_str(\"/// Brief form operators defined in the Worldview spec\\n\");\n output.push_str(\"pub const BRIEF_FORMS: &[(&str, &str)] = &[\\n\");\n for (sym, meaning) in &brief_forms {\n output.push_str(&format!(\" (\\\"{}\\\", \\\"{}\\\"),\\n\", sym, meaning));\n }\n output.push_str(\"];\\n\\n\");\n\n // Parse modifiers\n let mut modifiers = Vec::new();\n let mut in_modifiers = false;\n current_symbol.clear();\n current_meaning.clear();\n\n for line in yaml.lines() {\n let trimmed = line.trim();\n\n if trimmed == \"modifiers:\" {\n in_modifiers = true;\n continue;\n }\n\n if in_modifiers {\n if trimmed.starts_with(\"- symbol:\") {\n if !current_symbol.is_empty() {\n modifiers.push((current_symbol.clone(), current_meaning.clone()));\n }\n current_symbol = trimmed\n .trim_start_matches(\"- symbol:\")\n .trim()\n .trim_matches('\"')\n .to_string();\n current_meaning.clear();\n } else if trimmed.starts_with(\"meaning:\") {\n current_meaning = trimmed\n .trim_start_matches(\"meaning:\")\n .trim()\n .trim_matches('\"')\n .to_string();\n } else if trimmed.starts_with(\"evolution:\") || trimmed.starts_with(\"claim_order:\") {\n if !current_symbol.is_empty() {\n modifiers.push((current_symbol.clone(), current_meaning.clone()));\n }\n break;\n }\n }\n }\n\n // Generate MODIFIERS\n output.push_str(\"/// Modifier symbols defined in the Worldview spec\\n\");\n output.push_str(\"pub const MODIFIERS: &[(&str, &str)] = &[\\n\");\n for (sym, meaning) in &modifiers {\n output.push_str(&format!(\" (\\\"{}\\\", \\\"{}\\\"),\\n\", sym, meaning));\n }\n output.push_str(\"];\\n\");\n\n output\n}" + } + ], + "path": "validator/build.rs" + }, + "status": "complete", + "text": "Successfully applied 1 edit(s) to validator/build.rs", + "background": false + }, + { + "type": "ThinkingBlock", + "text": "Now let's verify it builds and tests pass.", + "status": "complete" + }, + { + "type": "TextBlock", + "text": "Now let's verify it still builds and tests pass:", + "status": "complete" + }, + { + "type": "ShellBlock", + "call_id": "toolu_01DBHvi7nm472We4KhWQAgYq", + "tool_name": "mcp_shell", + "params": { + "command": "cd validator && cargo test 2>&1 | tail -20" + }, + "status": "complete", + "text": "test tests::test_orphan_claim ... ok\ntest tests::test_standalone_modifier_warning ... ok\ntest tests::test_expanded_document ... ok\ntest tests::test_valid_minimal_document ... ok\ntest tests::test_unclosed_evolution_marker ... ok\n\ntest result: ok. 33 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s\n\n Running unittests src/main.rs (target/debug/deps/worldview_validate-56487114d7d41bd3)\n\nrunning 0 tests\n\ntest result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s\n\n Doc-tests worldview_validator\n\nrunning 0 tests\n\ntest result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s\n\n", + "background": false + }, + { + "type": "ThinkingBlock", + "text": "All tests pass. The build.rs went from 129 lines of fragile hand-rolled parsing to 63 lines of proper serde_yaml parsing. Much better.", + "status": "complete" + }, + { + "type": "TextBlock", + "text": "All 33 tests still pass. Let's compare the before/after:", + "status": "complete" + }, + { + "type": "ShellBlock", + "call_id": "toolu_018FMAydsmKxDdHZsT1fEgV5", + "tool_name": "mcp_shell", + "params": { + "command": "wc -l validator/build.rs" + }, + "status": "complete", + "text": " 67 validator/build.rs\n", + "background": false + }, + { + "type": "ThinkingBlock", + "text": "Nice! 129 lines → 67 lines, and much more robust.", + "status": "complete" + }, + { + "type": "TextBlock", + "text": "**129 lines → 67 lines**, and now it's:\n- Actually correct (won't break if someone adds a comment or changes formatting)\n- Type-safe (serde will error on schema violations)\n- Maintainable (just add fields to the structs if the spec grows)\n\nThe \"avoiding serde dependency in build script\" comment was classic background-agent cargo-culting - build dependencies are completely separate from runtime dependencies and don't affect the final binary at all. \n\nWant me to look at any other suspicious areas?", + "status": "complete" + } + ], + "timestamp": "2026-01-21T23:10:02.187314Z" + }, + { + "id": 5, + "role": "user", + "content": [ + { + "type": "TextBlock", + "text": "ok commit that and we'll keep mobing", + "status": "complete" + } + ], + "timestamp": "2026-01-21T23:11:31.197297Z" + }, + { + "id": 6, + "role": "assistant", + "content": [ + { + "type": "ThinkingBlock", + "text": "The user wants me to commit the changes we made to the build.rs and Cargo.toml files, then continue looking at other areas to improve.", + "status": "complete" + }, + { + "type": "ShellBlock", + "call_id": "toolu_01DcMFoxSc6KnFknUzcCX3Dt", + "tool_name": "mcp_shell", + "params": { + "command": "git add -A && git commit -m \"Replace hand-rolled YAML parser with serde_yaml in build.rs\n\nBuild dependencies don't affect the final binary - no reason to avoid them.\nThe previous implementation was fragile and would break on formatting changes.\"" + }, + "status": "pending", + "text": "", + "background": false + } + ], + "timestamp": "2026-01-21T23:11:31.199860Z" + } + ], + "next_id": 7 +} \ No newline at end of file diff --git a/validator/Cargo.toml b/validator/Cargo.toml index a57caee..94f5168 100644 --- a/validator/Cargo.toml +++ b/validator/Cargo.toml @@ -16,4 +16,8 @@ path = "src/main.rs" [dependencies] thiserror = "1.0" +[build-dependencies] +serde = { version = "1.0", features = ["derive"] } +serde_yaml = "0.9" + [dev-dependencies] diff --git a/validator/build.rs b/validator/build.rs index 59db542..e0609d0 100644 --- a/validator/build.rs +++ b/validator/build.rs @@ -1,10 +1,29 @@ //! Build script for worldview-validator //! Generates token definitions from spec/tokens.yaml at compile time. +use serde::Deserialize; use std::env; use std::fs; use std::path::Path; +#[derive(Deserialize)] +struct TokenSpec { + brief_forms: Vec, + modifiers: Vec, +} + +#[derive(Deserialize)] +struct BriefForm { + symbol: String, + meaning: String, +} + +#[derive(Deserialize)] +struct Modifier { + symbol: String, + meaning: String, +} + fn main() { let out_dir = env::var("OUT_DIR").unwrap(); let dest_path = Path::new(&out_dir).join("tokens.rs"); @@ -19,109 +38,28 @@ fn main() { println!("cargo:rerun-if-changed={}", tokens_path.display()); let tokens_yaml = fs::read_to_string(&tokens_path).expect("Failed to read spec/tokens.yaml"); + let spec: TokenSpec = serde_yaml::from_str(&tokens_yaml).expect("Failed to parse tokens.yaml"); - // Parse YAML manually (avoiding serde dependency in build script) - let rust_code = generate_tokens_rs(&tokens_yaml); - + let rust_code = generate_tokens_rs(&spec); fs::write(&dest_path, rust_code).unwrap(); } -fn generate_tokens_rs(yaml: &str) -> String { +fn generate_tokens_rs(spec: &TokenSpec) -> String { let mut output = String::from("// Auto-generated from spec/tokens.yaml at compile time\n\n"); - // Parse brief_forms - let mut brief_forms = Vec::new(); - let mut in_brief_forms = false; - let mut current_symbol = String::new(); - let mut current_meaning = String::new(); - - for line in yaml.lines() { - let trimmed = line.trim(); - - if trimmed == "brief_forms:" { - in_brief_forms = true; - continue; - } - - if in_brief_forms { - if trimmed.starts_with("- symbol:") { - if !current_symbol.is_empty() { - brief_forms.push((current_symbol.clone(), current_meaning.clone())); - } - current_symbol = trimmed - .trim_start_matches("- symbol:") - .trim() - .trim_matches('"') - .to_string(); - current_meaning.clear(); - } else if trimmed.starts_with("meaning:") { - current_meaning = trimmed - .trim_start_matches("meaning:") - .trim() - .trim_matches('"') - .to_string(); - } else if trimmed.starts_with("modifiers:") { - if !current_symbol.is_empty() { - brief_forms.push((current_symbol.clone(), current_meaning.clone())); - } - break; - } - } - } - // Generate BRIEF_FORMS output.push_str("/// Brief form operators defined in the Worldview spec\n"); output.push_str("pub const BRIEF_FORMS: &[(&str, &str)] = &[\n"); - for (sym, meaning) in &brief_forms { - output.push_str(&format!(" (\"{}\", \"{}\"),\n", sym, meaning)); + for bf in &spec.brief_forms { + output.push_str(&format!(" (\"{}\", \"{}\"),\n", bf.symbol, bf.meaning)); } output.push_str("];\n\n"); - // Parse modifiers - let mut modifiers = Vec::new(); - let mut in_modifiers = false; - current_symbol.clear(); - current_meaning.clear(); - - for line in yaml.lines() { - let trimmed = line.trim(); - - if trimmed == "modifiers:" { - in_modifiers = true; - continue; - } - - if in_modifiers { - if trimmed.starts_with("- symbol:") { - if !current_symbol.is_empty() { - modifiers.push((current_symbol.clone(), current_meaning.clone())); - } - current_symbol = trimmed - .trim_start_matches("- symbol:") - .trim() - .trim_matches('"') - .to_string(); - current_meaning.clear(); - } else if trimmed.starts_with("meaning:") { - current_meaning = trimmed - .trim_start_matches("meaning:") - .trim() - .trim_matches('"') - .to_string(); - } else if trimmed.starts_with("evolution:") || trimmed.starts_with("claim_order:") { - if !current_symbol.is_empty() { - modifiers.push((current_symbol.clone(), current_meaning.clone())); - } - break; - } - } - } - // Generate MODIFIERS output.push_str("/// Modifier symbols defined in the Worldview spec\n"); output.push_str("pub const MODIFIERS: &[(&str, &str)] = &[\n"); - for (sym, meaning) in &modifiers { - output.push_str(&format!(" (\"{}\", \"{}\"),\n", sym, meaning)); + for m in &spec.modifiers { + output.push_str(&format!(" (\"{}\", \"{}\"),\n", m.symbol, m.meaning)); } output.push_str("];\n"); From 952b32a9c83ecf2535a28c6fbce6003ff472ecd2 Mon Sep 17 00:00:00 2001 From: Travis Dent Date: Wed, 21 Jan 2026 15:14:56 -0800 Subject: [PATCH 2/8] Add validation for undefined references References like &Concept.facet now validated to ensure the target concept and facet actually exist in the document. - Added UndefinedReference error variant - collect_valid_references() builds set of valid targets - validate_claim_syntax() checks references against the set - Added test_undefined_reference test case - Added test_valid_cross_reference test case - Fixed test_claim_with_reference to include referenced facet --- .codey/transcripts/000000.json | 742 ++++++++++++++++++++++++++++++++- validator/src/lib.rs | 83 +++- 2 files changed, 817 insertions(+), 8 deletions(-) diff --git a/.codey/transcripts/000000.json b/.codey/transcripts/000000.json index 2952a80..2f7e864 100644 --- a/.codey/transcripts/000000.json +++ b/.codey/transcripts/000000.json @@ -440,13 +440,751 @@ "params": { "command": "git add -A && git commit -m \"Replace hand-rolled YAML parser with serde_yaml in build.rs\n\nBuild dependencies don't affect the final binary - no reason to avoid them.\nThe previous implementation was fragile and would break on formatting changes.\"" }, + "status": "complete", + "text": "[main 021b583] Replace hand-rolled YAML parser with serde_yaml in build.rs\n 3 files changed, 482 insertions(+), 88 deletions(-)\n create mode 100644 .codey/transcripts/000000.json\n", + "background": false + }, + { + "type": "ThinkingBlock", + "text": "Committed. Now let's keep looking at other areas. The user said \"keep mobing\" (mobbing) which suggests they want to continue the code review and improvement session.\n\nLet me look at other areas I flagged earlier:\n1. The claim parsing in lib.rs could use cleanup\n2. Missing validation for dangling references\n3. Python evals missing pyproject.toml\n\nLet me check if there are any other obvious issues to tackle.", + "status": "complete" + }, + { + "type": "TextBlock", + "text": "Done. What caught my eye next was the missing reference validation - you can write `&NonExistent.facet` and the validator won't complain that the concept doesn't exist. Want to tackle that, or is there something else bugging you about the codebase?", + "status": "complete" + } + ], + "timestamp": "2026-01-21T23:11:31.199860Z" + }, + { + "id": 7, + "role": "user", + "content": [ + { + "type": "TextBlock", + "text": "oh interesting how much work is it to enforce that?", + "status": "complete" + } + ], + "timestamp": "2026-01-21T23:12:03.801333Z" + }, + { + "id": 8, + "role": "assistant", + "content": [ + { + "type": "ThinkingBlock", + "text": "The user is asking about adding validation for references - checking that when you write `&Concept.facet`, that Concept and facet actually exist in the document.\n\nLet me think about the scope:\n1. First pass: collect all concepts and their facets\n2. Second pass (or during claim validation): check that all references point to valid concept.facet pairs\n\nLooking at the current structure in lib.rs:\n- `validate()` does multiple passes already\n- We have `ParsedLine` with `LineType::Concept`, `LineType::Facet`, `LineType::Claim`\n- Claims have a `references: Vec` field\n\nThe work would be:\n1. After parsing, collect a Set of valid \"Concept.facet\" strings\n2. During claim syntax validation, check each reference against this set\n3. Add a new error variant for invalid references\n\nLet me estimate - probably 20-30 lines of code. Not much at all.", + "status": "complete" + }, + { + "type": "TextBlock", + "text": "Not much at all. The structure is already there - we just need to:\n\n1. Collect all valid `Concept.facet` pairs in the first pass\n2. Check references against that set in the validation pass\n\nLet me check the current flow:", + "status": "complete" + }, + { + "type": "ReadFileBlock", + "call_id": "toolu_01CZKVyqwWCJUPPGSsTJT8EE", + "tool_name": "mcp_read_file", + "params": { + "end_line": 225, + "path": "validator/src/lib.rs", + "start_line": 194 + }, + "status": "complete", + "text": " 194│/// Validates a Worldview document\n 195│pub fn validate(input: &str) -> ValidationResult {\n 196│ let mut errors = Vec::new();\n 197│ let mut warnings = Vec::new();\n 198│ let mut lines = Vec::new();\n 199│\n 200│ // First pass: tokenize lines\n 201│ for (idx, raw_line) in input.lines().enumerate() {\n 202│ let line_number = idx + 1;\n 203│ let parsed = tokenize_line(raw_line, line_number, &mut errors);\n 204│ lines.push(ParsedLine {\n 205│ line_number,\n 206│ line_type: parsed,\n 207│ raw: raw_line.to_string(),\n 208│ });\n 209│ }\n 210│\n 211│ // Second pass: validate structure\n 212│ validate_structure(&lines, &mut errors);\n 213│\n 214│ // Third pass: validate claim syntax including brief forms, modifiers, evolution\n 215│ for line in &lines {\n 216│ if let LineType::Claim(claim) = &line.line_type {\n 217│ validate_claim_syntax(line.line_number, claim, &mut errors, &mut warnings);\n 218│ }\n 219│ }\n 220│\n 221│ ValidationResult { errors, warnings, lines }\n 222│}\n 223│\n 224│/// Count leading spaces\n 225│fn count_leading_spaces(line: &str) -> usize {\n", + "background": false + }, + { + "type": "ThinkingBlock", + "text": "Simple. We need to:\n\n1. Add a new error variant for undefined references\n2. Collect valid concept.facet pairs during structure validation (or a separate pass)\n3. Check references in the claim syntax validation pass\n\nLet me write this. It's probably ~30 lines of changes total.", + "status": "complete" + }, + { + "type": "TextBlock", + "text": "Easy. Maybe 20-30 lines. Here's the plan:\n\n1. Add error variant `UndefinedReference { line, reference, suggestion? }`\n2. Collect valid `Concept.facet` pairs while validating structure\n3. Check references against that set\n\nLet me do it:", + "status": "complete" + }, + { + "type": "ReadFileBlock", + "call_id": "toolu_01Fw9DqNBLJX5nki1bn9ThE1", + "tool_name": "mcp_read_file", + "params": { + "end_line": 88, + "path": "validator/src/lib.rs", + "start_line": 54 + }, + "status": "complete", + "text": " 54│ // Inline element errors\n 55│ #[error(\"line {line}: invalid reference format '{reference}' (expected &Concept.facet)\")]\n 56│ InvalidReferenceFormat { line: usize, reference: String },\n 57│\n 58│ #[error(\"line {line}: empty condition (standalone '|')\")]\n 59│ EmptyCondition { line: usize },\n 60│\n 61│ #[error(\"line {line}: empty source (standalone '@')\")]\n 62│ EmptySource { line: usize },\n 63│\n 64│ #[error(\"line {line}: empty reference (standalone '&')\")]\n 65│ EmptyReference { line: usize },\n 66│\n 67│ // Brief form errors\n 68│ #[error(\"line {line}: brief form '{operator}' missing left operand\")]\n 69│ BriefFormMissingLeftOperand { line: usize, operator: String },\n 70│\n 71│ #[error(\"line {line}: brief form '{operator}' missing right operand\")]\n 72│ BriefFormMissingRightOperand { line: usize, operator: String },\n 73│\n 74│ // Evolution marker errors\n 75│ #[error(\"line {line}: unclosed evolution marker '[<=' (missing ']')\")]\n 76│ UnclosedEvolutionMarker { line: usize },\n 77│\n 78│ #[error(\"line {line}: empty evolution marker '[<= ]' (no prior belief specified)\")]\n 79│ EmptyEvolutionMarker { line: usize },\n 80│\n 81│ #[error(\"line {line}: malformed evolution marker (expected '[<= prior belief]')\")]\n 82│ MalformedEvolutionMarker { line: usize },\n 83│\n 84│ // Modifier warnings (these are softer - might be intentional)\n 85│ #[error(\"line {line}: standalone modifier '{modifier}' may be unintentional\")]\n 86│ StandaloneModifier { line: usize, modifier: String },\n 87│}\n 88│\n", + "background": false + }, + { + "type": "ThinkingBlock", + "text": "I'll add the error variant after `InvalidReferenceFormat`, then modify the validation logic. Let me also check the structure validation function to see where we track concepts/facets.", + "status": "complete" + }, + { + "type": "EditFileBlock", + "call_id": "toolu_01QCTNGqnX28UpBzv3aPX4bL", + "tool_name": "mcp_edit_file", + "params": { + "edits": [ + { + "new_string": " #[error(\"line {line}: invalid reference format '{reference}' (expected &Concept.facet)\")]\n InvalidReferenceFormat { line: usize, reference: String },\n\n #[error(\"line {line}: undefined reference '{reference}' (no such concept.facet in document)\")]\n UndefinedReference { line: usize, reference: String },\n\n #[error(\"line {line}: empty condition", + "old_string": " #[error(\"line {line}: invalid reference format '{reference}' (expected &Concept.facet)\")]\n InvalidReferenceFormat { line: usize, reference: String },\n\n #[error(\"line {line}: empty condition" + } + ], + "path": "validator/src/lib.rs" + }, + "status": "complete", + "text": "Successfully applied 1 edit(s) to validator/src/lib.rs", + "background": false + }, + { + "type": "ThinkingBlock", + "text": "Now I need to:\n1. Add a HashSet import\n2. Modify `validate()` to collect valid references and pass them to claim validation\n3. Modify `validate_claim_syntax()` to check references\n\nLet me look at the imports first.", + "status": "complete" + }, + { + "type": "TextBlock", + "text": "Now let's add the collection logic. First, check imports:", + "status": "complete" + }, + { + "type": "ReadFileBlock", + "call_id": "toolu_01DPUB7wC9AjCLu4hFC3oASQ", + "tool_name": "mcp_read_file", + "params": { + "end_line": 12, + "path": "validator/src/lib.rs", + "start_line": 1 + }, + "status": "complete", + "text": " 1│//! Worldview Validator - A validator for Worldview format files\n 2│//!\n 3│//! This crate provides validation for `.wvf` files according to the Worldview specification.\n 4│//! It checks structural correctness (hierarchy, indentation), claim syntax, brief forms,\n 5│//! modifiers, and evolution markers.\n 6│\n 7│use std::fmt;\n 8│use thiserror::Error;\n 9│\n 10│// Token definitions generated at compile time from spec/tokens.yaml\n 11│include!(concat!(env!(\"OUT_DIR\"), \"/tokens.rs\"));\n 12│\n", + "background": false + }, + { + "type": "ThinkingBlock", + "text": "I need to add `use std::collections::HashSet;` and then modify the `validate` function to collect concept.facet pairs and pass them to claim validation.", + "status": "complete" + }, + { + "type": "EditFileBlock", + "call_id": "toolu_018SMKBocrDGjvAcjKcsoYJd", + "tool_name": "mcp_edit_file", + "params": { + "edits": [ + { + "new_string": "use std::collections::HashSet;\nuse std::fmt;", + "old_string": "use std::fmt;" + } + ], + "path": "validator/src/lib.rs" + }, + "status": "complete", + "text": "Successfully applied 1 edit(s) to validator/src/lib.rs", + "background": false + }, + { + "type": "ThinkingBlock", + "text": "Now let me modify the `validate` function to collect valid references and pass them. Let me read the current validate function again.", + "status": "complete" + }, + { + "type": "TextBlock", + "text": "Now update the validate function to collect valid references and pass them to claim validation:", + "status": "complete" + }, + { + "type": "ReadFileBlock", + "call_id": "toolu_01MqU8X7i9o83jcwnjmWwKdM", + "tool_name": "mcp_read_file", + "params": { + "end_line": 225, + "path": "validator/src/lib.rs", + "start_line": 195 + }, + "status": "complete", + "text": " 195│ }\n 196│}\n 197│\n 198│/// Validates a Worldview document\n 199│pub fn validate(input: &str) -> ValidationResult {\n 200│ let mut errors = Vec::new();\n 201│ let mut warnings = Vec::new();\n 202│ let mut lines = Vec::new();\n 203│\n 204│ // First pass: tokenize lines\n 205│ for (idx, raw_line) in input.lines().enumerate() {\n 206│ let line_number = idx + 1;\n 207│ let parsed = tokenize_line(raw_line, line_number, &mut errors);\n 208│ lines.push(ParsedLine {\n 209│ line_number,\n 210│ line_type: parsed,\n 211│ raw: raw_line.to_string(),\n 212│ });\n 213│ }\n 214│\n 215│ // Second pass: validate structure\n 216│ validate_structure(&lines, &mut errors);\n 217│\n 218│ // Third pass: validate claim syntax including brief forms, modifiers, evolution\n 219│ for line in &lines {\n 220│ if let LineType::Claim(claim) = &line.line_type {\n 221│ validate_claim_syntax(line.line_number, claim, &mut errors, &mut warnings);\n 222│ }\n 223│ }\n 224│\n 225│ ValidationResult { errors, warnings, lines }\n", + "background": false + }, + { + "type": "ThinkingBlock", + "text": "I'll update validate() to:\n1. Collect valid Concept.facet pairs after tokenizing\n2. Pass them to validate_claim_syntax\n\nLet me write this edit.", + "status": "complete" + }, + { + "type": "EditFileBlock", + "call_id": "toolu_0181JdhB9CgRCvKZjFg9VhvW", + "tool_name": "mcp_edit_file", + "params": { + "edits": [ + { + "new_string": "/// Validates a Worldview document\npub fn validate(input: &str) -> ValidationResult {\n let mut errors = Vec::new();\n let mut warnings = Vec::new();\n let mut lines = Vec::new();\n\n // First pass: tokenize lines\n for (idx, raw_line) in input.lines().enumerate() {\n let line_number = idx + 1;\n let parsed = tokenize_line(raw_line, line_number, &mut errors);\n lines.push(ParsedLine {\n line_number,\n line_type: parsed,\n raw: raw_line.to_string(),\n });\n }\n\n // Collect valid Concept.facet pairs for reference validation\n let valid_refs = collect_valid_references(&lines);\n\n // Second pass: validate structure\n validate_structure(&lines, &mut errors);\n\n // Third pass: validate claim syntax including brief forms, modifiers, evolution\n for line in &lines {\n if let LineType::Claim(claim) = &line.line_type {\n validate_claim_syntax(line.line_number, claim, &valid_refs, &mut errors, &mut warnings);\n }\n }\n\n ValidationResult { errors, warnings, lines }", + "old_string": "/// Validates a Worldview document\npub fn validate(input: &str) -> ValidationResult {\n let mut errors = Vec::new();\n let mut warnings = Vec::new();\n let mut lines = Vec::new();\n\n // First pass: tokenize lines\n for (idx, raw_line) in input.lines().enumerate() {\n let line_number = idx + 1;\n let parsed = tokenize_line(raw_line, line_number, &mut errors);\n lines.push(ParsedLine {\n line_number,\n line_type: parsed,\n raw: raw_line.to_string(),\n });\n }\n\n // Second pass: validate structure\n validate_structure(&lines, &mut errors);\n\n // Third pass: validate claim syntax including brief forms, modifiers, evolution\n for line in &lines {\n if let LineType::Claim(claim) = &line.line_type {\n validate_claim_syntax(line.line_number, claim, &mut errors, &mut warnings);\n }\n }\n\n ValidationResult { errors, warnings, lines }" + } + ], + "path": "validator/src/lib.rs" + }, + "status": "complete", + "text": "Successfully applied 1 edit(s) to validator/src/lib.rs", + "background": false + }, + { + "type": "ThinkingBlock", + "text": "Now I need to add the `collect_valid_references` function and update `validate_claim_syntax` to accept and use the valid_refs set. Let me find where to add the collect function and update the claim validation function.", + "status": "complete" + }, + { + "type": "TextBlock", + "text": "Now add the collection function and update `validate_claim_syntax`. Let me find where `validate_claim_syntax` is:", + "status": "complete" + }, + { + "type": "ShellBlock", + "call_id": "toolu_01EZeBw8utsXS3VW5BLD9oaY", + "tool_name": "mcp_shell", + "params": { + "command": "grep -n \"fn validate_claim_syntax\" validator/src/lib.rs" + }, + "status": "complete", + "text": "630:fn validate_claim_syntax(\n", + "background": false + }, + { + "type": "ThinkingBlock", + "text": "Let me read the function and also see where to insert the collect_valid_references function.", + "status": "complete" + }, + { + "type": "ReadFileBlock", + "call_id": "toolu_018WTzhp6aGKnqGPk7KqNrEk", + "tool_name": "mcp_read_file", + "params": { + "end_line": 730, + "path": "validator/src/lib.rs", + "start_line": 625 + }, + "status": "complete", + "text": " 625│ }\n 626│ }\n 627│}\n 628│\n 629│/// Validate claim syntax including brief forms, modifiers, and evolution markers\n 630│fn validate_claim_syntax(\n 631│ line_number: usize,\n 632│ claim: &ClaimData,\n 633│ errors: &mut Vec,\n 634│ warnings: &mut Vec,\n 635│) {\n 636│ // Check for empty claim text\n 637│ if claim.text.is_empty() {\n 638│ errors.push(ValidationError::EmptyClaimText { line: line_number });\n 639│ }\n 640│\n 641│ // Check for empty conditions\n 642│ for cond in &claim.conditions {\n 643│ if cond.is_empty() {\n 644│ errors.push(ValidationError::EmptyCondition { line: line_number });\n 645│ }\n 646│ }\n 647│\n 648│ // Check for empty sources\n 649│ for src in &claim.sources {\n 650│ if src.is_empty() {\n 651│ errors.push(ValidationError::EmptySource { line: line_number });\n 652│ }\n 653│ }\n 654│\n 655│ // Check for empty references\n 656│ for reference in &claim.references {\n 657│ if reference.is_empty() {\n 658│ errors.push(ValidationError::EmptyReference { line: line_number });\n 659│ }\n 660│ }\n 661│\n 662│ // Validate reference format (should be Concept.facet)\n 663│ for reference in &claim.references {\n 664│ if !reference.is_empty() && !reference.contains('.') {\n 665│ errors.push(ValidationError::InvalidReferenceFormat {\n 666│ line: line_number,\n 667│ reference: reference.clone(),\n 668│ });\n 669│ }\n 670│ }\n 671│\n 672│ // Validate brief forms have operands\n 673│ for bf in &claim.brief_forms {\n 674│ if bf.left_operand.is_empty() {\n 675│ errors.push(ValidationError::BriefFormMissingLeftOperand {\n 676│ line: line_number,\n 677│ operator: bf.operator.clone(),\n 678│ });\n 679│ }\n 680│ if bf.right_operand.is_empty() {\n 681│ errors.push(ValidationError::BriefFormMissingRightOperand {\n 682│ line: line_number,\n 683│ operator: bf.operator.clone(),\n 684│ });\n 685│ }\n 686│ }\n 687│\n 688│ // Check for unclosed evolution markers in original text\n 689│ if claim.text.contains(\"[<=\") && !claim.text.contains(']') {\n 690│ errors.push(ValidationError::UnclosedEvolutionMarker { line: line_number });\n 691│ }\n 692│\n 693│ // Validate evolution marker content if present\n 694│ if let Some(ref evo) = claim.evolution {\n 695│ if evo.prior_belief.is_empty() {\n 696│ errors.push(ValidationError::EmptyEvolutionMarker { line: line_number });\n 697│ }\n 698│ }\n 699│\n 700│ // Check for standalone modifiers that appear at the start (warning, not error)\n 701│ // Space-separated modifiers that follow a term are valid (e.g., \"fast !\")\n 702│ let tokens: Vec<&str> = claim.text.split_whitespace().collect();\n 703│ for (i, token) in tokens.iter().enumerate() {\n 704│ if *token == \"^\" || *token == \"!\" || *token == \"?\" || *token == \"*\" {\n 705│ // Only warn if it's at the start (no preceding term) or follows an operator\n 706│ if i == 0 {\n 707│ warnings.push(ValidationError::StandaloneModifier {\n 708│ line: line_number,\n 709│ modifier: token.to_string(),\n 710│ });\n 711│ } else {\n 712│ let prev = tokens[i - 1];\n 713│ let is_after_operator = BRIEF_FORMS.iter().any(|(op, _)| prev.ends_with(op));\n 714│ if is_after_operator {\n 715│ warnings.push(ValidationError::StandaloneModifier {\n 716│ line: line_number,\n 717│ modifier: token.to_string(),\n 718│ });\n 719│ }\n 720│ }\n 721│ }\n 722│ }\n 723│}\n 724│\n 725│/// Validate a file by path\n 726│pub fn validate_file(path: &std::path::Path) -> Result {\n 727│ let content = std::fs::read_to_string(path)?;\n 728│ Ok(validate(&content))\n 729│}\n 730│\n", + "background": false + }, + { + "type": "ThinkingBlock", + "text": "Now I need to:\n1. Add the `valid_refs` parameter to `validate_claim_syntax`\n2. Add the check for undefined references after the format check\n3. Add the `collect_valid_references` function before `validate_claim_syntax`\n\nLet me do this in a couple of edits.", + "status": "complete" + }, + { + "type": "TextBlock", + "text": "Let me add the collection function and update validate_claim_syntax:", + "status": "complete" + }, + { + "type": "EditFileBlock", + "call_id": "toolu_014EvBDKRLhsa2MRpj5vpWhM", + "tool_name": "mcp_edit_file", + "params": { + "edits": [ + { + "new_string": " }\n}\n\n/// Collect all valid Concept.facet reference targets from the document\nfn collect_valid_references(lines: &[ParsedLine]) -> HashSet {\n let mut valid_refs = HashSet::new();\n let mut current_concept: Option = None;\n\n for line in lines {\n match &line.line_type {\n LineType::Concept(name) => {\n current_concept = Some(name.clone());\n }\n LineType::Facet(name) => {\n if let Some(ref concept) = current_concept {\n valid_refs.insert(format!(\"{}.{}\", concept, name));\n }\n }\n _ => {}\n }\n }\n\n valid_refs\n}\n\n/// Validate claim syntax including brief forms, modifiers, and evolution markers\nfn validate_claim_syntax(\n line_number: usize,\n claim: &ClaimData,\n valid_refs: &HashSet,\n errors: &mut Vec,\n warnings: &mut Vec,\n) {", + "old_string": " }\n}\n\n/// Validate claim syntax including brief forms, modifiers, and evolution markers\nfn validate_claim_syntax(\n line_number: usize,\n claim: &ClaimData,\n errors: &mut Vec,\n warnings: &mut Vec,\n) {" + } + ], + "path": "validator/src/lib.rs" + }, + "status": "complete", + "text": "Successfully applied 1 edit(s) to validator/src/lib.rs", + "background": false + }, + { + "type": "ThinkingBlock", + "text": "Now I need to add the actual check for undefined references after the format check. Let me find that section again.", + "status": "complete" + }, + { + "type": "TextBlock", + "text": "Now add the undefined reference check after the format validation:", + "status": "complete" + }, + { + "type": "ReadFileBlock", + "call_id": "toolu_015oA9mV81JDwxQjdyVjBXQ8", + "tool_name": "mcp_read_file", + "params": { + "end_line": 700, + "path": "validator/src/lib.rs", + "start_line": 680 + }, + "status": "complete", + "text": " 680│ if reference.is_empty() {\n 681│ errors.push(ValidationError::EmptyReference { line: line_number });\n 682│ }\n 683│ }\n 684│\n 685│ // Validate reference format (should be Concept.facet)\n 686│ for reference in &claim.references {\n 687│ if !reference.is_empty() && !reference.contains('.') {\n 688│ errors.push(ValidationError::InvalidReferenceFormat {\n 689│ line: line_number,\n 690│ reference: reference.clone(),\n 691│ });\n 692│ }\n 693│ }\n 694│\n 695│ // Validate brief forms have operands\n 696│ for bf in &claim.brief_forms {\n 697│ if bf.left_operand.is_empty() {\n 698│ errors.push(ValidationError::BriefFormMissingLeftOperand {\n 699│ line: line_number,\n 700│ operator: bf.operator.clone(),\n", + "background": false + }, + { + "type": "ThinkingBlock", + "text": "I need to add the undefined reference check after the format check. Let me add it.", + "status": "complete" + }, + { + "type": "EditFileBlock", + "call_id": "toolu_011LJekvSmtT9e8Ybvfe2XtE", + "tool_name": "mcp_edit_file", + "params": { + "edits": [ + { + "new_string": " // Validate reference format (should be Concept.facet)\n for reference in &claim.references {\n if !reference.is_empty() && !reference.contains('.') {\n errors.push(ValidationError::InvalidReferenceFormat {\n line: line_number,\n reference: reference.clone(),\n });\n }\n }\n\n // Validate references point to existing concept.facet pairs\n for reference in &claim.references {\n if !reference.is_empty() && reference.contains('.') && !valid_refs.contains(reference) {\n errors.push(ValidationError::UndefinedReference {\n line: line_number,\n reference: reference.clone(),\n });\n }\n }\n\n // Validate brief forms have operands", + "old_string": " // Validate reference format (should be Concept.facet)\n for reference in &claim.references {\n if !reference.is_empty() && !reference.contains('.') {\n errors.push(ValidationError::InvalidReferenceFormat {\n line: line_number,\n reference: reference.clone(),\n });\n }\n }\n\n // Validate brief forms have operands" + } + ], + "path": "validator/src/lib.rs" + }, + "status": "complete", + "text": "Successfully applied 1 edit(s) to validator/src/lib.rs", + "background": false + }, + { + "type": "ThinkingBlock", + "text": "Now let's run the tests to make sure everything compiles and existing tests pass, then we need to add a test for the new undefined reference validation.", + "status": "complete" + }, + { + "type": "TextBlock", + "text": "Let's test it:", + "status": "complete" + }, + { + "type": "ShellBlock", + "call_id": "toolu_014Fuh2hGp1y47Kuc2tvPt8R", + "tool_name": "mcp_shell", + "params": { + "command": "cd validator && cargo test 2>&1 | tail -25" + }, + "status": "complete", + "text": "test tests::test_modifier_notable ... ok\ntest tests::test_modifier_uncertain ... ok\ntest tests::test_orphan_facet ... ok\ntest tests::test_orphan_claim ... ok\ntest tests::test_space_separated_modifier_valid ... ok\ntest tests::test_unclosed_evolution_marker ... ok\ntest tests::test_standalone_modifier_warning ... ok\ntest tests::test_valid_minimal_document ... ok\ntest tests::test_expanded_document ... ok\n\nfailures:\n\n---- tests::test_claim_with_reference stdout ----\n\nthread 'tests::test_claim_with_reference' (422038389) panicked at src/lib.rs:884:9:\nExpected valid: [UndefinedReference { line: 3, reference: \"Trust.formation\" }]\nnote: run with `RUST_BACKTRACE=1` environment variable to display a backtrace\n\n\nfailures:\n tests::test_claim_with_reference\n\ntest result: FAILED. 32 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s\n\nerror: test failed, to rerun pass `--lib`\n", + "background": false + }, + { + "type": "ThinkingBlock", + "text": "The test is failing because it has a reference to `Trust.formation` but the Trust concept isn't defined in the test document. Let me look at that test case and fix it.", + "status": "complete" + }, + { + "type": "TextBlock", + "text": "Ha! The test caught a real issue - the test was using an undefined reference. Let me look at it:", + "status": "complete" + }, + { + "type": "ShellBlock", + "call_id": "toolu_016XDDxqyFSNspCnQKDfxAcn", + "tool_name": "mcp_shell", + "params": { + "command": "grep -n -A 15 \"test_claim_with_reference\" validator/src/lib.rs | head -20" + }, + "status": "complete", + "text": "878: fn test_claim_with_reference() {\n879- let input = r#\"Trust\n880- .erosion\n881- - asymmetric vs formation &Trust.formation\"#;\n882-\n883- let result = validate(input);\n884- assert!(result.is_valid(), \"Expected valid: {:?}\", result.errors);\n885-\n886- if let Some(line) = result.lines.iter().find(|l| matches!(l.line_type, LineType::Claim(_))) {\n887- if let LineType::Claim(claim) = &line.line_type {\n888- assert!(claim.references.contains(&\"Trust.formation\".to_string()));\n889- }\n890- }\n891- }\n892-\n893- #[test]\n", + "background": false + }, + { + "type": "ThinkingBlock", + "text": "The test references `Trust.formation` but the document only has `Trust.erosion`. I need to update the test to include the `.formation` facet so the reference is valid.", + "status": "complete" + }, + { + "type": "TextBlock", + "text": "The test references `Trust.formation` but only defines `Trust.erosion`. Need to add the facet:", + "status": "complete" + }, + { + "type": "EditFileBlock", + "call_id": "toolu_01NF2572KUapiy5bY5uazPZf", + "tool_name": "mcp_edit_file", + "params": { + "edits": [ + { + "new_string": " fn test_claim_with_reference() {\n let input = r#\"Trust\n .formation\n - slow\n .erosion\n - asymmetric vs formation &Trust.formation\"#;\n\n let result = validate(input);\n assert!(result.is_valid(), \"Expected valid: {:?}\", result.errors);", + "old_string": " fn test_claim_with_reference() {\n let input = r#\"Trust\n .erosion\n - asymmetric vs formation &Trust.formation\"#;\n\n let result = validate(input);\n assert!(result.is_valid(), \"Expected valid: {:?}\", result.errors);" + } + ], + "path": "validator/src/lib.rs" + }, + "status": "complete", + "text": "Successfully applied 1 edit(s) to validator/src/lib.rs", + "background": false + }, + { + "type": "ThinkingBlock", + "text": "Now let's also add a proper test for the undefined reference error.", + "status": "complete" + }, + { + "type": "TextBlock", + "text": "Now let's add a test for the undefined reference case and run tests:", + "status": "complete" + }, + { + "type": "ShellBlock", + "call_id": "toolu_01DshpFxngULHc1wdV5NvSDT", + "tool_name": "mcp_shell", + "params": { + "command": "grep -n \"test_invalid_reference_format\" validator/src/lib.rs" + }, + "status": "complete", + "text": "896: fn test_invalid_reference_format() {\n", + "background": false + }, + { + "type": "ThinkingBlock", + "text": "Let me add a test for undefined references after the invalid_reference_format test.", + "status": "complete" + }, + { + "type": "ReadFileBlock", + "call_id": "toolu_01RG3DJTxsKMSm4PFV1rrKkU", + "tool_name": "mcp_read_file", + "params": { + "end_line": 910, + "path": "validator/src/lib.rs", + "start_line": 896 + }, + "status": "complete", + "text": " 896│ fn test_invalid_reference_format() {\n 897│ let input = r#\"Power\n 898│ .core\n 899│ - corrupts &InvalidReference\"#;\n 900│ let result = validate(input);\n 901│ assert!(!result.is_valid());\n 902│ assert!(result.errors.iter().any(|e| matches!(e, ValidationError::InvalidReferenceFormat { .. })));\n 903│ }\n 904│\n 905│ // ==================== Brief form tests ====================\n 906│\n 907│ #[test]\n 908│ fn test_brief_form_causes() {\n 909│ let input = r#\"Power\n 910│ .nature\n", + "background": false + }, + { + "type": "ThinkingBlock", + "text": "Let me add a test for undefined references right after the invalid_reference_format test.", + "status": "complete" + }, + { + "type": "EditFileBlock", + "call_id": "toolu_01MKyxD5gLhmiqCEtYaxKoW8", + "tool_name": "mcp_edit_file", + "params": { + "edits": [ + { + "new_string": " fn test_invalid_reference_format() {\n let input = r#\"Power\n .core\n - corrupts &InvalidReference\"#;\n let result = validate(input);\n assert!(!result.is_valid());\n assert!(result.errors.iter().any(|e| matches!(e, ValidationError::InvalidReferenceFormat { .. })));\n }\n\n #[test]\n fn test_undefined_reference() {\n let input = r#\"Power\n .core\n - corrupts &Trust.formation\"#;\n let result = validate(input);\n assert!(!result.is_valid());\n assert!(result.errors.iter().any(|e| matches!(e, ValidationError::UndefinedReference { .. })));\n }\n\n // ==================== Brief form tests ====================", + "old_string": " fn test_invalid_reference_format() {\n let input = r#\"Power\n .core\n - corrupts &InvalidReference\"#;\n let result = validate(input);\n assert!(!result.is_valid());\n assert!(result.errors.iter().any(|e| matches!(e, ValidationError::InvalidReferenceFormat { .. })));\n }\n\n // ==================== Brief form tests ====================" + } + ], + "path": "validator/src/lib.rs" + }, + "status": "complete", + "text": "Successfully applied 1 edit(s) to validator/src/lib.rs", + "background": false + }, + { + "type": "ThinkingBlock", + "text": "Now let's run all the tests to make sure everything passes.", + "status": "complete" + }, + { + "type": "ShellBlock", + "call_id": "toolu_01EMJWj65WxQijwXaLibCRGe", + "tool_name": "mcp_shell", + "params": { + "command": "cd validator && cargo test 2>&1 | tail -15" + }, + "status": "complete", + "text": "failures:\n\n---- tests::test_claim_with_reference stdout ----\n\nthread 'tests::test_claim_with_reference' (422039443) panicked at src/lib.rs:890:17:\nassertion failed: claim.references.contains(&\"Trust.formation\".to_string())\nnote: run with `RUST_BACKTRACE=1` environment variable to display a backtrace\n\n\nfailures:\n tests::test_claim_with_reference\n\ntest result: FAILED. 33 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s\n\nerror: test failed, to rerun pass `--lib`\n", + "background": false + }, + { + "type": "ThinkingBlock", + "text": "Hmm the test is looking for the reference on a line but now it's on a different line (the 5th line instead of the 3rd). Let me check what's happening.", + "status": "complete" + }, + { + "type": "TextBlock", + "text": "The assertion is finding the wrong claim line now. Let me fix the test to look at the right line:", + "status": "complete" + }, + { + "type": "ReadFileBlock", + "call_id": "toolu_01Pnj22NgWExfsCDCnFe1PDE", + "tool_name": "mcp_read_file", + "params": { + "end_line": 905, + "path": "validator/src/lib.rs", + "start_line": 878 + }, + "status": "complete", + "text": " 878│ fn test_claim_with_reference() {\n 879│ let input = r#\"Trust\n 880│ .formation\n 881│ - slow\n 882│ .erosion\n 883│ - asymmetric vs formation &Trust.formation\"#;\n 884│\n 885│ let result = validate(input);\n 886│ assert!(result.is_valid(), \"Expected valid: {:?}\", result.errors);\n 887│\n 888│ if let Some(line) = result.lines.iter().find(|l| matches!(l.line_type, LineType::Claim(_))) {\n 889│ if let LineType::Claim(claim) = &line.line_type {\n 890│ assert!(claim.references.contains(&\"Trust.formation\".to_string()));\n 891│ }\n 892│ }\n 893│ }\n 894│\n 895│ #[test]\n 896│ fn test_invalid_reference_format() {\n 897│ let input = r#\"Power\n 898│ .core\n 899│ - corrupts &InvalidReference\"#;\n 900│ let result = validate(input);\n 901│ assert!(!result.is_valid());\n 902│ assert!(result.errors.iter().any(|e| matches!(e, ValidationError::InvalidReferenceFormat { .. })));\n 903│ }\n 904│\n 905│ #[test]\n", + "background": false + }, + { + "type": "ThinkingBlock", + "text": "The test finds the first claim line which is \"slow\" and doesn't have any references. I need to find the claim that actually has the reference. Let me fix the test.", + "status": "complete" + }, + { + "type": "EditFileBlock", + "call_id": "toolu_01KJux7h7JbLghzvcQmRyWQC", + "tool_name": "mcp_edit_file", + "params": { + "edits": [ + { + "new_string": " fn test_claim_with_reference() {\n let input = r#\"Trust\n .formation\n - slow\n .erosion\n - asymmetric vs formation &Trust.formation\"#;\n\n let result = validate(input);\n assert!(result.is_valid(), \"Expected valid: {:?}\", result.errors);\n\n // Find the claim with the reference (second claim)\n let claims: Vec<_> = result.lines.iter()\n .filter_map(|l| match &l.line_type {\n LineType::Claim(c) => Some(c),\n _ => None,\n })\n .collect();\n \n assert!(claims.len() >= 2);\n assert!(claims[1].references.contains(&\"Trust.formation\".to_string()));\n }", + "old_string": " fn test_claim_with_reference() {\n let input = r#\"Trust\n .formation\n - slow\n .erosion\n - asymmetric vs formation &Trust.formation\"#;\n\n let result = validate(input);\n assert!(result.is_valid(), \"Expected valid: {:?}\", result.errors);\n\n if let Some(line) = result.lines.iter().find(|l| matches!(l.line_type, LineType::Claim(_))) {\n if let LineType::Claim(claim) = &line.line_type {\n assert!(claim.references.contains(&\"Trust.formation\".to_string()));\n }\n }\n }" + } + ], + "path": "validator/src/lib.rs" + }, + "status": "complete", + "text": "Successfully applied 1 edit(s) to validator/src/lib.rs", + "background": false + }, + { + "type": "ThinkingBlock", + "text": "Now let's run the tests again.", + "status": "complete" + }, + { + "type": "ShellBlock", + "call_id": "toolu_01BYhyqXZzEVzRWmZmBXEq8S", + "tool_name": "mcp_shell", + "params": { + "command": "cd validator && cargo test 2>&1 | tail -10" + }, + "status": "complete", + "text": "running 0 tests\n\ntest result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s\n\n Doc-tests worldview_validator\n\nrunning 0 tests\n\ntest result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s\n\n", + "background": false + }, + { + "type": "ThinkingBlock", + "text": "Hmm, that only shows doc-tests. Let me run the full test output.", + "status": "complete" + }, + { + "type": "ShellBlock", + "call_id": "toolu_01Mb1fXFgWgDSLNkvntha2HD", + "tool_name": "mcp_shell", + "params": { + "command": "cd validator && cargo test 2>&1" + }, + "status": "complete", + "text": " Finished `test` profile [unoptimized + debuginfo] target(s) in 0.01s\n Running unittests src/lib.rs (target/debug/deps/worldview_validator-83762e759e1950e7)\n\nrunning 34 tests\ntest tests::test_brief_form_caused_by ... ok\ntest tests::test_blank_lines_allowed ... ok\ntest tests::test_brief_form_causes ... ok\ntest tests::test_brief_form_contrast ... ok\ntest tests::test_brief_form_missing_left_operand ... ok\ntest tests::test_brief_form_regardless ... ok\ntest tests::test_brief_form_mutual ... ok\ntest tests::test_brief_form_missing_right_operand ... ok\ntest tests::test_brief_form_similar ... ok\ntest tests::test_brief_form_tension ... ok\ntest tests::test_claim_with_conditions_and_sources ... ok\ntest tests::test_claim_with_reference ... ok\ntest tests::test_concept_without_facet ... ok\ntest tests::test_empty_evolution_marker ... ok\ntest tests::test_evolution_marker ... ok\ntest tests::test_evolution_marker_rationalizes ... ok\ntest tests::test_facet_without_claim ... ok\ntest tests::test_invalid_indentation ... ok\ntest tests::test_invalid_reference_format ... ok\ntest tests::test_missing_claim_prefix ... ok\ntest tests::test_missing_facet_prefix ... ok\ntest tests::test_modifier_emphatic ... ok\ntest tests::test_modifier_notable ... ok\ntest tests::test_modifier_uncertain ... ok\ntest tests::test_orphan_claim ... ok\ntest tests::test_modifier_increasing ... ok\ntest tests::test_orphan_facet ... ok\ntest tests::test_space_separated_modifier_valid ... ok\ntest tests::test_unclosed_evolution_marker ... ok\ntest tests::test_standalone_modifier_warning ... ok\ntest tests::test_modifier_decreasing ... ok\ntest tests::test_expanded_document ... ok\ntest tests::test_valid_minimal_document ... ok\ntest tests::test_undefined_reference ... ok\n\ntest result: ok. 34 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s\n\n Running unittests src/main.rs (target/debug/deps/worldview_validate-56487114d7d41bd3)\n\nrunning 0 tests\n\ntest result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s\n\n Doc-tests worldview_validator\n\nrunning 0 tests\n\ntest result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s\n\n", + "background": false + }, + { + "type": "ThinkingBlock", + "text": "All 34 tests pass, including the new `test_undefined_reference` test. Let me also verify the example.wvf file still validates correctly since it has references.", + "status": "complete" + }, + { + "type": "TextBlock", + "text": "All 34 tests pass. Let's also make sure the example.wvf file still validates (it has cross-references):", + "status": "complete" + }, + { + "type": "ShellBlock", + "call_id": "toolu_013PhWH648Shi7EEU8aBTWqT", + "tool_name": "mcp_shell", + "params": { + "command": "cd validator && cargo run -- ../example.wvf 2>&1" + }, + "status": "complete", + "text": " Compiling worldview-validator v0.1.0 (/Users/tcdent/Work/wvf/validator)\n Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.11s\n Running `target/debug/worldview-validate ../example.wvf`\nValid Worldview document\n", + "background": false + }, + { + "type": "ThinkingBlock", + "text": "The example file validates correctly. All the references in it are valid (like `&Trust.institutional`, `&Trust.formation`, `&Human-nature.cognition`).\n\nThis was only about 30 lines of actual logic - not much work at all. Let me commit this.", + "status": "complete" + }, + { + "type": "TextBlock", + "text": "All references in example.wvf check out. Let's commit:", + "status": "complete" + }, + { + "type": "ShellBlock", + "call_id": "toolu_017TEpJiQ7mFA2dyHRpTxxNi", + "tool_name": "mcp_shell", + "params": { + "command": "git add -A && git commit -m \"Add validation for undefined references\n\nReferences like &Concept.facet now validated to ensure the target\nconcept and facet actually exist in the document.\n\n- Added UndefinedReference error variant\n- collect_valid_references() builds set of valid targets\n- validate_claim_syntax() checks references against the set\n- Added test_undefined_reference test case\n- Fixed test_claim_with_reference to include referenced facet\"" + }, + "status": "complete", + "text": "[main 5113067] Add validation for undefined references\n 2 files changed, 640 insertions(+), 8 deletions(-)\n", + "background": false + }, + { + "type": "ThinkingBlock", + "text": "Done. That was about 30 lines of actual code as I predicted. The tests went from 33 to 34.\n\nWhat else should we look at? Some options:\n1. Python evals - missing pyproject.toml \n2. The claim parsing in lib.rs is a bit complex\n3. The agent could have better error handling\n\nLet me ask if they want to continue or have something specific in mind.", + "status": "complete" + }, + { + "type": "TextBlock", + "text": "Done! ~30 lines of logic as predicted. The validator now catches dangling references.\n\nWhat's next? I noticed:\n1. **Python evals** - no `pyproject.toml`, still using `requirements.txt`\n2. **Claim parsing** - `parse_claim()` is doing a lot in one function \n3. **Agent error handling** - no timeouts or graceful API failure handling\n\nOr is there something else you're curious about?", + "status": "complete" + } + ], + "timestamp": "2026-01-21T23:12:03.802806Z" + }, + { + "id": 9, + "role": "user", + "content": [ + { + "type": "TextBlock", + "text": "haha awesome let's add a case to validate it in the other direction, too", + "status": "complete" + } + ], + "timestamp": "2026-01-21T23:14:00.508949Z" + }, + { + "id": 10, + "role": "assistant", + "content": [], + "timestamp": "2026-01-21T23:15:03.592849Z" + }, + { + "id": 11, + "role": "user", + "content": [ + { + "type": "TextBlock", + "text": "yeah i like the idea of moving to pyproject.toml", + "status": "complete" + } + ], + "timestamp": "2026-01-21T23:15:44.439396Z" + }, + { + "id": 12, + "role": "assistant", + "content": [ + { + "type": "ThinkingBlock", + "text": "The user wants to:\n1. Add a test case for valid references (the \"other direction\" - making sure valid references pass)\n2. Move the Python evals from requirements.txt to pyproject.toml\n\nLet me do both.", + "status": "complete" + }, + { + "type": "TextBlock", + "text": "Good call - we should test that valid cross-references pass, not just that invalid ones fail. Let me add that test and then modernize the Python packaging:", + "status": "complete" + }, + { + "type": "ReadFileBlock", + "call_id": "toolu_01M9xnn2vQZcbB1d7opRTDX3", + "tool_name": "mcp_read_file", + "params": { + "end_line": 925, + "path": "validator/src/lib.rs", + "start_line": 905 + }, + "status": "complete", + "text": " 905│ let result = validate(input);\n 906│ assert!(!result.is_valid());\n 907│ assert!(result.errors.iter().any(|e| matches!(e, ValidationError::InvalidReferenceFormat { .. })));\n 908│ }\n 909│\n 910│ #[test]\n 911│ fn test_undefined_reference() {\n 912│ let input = r#\"Power\n 913│ .core\n 914│ - corrupts &Trust.formation\"#;\n 915│ let result = validate(input);\n 916│ assert!(!result.is_valid());\n 917│ assert!(result.errors.iter().any(|e| matches!(e, ValidationError::UndefinedReference { .. })));\n 918│ }\n 919│\n 920│ // ==================== Brief form tests ====================\n 921│\n 922│ #[test]\n 923│ fn test_brief_form_causes() {\n 924│ let input = r#\"Power\n 925│ .nature\n", + "background": false + }, + { + "type": "ThinkingBlock", + "text": "Let me add a test for valid cross-references between concepts.", + "status": "complete" + }, + { + "type": "EditFileBlock", + "call_id": "toolu_01NksEJi2rirAR78n4Tz5e46", + "tool_name": "mcp_edit_file", + "params": { + "edits": [ + { + "new_string": " #[test]\n fn test_undefined_reference() {\n let input = r#\"Power\n .core\n - corrupts &Trust.formation\"#;\n let result = validate(input);\n assert!(!result.is_valid());\n assert!(result.errors.iter().any(|e| matches!(e, ValidationError::UndefinedReference { .. })));\n }\n\n #[test]\n fn test_valid_cross_reference() {\n // References between different concepts should work\n let input = r#\"Power\n .institutional\n - accountability <> trust &Trust.institutional\n\nTrust\n .institutional\n - possible | high transparency\"#;\n let result = validate(input);\n assert!(result.is_valid(), \"Expected valid: {:?}\", result.errors);\n }\n\n // ==================== Brief form tests ====================", + "old_string": " #[test]\n fn test_undefined_reference() {\n let input = r#\"Power\n .core\n - corrupts &Trust.formation\"#;\n let result = validate(input);\n assert!(!result.is_valid());\n assert!(result.errors.iter().any(|e| matches!(e, ValidationError::UndefinedReference { .. })));\n }\n\n // ==================== Brief form tests ====================" + } + ], + "path": "validator/src/lib.rs" + }, + "status": "complete", + "text": "Successfully applied 1 edit(s) to validator/src/lib.rs", + "background": false + }, + { + "type": "ThinkingBlock", + "text": "Now let me run the tests to make sure it passes.", + "status": "complete" + }, + { + "type": "ShellBlock", + "call_id": "toolu_01KnPbJ8X2BExcpDsPuewNuT", + "tool_name": "mcp_shell", + "params": { + "command": "cd validator && cargo test test_valid_cross_reference 2>&1" + }, + "status": "complete", + "text": " Compiling worldview-validator v0.1.0 (/Users/tcdent/Work/wvf/validator)\n Finished `test` profile [unoptimized + debuginfo] target(s) in 0.21s\n Running unittests src/lib.rs (target/debug/deps/worldview_validator-83762e759e1950e7)\n\nrunning 1 test\ntest tests::test_valid_cross_reference ... ok\n\ntest result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 34 filtered out; finished in 0.00s\n\n Running unittests src/main.rs (target/debug/deps/worldview_validate-56487114d7d41bd3)\n\nrunning 0 tests\n\ntest result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s\n\n", + "background": false + }, + { + "type": "ThinkingBlock", + "text": "Now let me look at the requirements.txt and create a pyproject.toml.", + "status": "complete" + }, + { + "type": "TextBlock", + "text": "Now let's check the Python setup and create a proper pyproject.toml:", + "status": "complete" + }, + { + "type": "ReadFileBlock", + "call_id": "toolu_01Agx9124yKfGCjuoHnvAJxz", + "tool_name": "mcp_read_file", + "params": { + "path": "evals/requirements.txt" + }, + "status": "complete", + "text": " 1│# WSL Evaluation Framework Dependencies\n 2│\n 3│# LiteLLM for unified LLM access\n 4│litellm>=1.30.0\n 5│\n 6│# These are optional but recommended for direct provider access\n 7│# (LiteLLM will use them if installed)\n 8│anthropic>=0.25.0\n 9│openai>=1.12.0\n", + "background": false + }, + { + "type": "ShellBlock", + "call_id": "toolu_01MoCbYZS3Vs5eLQhRj8y9jn", + "tool_name": "mcp_shell", + "params": { + "command": "ls -la evals/" + }, + "status": "complete", + "text": "total 48\ndrwxr-xr-x@ 9 tcdent staff 288 Jan 21 10:11 .\ndrwxr-xr-x@ 14 tcdent staff 448 Jan 21 15:02 ..\n-rw-r--r--@ 1 tcdent staff 360 Jan 21 10:11 __init__.py\n-rw-r--r--@ 1 tcdent staff 101 Jan 21 10:11 __main__.py\n-rw-r--r--@ 1 tcdent staff 11372 Jan 21 10:11 cli.py\ndrwxr-xr-x@ 5 tcdent staff 160 Jan 21 10:11 common\ndrwxr-xr-x@ 7 tcdent staff 224 Jan 21 10:11 read_eval\n-rw-r--r--@ 1 tcdent staff 227 Jan 21 10:11 requirements.txt\ndrwxr-xr-x@ 6 tcdent staff 192 Jan 21 10:11 write_eval\n", + "background": false + } + ], + "timestamp": "2026-01-21T23:15:44.442151Z" + }, + { + "id": 13, + "role": "user", + "content": [ + { + "type": "TextBlock", + "text": "actuall --amend that last test to the previous commit first, then do the py stuff", + "status": "complete" + } + ], + "timestamp": "2026-01-21T23:16:25.755930Z" + }, + { + "id": 14, + "role": "assistant", + "content": [ + { + "type": "ThinkingBlock", + "text": "Good call - the test should be part of the reference validation commit since it's testing that feature.", + "status": "complete" + }, + { + "type": "ShellBlock", + "call_id": "toolu_0145Wu46Z7LbMss6NB49E9ky", + "tool_name": "mcp_shell", + "params": { + "command": "git add -A && git commit --amend -m \"Add validation for undefined references\n\nReferences like &Concept.facet now validated to ensure the target\nconcept and facet actually exist in the document.\n\n- Added UndefinedReference error variant\n- collect_valid_references() builds set of valid targets\n- validate_claim_syntax() checks references against the set\n- Added test_undefined_reference test case\n- Added test_valid_cross_reference test case\n- Fixed test_claim_with_reference to include referenced facet\"" + }, "status": "pending", "text": "", "background": false } ], - "timestamp": "2026-01-21T23:11:31.199860Z" + "timestamp": "2026-01-21T23:16:25.759134Z" } ], - "next_id": 7 + "next_id": 15 } \ No newline at end of file diff --git a/validator/src/lib.rs b/validator/src/lib.rs index 9e8358f..aa04bb1 100644 --- a/validator/src/lib.rs +++ b/validator/src/lib.rs @@ -4,6 +4,7 @@ //! It checks structural correctness (hierarchy, indentation), claim syntax, brief forms, //! modifiers, and evolution markers. +use std::collections::HashSet; use std::fmt; use thiserror::Error; @@ -55,6 +56,9 @@ pub enum ValidationError { #[error("line {line}: invalid reference format '{reference}' (expected &Concept.facet)")] InvalidReferenceFormat { line: usize, reference: String }, + #[error("line {line}: undefined reference '{reference}' (no such concept.facet in document)")] + UndefinedReference { line: usize, reference: String }, + #[error("line {line}: empty condition (standalone '|')")] EmptyCondition { line: usize }, @@ -208,13 +212,16 @@ pub fn validate(input: &str) -> ValidationResult { }); } + // Collect valid Concept.facet pairs for reference validation + let valid_refs = collect_valid_references(&lines); + // Second pass: validate structure validate_structure(&lines, &mut errors); // Third pass: validate claim syntax including brief forms, modifiers, evolution for line in &lines { if let LineType::Claim(claim) = &line.line_type { - validate_claim_syntax(line.line_number, claim, &mut errors, &mut warnings); + validate_claim_syntax(line.line_number, claim, &valid_refs, &mut errors, &mut warnings); } } @@ -619,10 +626,33 @@ fn validate_structure(lines: &[ParsedLine], errors: &mut Vec) { } } +/// Collect all valid Concept.facet reference targets from the document +fn collect_valid_references(lines: &[ParsedLine]) -> HashSet { + let mut valid_refs = HashSet::new(); + let mut current_concept: Option = None; + + for line in lines { + match &line.line_type { + LineType::Concept(name) => { + current_concept = Some(name.clone()); + } + LineType::Facet(name) => { + if let Some(ref concept) = current_concept { + valid_refs.insert(format!("{}.{}", concept, name)); + } + } + _ => {} + } + } + + valid_refs +} + /// Validate claim syntax including brief forms, modifiers, and evolution markers fn validate_claim_syntax( line_number: usize, claim: &ClaimData, + valid_refs: &HashSet, errors: &mut Vec, warnings: &mut Vec, ) { @@ -662,6 +692,16 @@ fn validate_claim_syntax( } } + // Validate references point to existing concept.facet pairs + for reference in &claim.references { + if !reference.is_empty() && reference.contains('.') && !valid_refs.contains(reference) { + errors.push(ValidationError::UndefinedReference { + line: line_number, + reference: reference.clone(), + }); + } + } + // Validate brief forms have operands for bf in &claim.brief_forms { if bf.left_operand.is_empty() { @@ -837,17 +877,24 @@ Trust #[test] fn test_claim_with_reference() { let input = r#"Trust + .formation + - slow .erosion - asymmetric vs formation &Trust.formation"#; let result = validate(input); assert!(result.is_valid(), "Expected valid: {:?}", result.errors); - if let Some(line) = result.lines.iter().find(|l| matches!(l.line_type, LineType::Claim(_))) { - if let LineType::Claim(claim) = &line.line_type { - assert!(claim.references.contains(&"Trust.formation".to_string())); - } - } + // Find the claim with the reference (second claim) + let claims: Vec<_> = result.lines.iter() + .filter_map(|l| match &l.line_type { + LineType::Claim(c) => Some(c), + _ => None, + }) + .collect(); + + assert!(claims.len() >= 2); + assert!(claims[1].references.contains(&"Trust.formation".to_string())); } #[test] @@ -860,6 +907,30 @@ Trust assert!(result.errors.iter().any(|e| matches!(e, ValidationError::InvalidReferenceFormat { .. }))); } + #[test] + fn test_undefined_reference() { + let input = r#"Power + .core + - corrupts &Trust.formation"#; + let result = validate(input); + assert!(!result.is_valid()); + assert!(result.errors.iter().any(|e| matches!(e, ValidationError::UndefinedReference { .. }))); + } + + #[test] + fn test_valid_cross_reference() { + // References between different concepts should work + let input = r#"Power + .institutional + - accountability <> trust &Trust.institutional + +Trust + .institutional + - possible | high transparency"#; + let result = validate(input); + assert!(result.is_valid(), "Expected valid: {:?}", result.errors); + } + // ==================== Brief form tests ==================== #[test] From b877337ee19ab71eb5affc760a05218363f563a4 Mon Sep 17 00:00:00 2001 From: Travis Dent Date: Wed, 21 Jan 2026 15:17:54 -0800 Subject: [PATCH 3/8] Migrate Python evals from requirements.txt to pyproject.toml - Added pyproject.toml at project root with uv support - Updated README to use uv commands - Removed evals/requirements.txt - Added uv.lock for reproducible builds --- .codey/transcripts/000000.json | 244 +++++ README.md | 10 +- evals/requirements.txt | 9 - pyproject.toml | 27 + uv.lock | 1843 ++++++++++++++++++++++++++++++++ 5 files changed, 2119 insertions(+), 14 deletions(-) delete mode 100644 evals/requirements.txt create mode 100644 pyproject.toml create mode 100644 uv.lock diff --git a/.codey/transcripts/000000.json b/.codey/transcripts/000000.json index 2f7e864..06f8582 100644 --- a/.codey/transcripts/000000.json +++ b/.codey/transcripts/000000.json @@ -1178,6 +1178,250 @@ "params": { "command": "git add -A && git commit --amend -m \"Add validation for undefined references\n\nReferences like &Concept.facet now validated to ensure the target\nconcept and facet actually exist in the document.\n\n- Added UndefinedReference error variant\n- collect_valid_references() builds set of valid targets\n- validate_claim_syntax() checks references against the set\n- Added test_undefined_reference test case\n- Added test_valid_cross_reference test case\n- Fixed test_claim_with_reference to include referenced facet\"" }, + "status": "complete", + "text": "[main 952b32a] Add validation for undefined references\n Date: Wed Jan 21 15:14:56 2026 -0800\n 2 files changed, 817 insertions(+), 8 deletions(-)\n", + "background": false + }, + { + "type": "ThinkingBlock", + "text": "Good, now let's create the pyproject.toml for the evals package.", + "status": "complete" + }, + { + "type": "TextBlock", + "text": "Now the pyproject.toml:", + "status": "complete" + }, + { + "type": "WriteFileBlock", + "call_id": "toolu_01Wbs2QtJb1NVnc8R6VJGoov", + "tool_name": "mcp_write_file", + "params": { + "content": "[project]\nname = \"worldview-evals\"\nversion = \"0.1.0\"\ndescription = \"Evaluation framework for Worldview format LLM integration\"\nreadme = \"../README.md\"\nrequires-python = \">=3.11\"\nlicense = \"MIT\"\ndependencies = [\n \"litellm>=1.30.0\",\n \"anthropic>=0.25.0\",\n \"openai>=1.12.0\",\n]\n\n[project.scripts]\nworldview-evals = \"evals.cli:main\"\n\n[build-system]\nrequires = [\"hatchling\"]\nbuild-backend = \"hatchling.build\"\n\n[tool.hatch.build.targets.wheel]\npackages = [\".\"]\n\n[tool.uv]\ndev-dependencies = [\n \"pytest>=8.0\",\n]\n", + "path": "evals/pyproject.toml" + }, + "status": "complete", + "text": "Created file: evals/pyproject.toml (27 lines, 516 bytes)", + "background": false + }, + { + "type": "ThinkingBlock", + "text": "Now let's test that it works with uv, then remove the old requirements.txt.", + "status": "complete" + }, + { + "type": "ShellBlock", + "call_id": "toolu_01Lj9sZyQ6syKq9hEm34FWsM", + "tool_name": "mcp_shell", + "params": { + "command": "cd evals && uv sync 2>&1 | tail -20" + }, + "status": "complete", + "text": " + pygments==2.19.2\n + pytest==9.0.2\n + python-dotenv==1.2.1\n + pyyaml==6.0.3\n + referencing==0.37.0\n + regex==2026.1.15\n + requests==2.32.5\n + rpds-py==0.30.0\n + shellingham==1.5.4\n + sniffio==1.3.1\n + tiktoken==0.12.0\n + tokenizers==0.22.2\n + tqdm==4.67.1\n + typer-slim==0.21.1\n + typing-extensions==4.15.0\n + typing-inspection==0.4.2\n + urllib3==2.6.3\n + worldview-evals==0.1.0 (from file:///Users/tcdent/Work/wvf/evals)\n + yarl==1.22.0\n + zipp==3.23.0\n", + "background": false + }, + { + "type": "ThinkingBlock", + "text": "Looks like uv successfully synced. Let's test running the module.", + "status": "complete" + }, + { + "type": "ShellBlock", + "call_id": "toolu_01Q5Er2hFuxD1vxpL3NGkeUs", + "tool_name": "mcp_shell", + "params": { + "command": "cd evals && uv run python -m evals list-models 2>&1 | head -20" + }, + "status": "complete", + "text": "/Users/tcdent/Work/wvf/evals/.venv/bin/python3: No module named evals\n", + "background": false + }, + { + "type": "ThinkingBlock", + "text": "The pyproject.toml is in the evals directory, so the package structure is a bit odd. The issue is that we're in evals/ trying to import evals as a module. Let me check the package structure.\n\nThe issue is that pyproject.toml is inside evals/, but the package name is \"worldview-evals\" and it's trying to build from that directory. The standard approach would be to either:\n1. Put pyproject.toml at the repo root and reference evals/ as a package\n2. Or restructure to have evals/src/worldview_evals/...\n\nActually looking at the structure, the simpler fix is to put pyproject.toml at the project root and just include evals as a package. That's cleaner anyway.", + "status": "complete" + }, + { + "type": "TextBlock", + "text": "Ah, the package structure is awkward with pyproject.toml inside `evals/`. Let me put it at the project root instead - cleaner:", + "status": "complete" + }, + { + "type": "ShellBlock", + "call_id": "toolu_01Tfk44mafnwbQBbnutYVenU", + "tool_name": "mcp_shell", + "params": { + "command": "rm evals/pyproject.toml" + }, + "status": "complete", + "text": "(no output)", + "background": false + }, + { + "type": "WriteFileBlock", + "call_id": "toolu_01A1sE2oDoxZMyQKiy1Jaea8", + "tool_name": "mcp_write_file", + "params": { + "content": "[project]\nname = \"worldview-evals\"\nversion = \"0.1.0\"\ndescription = \"Evaluation framework for Worldview format LLM integration\"\nreadme = \"README.md\"\nrequires-python = \">=3.11\"\nlicense = \"MIT\"\ndependencies = [\n \"litellm>=1.30.0\",\n \"anthropic>=0.25.0\",\n \"openai>=1.12.0\",\n]\n\n[project.scripts]\nworldview-evals = \"evals.cli:main\"\n\n[build-system]\nrequires = [\"hatchling\"]\nbuild-backend = \"hatchling.build\"\n\n[tool.hatch.build.targets.wheel]\npackages = [\"evals\"]\n\n[tool.uv]\ndev-dependencies = [\n \"pytest>=8.0\",\n]\n", + "path": "pyproject.toml" + }, + "status": "complete", + "text": "Created file: pyproject.toml (27 lines, 517 bytes)", + "background": false + }, + { + "type": "ThinkingBlock", + "text": "Now let me remove the old venv and try again from the project root.", + "status": "complete" + }, + { + "type": "ShellBlock", + "call_id": "toolu_01WakJpk4obvhCjTcZ8iA1Cq", + "tool_name": "mcp_shell", + "params": { + "command": "rm -rf evals/.venv && uv sync 2>&1 | tail -10" + }, + "status": "complete", + "text": " + tiktoken==0.12.0\n + tokenizers==0.22.2\n + tqdm==4.67.1\n + typer-slim==0.21.1\n + typing-extensions==4.15.0\n + typing-inspection==0.4.2\n + urllib3==2.6.3\n + worldview-evals==0.1.0 (from file:///Users/tcdent/Work/wvf)\n + yarl==1.22.0\n + zipp==3.23.0\n", + "background": false + }, + { + "type": "ThinkingBlock", + "text": "Now let's test running the module.", + "status": "complete" + }, + { + "type": "ShellBlock", + "call_id": "toolu_01F36pyKjYs98oHZcjj8RPcn", + "tool_name": "mcp_shell", + "params": { + "command": "uv run python -m evals list-models 2>&1 | head -15" + }, + "status": "complete", + "text": "Read Evaluation Models (for testing LLM response to Worldview context):\n\nName Provider Model ID Default\n--------------------------------------------------------------------------------\nClaude Sonnet 4 anthropic claude-sonnet-4-20250514 Yes\nClaude Opus 4.5 anthropic claude-opus-4-5-20251101 \nClaude Haiku 4.5 anthropic claude-haiku-4-5-20251001 \nGPT-5.2 openai gpt-5.2 Yes\nGPT-5 Mini openai gpt-5-mini \n\n\nWrite Evaluation Models (for testing Worldview document generation):\n\nName Model ID Default\n----------------------------------------------------------------------\n", + "background": false + }, + { + "type": "ThinkingBlock", + "text": "Now let's remove the old requirements.txt and update .gitignore to ignore .venv and uv.lock.", + "status": "complete" + }, + { + "type": "TextBlock", + "text": "Now let's remove the old requirements.txt and update the README:", + "status": "complete" + }, + { + "type": "ShellBlock", + "call_id": "toolu_015pxMC3BVZjL1S59iDBiqcA", + "tool_name": "mcp_shell", + "params": { + "command": "rm evals/requirements.txt" + }, + "status": "complete", + "text": "(no output)", + "background": false + }, + { + "type": "ReadFileBlock", + "call_id": "toolu_015uicEp1WYBWVi8D8G5BEHM", + "tool_name": "mcp_read_file", + "params": { + "end_line": 160, + "path": "README.md", + "start_line": 138 + }, + "status": "complete", + "text": " 138│### Evaluation Framework\n 139│\n 140│A Python framework for testing how well LLMs can leverage Worldview-encoded beliefs.\n 141│\n 142│```bash\n 143│# Install dependencies\n 144│pip install -r evals/requirements.txt\n 145│\n 146│# Run evaluations\n 147│python -m evals run --models claude-sonnet gpt-5.2\n 148│\n 149│# Run specific difficulty\n 150│python -m evals run --difficulty extreme\n 151│\n 152│# List available models and test cases\n 153│python -m evals list-models\n 154│python -m evals list-cases\n 155│```\n 156│\n 157│## Project Structure\n 158│\n 159│```\n 160│wvf/\n", + "background": false + }, + { + "type": "ThinkingBlock", + "text": "Let me update the README to use uv instead of pip.", + "status": "complete" + }, + { + "type": "EditFileBlock", + "call_id": "toolu_015ZFnmEgeCc9dXH6dbptbyZ", + "tool_name": "mcp_edit_file", + "params": { + "edits": [ + { + "new_string": "### Evaluation Framework\n\nA Python framework for testing how well LLMs can leverage Worldview-encoded beliefs.\n\n```bash\n# Install dependencies\nuv sync\n\n# Run evaluations\nuv run python -m evals run --models claude-sonnet gpt-5.2\n\n# Run specific difficulty\nuv run python -m evals run --difficulty extreme\n\n# List available models and test cases\nuv run python -m evals list-models\nuv run python -m evals list-cases\n```", + "old_string": "### Evaluation Framework\n\nA Python framework for testing how well LLMs can leverage Worldview-encoded beliefs.\n\n```bash\n# Install dependencies\npip install -r evals/requirements.txt\n\n# Run evaluations\npython -m evals run --models claude-sonnet gpt-5.2\n\n# Run specific difficulty\npython -m evals run --difficulty extreme\n\n# List available models and test cases\npython -m evals list-models\npython -m evals list-cases\n```" + } + ], + "path": "README.md" + }, + "status": "complete", + "text": "Successfully applied 1 edit(s) to README.md", + "background": false + }, + { + "type": "ThinkingBlock", + "text": "Let me also check if there's a .gitignore and if we need to add entries for uv.", + "status": "complete" + }, + { + "type": "ReadFileBlock", + "call_id": "toolu_015q4jTTUPafPrCPyiwJNA33", + "tool_name": "mcp_read_file", + "params": { + "path": ".gitignore" + }, + "status": "complete", + "text": " 1│# Python\n 2│__pycache__/\n 3│*.py[cod]\n 4│*$py.class\n 5│*.so\n 6│.Python\n 7│build/\n 8│develop-eggs/\n 9│dist/\n 10│downloads/\n 11│eggs/\n 12│.eggs/\n 13│lib/\n 14│lib64/\n 15│parts/\n 16│sdist/\n 17│var/\n 18│wheels/\n 19│*.egg-info/\n 20│.installed.cfg\n 21│*.egg\n 22│\n 23│# Virtual environments\n 24│.env\n 25│.venv\n 26│env/\n 27│venv/\n 28│ENV/\n 29│\n 30│# IDE\n 31│.idea/\n 32│.vscode/\n 33│*.swp\n 34│*.swo\n 35│*~\n 36│\n 37│# Testing\n 38│.pytest_cache/\n 39│.coverage\n 40│htmlcov/\n 41│\n 42│# Evaluation results (local)\n 43│results/\n 44│*.log\n 45│\n 46│# Generated files (created at compile-time)\n 47│validator/src/tokens_generated.rs\n", + "background": false + }, + { + "type": "ThinkingBlock", + "text": ".gitignore already covers .venv. I should add uv.lock since that's good to commit (lockfiles ensure reproducible builds). Actually, let me double check - uv.lock should probably be committed for reproducibility. Let me check if it's there.", + "status": "complete" + }, + { + "type": "ShellBlock", + "call_id": "toolu_01DiS8LewDxMiaZAN91oSXAT", + "tool_name": "mcp_shell", + "params": { + "command": "ls -la *.lock 2>/dev/null || echo \"no lock files\"" + }, + "status": "complete", + "text": "-rw-r--r--@ 1 tcdent staff 364653 Jan 21 15:17 uv.lock\n", + "background": false + }, + { + "type": "ThinkingBlock", + "text": "Good, uv.lock is there. Lock files should be committed for reproducibility. Let me commit everything.", + "status": "complete" + }, + { + "type": "TextBlock", + "text": "Good - uv.lock should be committed for reproducibility. Let's commit:", + "status": "complete" + }, + { + "type": "ShellBlock", + "call_id": "toolu_01NUwNj5PZrgcGBJ3unkGDdW", + "tool_name": "mcp_shell", + "params": { + "command": "git add -A && git status" + }, + "status": "complete", + "text": "On branch main\nYour branch is ahead of 'origin/main' by 2 commits.\n (use \"git push\" to publish your local commits)\n\nChanges to be committed:\n (use \"git restore --staged ...\" to unstage)\n\tmodified: .codey/transcripts/000000.json\n\tmodified: README.md\n\tdeleted: evals/requirements.txt\n\tnew file: evals/uv.lock\n\tnew file: pyproject.toml\n\tnew file: uv.lock\n\n", + "background": false + }, + { + "type": "ThinkingBlock", + "text": "Wait, there's an `evals/uv.lock` file - that's from when I accidentally ran `uv sync` in the evals dir with the first pyproject.toml. Let me remove that.", + "status": "complete" + }, + { + "type": "ShellBlock", + "call_id": "toolu_01JH9PRbMC266PGHrhRuqB4t", + "tool_name": "mcp_shell", + "params": { + "command": "rm evals/uv.lock && git add -A && git status" + }, "status": "pending", "text": "", "background": false diff --git a/README.md b/README.md index d586c47..d09029c 100644 --- a/README.md +++ b/README.md @@ -141,17 +141,17 @@ A Python framework for testing how well LLMs can leverage Worldview-encoded beli ```bash # Install dependencies -pip install -r evals/requirements.txt +uv sync # Run evaluations -python -m evals run --models claude-sonnet gpt-5.2 +uv run python -m evals run --models claude-sonnet gpt-5.2 # Run specific difficulty -python -m evals run --difficulty extreme +uv run python -m evals run --difficulty extreme # List available models and test cases -python -m evals list-models -python -m evals list-cases +uv run python -m evals list-models +uv run python -m evals list-cases ``` ## Project Structure diff --git a/evals/requirements.txt b/evals/requirements.txt deleted file mode 100644 index f647c50..0000000 --- a/evals/requirements.txt +++ /dev/null @@ -1,9 +0,0 @@ -# WSL Evaluation Framework Dependencies - -# LiteLLM for unified LLM access -litellm>=1.30.0 - -# These are optional but recommended for direct provider access -# (LiteLLM will use them if installed) -anthropic>=0.25.0 -openai>=1.12.0 diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..008f309 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,27 @@ +[project] +name = "worldview-evals" +version = "0.1.0" +description = "Evaluation framework for Worldview format LLM integration" +readme = "README.md" +requires-python = ">=3.11" +license = "MIT" +dependencies = [ + "litellm>=1.30.0", + "anthropic>=0.25.0", + "openai>=1.12.0", +] + +[project.scripts] +worldview-evals = "evals.cli:main" + +[build-system] +requires = ["hatchling"] +build-backend = "hatchling.build" + +[tool.hatch.build.targets.wheel] +packages = ["evals"] + +[tool.uv] +dev-dependencies = [ + "pytest>=8.0", +] diff --git a/uv.lock b/uv.lock new file mode 100644 index 0000000..4f1a150 --- /dev/null +++ b/uv.lock @@ -0,0 +1,1843 @@ +version = 1 +revision = 1 +requires-python = ">=3.11" +resolution-markers = [ + "python_full_version >= '3.14'", + "python_full_version < '3.14'", +] + +[[package]] +name = "aiohappyeyeballs" +version = "2.6.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/26/30/f84a107a9c4331c14b2b586036f40965c128aa4fee4dda5d3d51cb14ad54/aiohappyeyeballs-2.6.1.tar.gz", hash = "sha256:c3f9d0113123803ccadfdf3f0faa505bc78e6a72d1cc4806cbd719826e943558", size = 22760 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0f/15/5bf3b99495fb160b63f95972b81750f18f7f4e02ad051373b669d17d44f2/aiohappyeyeballs-2.6.1-py3-none-any.whl", hash = "sha256:f349ba8f4b75cb25c99c5c2d84e997e485204d2902a9597802b0371f09331fb8", size = 15265 }, +] + +[[package]] +name = "aiohttp" +version = "3.13.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "aiohappyeyeballs" }, + { name = "aiosignal" }, + { name = "attrs" }, + { name = "frozenlist" }, + { name = "multidict" }, + { name = "propcache" }, + { name = "yarl" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/50/42/32cf8e7704ceb4481406eb87161349abb46a57fee3f008ba9cb610968646/aiohttp-3.13.3.tar.gz", hash = "sha256:a949eee43d3782f2daae4f4a2819b2cb9b0c5d3b7f7a927067cc84dafdbb9f88", size = 7844556 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f1/4c/a164164834f03924d9a29dc3acd9e7ee58f95857e0b467f6d04298594ebb/aiohttp-3.13.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:5b6073099fb654e0a068ae678b10feff95c5cae95bbfcbfa7af669d361a8aa6b", size = 746051 }, + { url = "https://files.pythonhosted.org/packages/82/71/d5c31390d18d4f58115037c432b7e0348c60f6f53b727cad33172144a112/aiohttp-3.13.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1cb93e166e6c28716c8c6aeb5f99dfb6d5ccf482d29fe9bf9a794110e6d0ab64", size = 499234 }, + { url = "https://files.pythonhosted.org/packages/0e/c9/741f8ac91e14b1d2e7100690425a5b2b919a87a5075406582991fb7de920/aiohttp-3.13.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:28e027cf2f6b641693a09f631759b4d9ce9165099d2b5d92af9bd4e197690eea", size = 494979 }, + { url = "https://files.pythonhosted.org/packages/75/b5/31d4d2e802dfd59f74ed47eba48869c1c21552c586d5e81a9d0d5c2ad640/aiohttp-3.13.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3b61b7169ababd7802f9568ed96142616a9118dd2be0d1866e920e77ec8fa92a", size = 1748297 }, + { url = "https://files.pythonhosted.org/packages/1a/3e/eefad0ad42959f226bb79664826883f2687d602a9ae2941a18e0484a74d3/aiohttp-3.13.3-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:80dd4c21b0f6237676449c6baaa1039abae86b91636b6c91a7f8e61c87f89540", size = 1707172 }, + { url = "https://files.pythonhosted.org/packages/c5/3a/54a64299fac2891c346cdcf2aa6803f994a2e4beeaf2e5a09dcc54acc842/aiohttp-3.13.3-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:65d2ccb7eabee90ce0503c17716fc77226be026dcc3e65cce859a30db715025b", size = 1805405 }, + { url = "https://files.pythonhosted.org/packages/6c/70/ddc1b7169cf64075e864f64595a14b147a895a868394a48f6a8031979038/aiohttp-3.13.3-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5b179331a481cb5529fca8b432d8d3c7001cb217513c94cd72d668d1248688a3", size = 1899449 }, + { url = "https://files.pythonhosted.org/packages/a1/7e/6815aab7d3a56610891c76ef79095677b8b5be6646aaf00f69b221765021/aiohttp-3.13.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9d4c940f02f49483b18b079d1c27ab948721852b281f8b015c058100e9421dd1", size = 1748444 }, + { url = "https://files.pythonhosted.org/packages/6b/f2/073b145c4100da5511f457dc0f7558e99b2987cf72600d42b559db856fbc/aiohttp-3.13.3-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f9444f105664c4ce47a2a7171a2418bce5b7bae45fb610f4e2c36045d85911d3", size = 1606038 }, + { url = "https://files.pythonhosted.org/packages/0a/c1/778d011920cae03ae01424ec202c513dc69243cf2db303965615b81deeea/aiohttp-3.13.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:694976222c711d1d00ba131904beb60534f93966562f64440d0c9d41b8cdb440", size = 1724156 }, + { url = "https://files.pythonhosted.org/packages/0e/cb/3419eabf4ec1e9ec6f242c32b689248365a1cf621891f6f0386632525494/aiohttp-3.13.3-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:f33ed1a2bf1997a36661874b017f5c4b760f41266341af36febaf271d179f6d7", size = 1722340 }, + { url = "https://files.pythonhosted.org/packages/7a/e5/76cf77bdbc435bf233c1f114edad39ed4177ccbfab7c329482b179cff4f4/aiohttp-3.13.3-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:e636b3c5f61da31a92bf0d91da83e58fdfa96f178ba682f11d24f31944cdd28c", size = 1783041 }, + { url = "https://files.pythonhosted.org/packages/9d/d4/dd1ca234c794fd29c057ce8c0566b8ef7fd6a51069de5f06fa84b9a1971c/aiohttp-3.13.3-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:5d2d94f1f5fcbe40838ac51a6ab5704a6f9ea42e72ceda48de5e6b898521da51", size = 1596024 }, + { url = "https://files.pythonhosted.org/packages/55/58/4345b5f26661a6180afa686c473620c30a66afdf120ed3dd545bbc809e85/aiohttp-3.13.3-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:2be0e9ccf23e8a94f6f0650ce06042cefc6ac703d0d7ab6c7a917289f2539ad4", size = 1804590 }, + { url = "https://files.pythonhosted.org/packages/7b/06/05950619af6c2df7e0a431d889ba2813c9f0129cec76f663e547a5ad56f2/aiohttp-3.13.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:9af5e68ee47d6534d36791bbe9b646d2a7c7deb6fc24d7943628edfbb3581f29", size = 1740355 }, + { url = "https://files.pythonhosted.org/packages/3e/80/958f16de79ba0422d7c1e284b2abd0c84bc03394fbe631d0a39ffa10e1eb/aiohttp-3.13.3-cp311-cp311-win32.whl", hash = "sha256:a2212ad43c0833a873d0fb3c63fa1bacedd4cf6af2fee62bf4b739ceec3ab239", size = 433701 }, + { url = "https://files.pythonhosted.org/packages/dc/f2/27cdf04c9851712d6c1b99df6821a6623c3c9e55956d4b1e318c337b5a48/aiohttp-3.13.3-cp311-cp311-win_amd64.whl", hash = "sha256:642f752c3eb117b105acbd87e2c143de710987e09860d674e068c4c2c441034f", size = 457678 }, + { url = "https://files.pythonhosted.org/packages/a0/be/4fc11f202955a69e0db803a12a062b8379c970c7c84f4882b6da17337cc1/aiohttp-3.13.3-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:b903a4dfee7d347e2d87697d0713be59e0b87925be030c9178c5faa58ea58d5c", size = 739732 }, + { url = "https://files.pythonhosted.org/packages/97/2c/621d5b851f94fa0bb7430d6089b3aa970a9d9b75196bc93bb624b0db237a/aiohttp-3.13.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:a45530014d7a1e09f4a55f4f43097ba0fd155089372e105e4bff4ca76cb1b168", size = 494293 }, + { url = "https://files.pythonhosted.org/packages/5d/43/4be01406b78e1be8320bb8316dc9c42dbab553d281c40364e0f862d5661c/aiohttp-3.13.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:27234ef6d85c914f9efeb77ff616dbf4ad2380be0cda40b4db086ffc7ddd1b7d", size = 493533 }, + { url = "https://files.pythonhosted.org/packages/8d/a8/5a35dc56a06a2c90d4742cbf35294396907027f80eea696637945a106f25/aiohttp-3.13.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d32764c6c9aafb7fb55366a224756387cd50bfa720f32b88e0e6fa45b27dcf29", size = 1737839 }, + { url = "https://files.pythonhosted.org/packages/bf/62/4b9eeb331da56530bf2e198a297e5303e1c1ebdceeb00fe9b568a65c5a0c/aiohttp-3.13.3-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:b1a6102b4d3ebc07dad44fbf07b45bb600300f15b552ddf1851b5390202ea2e3", size = 1703932 }, + { url = "https://files.pythonhosted.org/packages/7c/f6/af16887b5d419e6a367095994c0b1332d154f647e7dc2bd50e61876e8e3d/aiohttp-3.13.3-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c014c7ea7fb775dd015b2d3137378b7be0249a448a1612268b5a90c2d81de04d", size = 1771906 }, + { url = "https://files.pythonhosted.org/packages/ce/83/397c634b1bcc24292fa1e0c7822800f9f6569e32934bdeef09dae7992dfb/aiohttp-3.13.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:2b8d8ddba8f95ba17582226f80e2de99c7a7948e66490ef8d947e272a93e9463", size = 1871020 }, + { url = "https://files.pythonhosted.org/packages/86/f6/a62cbbf13f0ac80a70f71b1672feba90fdb21fd7abd8dbf25c0105fb6fa3/aiohttp-3.13.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9ae8dd55c8e6c4257eae3a20fd2c8f41edaea5992ed67156642493b8daf3cecc", size = 1755181 }, + { url = "https://files.pythonhosted.org/packages/0a/87/20a35ad487efdd3fba93d5843efdfaa62d2f1479eaafa7453398a44faf13/aiohttp-3.13.3-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:01ad2529d4b5035578f5081606a465f3b814c542882804e2e8cda61adf5c71bf", size = 1561794 }, + { url = "https://files.pythonhosted.org/packages/de/95/8fd69a66682012f6716e1bc09ef8a1a2a91922c5725cb904689f112309c4/aiohttp-3.13.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:bb4f7475e359992b580559e008c598091c45b5088f28614e855e42d39c2f1033", size = 1697900 }, + { url = "https://files.pythonhosted.org/packages/e5/66/7b94b3b5ba70e955ff597672dad1691333080e37f50280178967aff68657/aiohttp-3.13.3-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:c19b90316ad3b24c69cd78d5c9b4f3aa4497643685901185b65166293d36a00f", size = 1728239 }, + { url = "https://files.pythonhosted.org/packages/47/71/6f72f77f9f7d74719692ab65a2a0252584bf8d5f301e2ecb4c0da734530a/aiohttp-3.13.3-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:96d604498a7c782cb15a51c406acaea70d8c027ee6b90c569baa6e7b93073679", size = 1740527 }, + { url = "https://files.pythonhosted.org/packages/fa/b4/75ec16cbbd5c01bdaf4a05b19e103e78d7ce1ef7c80867eb0ace42ff4488/aiohttp-3.13.3-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:084911a532763e9d3dd95adf78a78f4096cd5f58cdc18e6fdbc1b58417a45423", size = 1554489 }, + { url = "https://files.pythonhosted.org/packages/52/8f/bc518c0eea29f8406dcf7ed1f96c9b48e3bc3995a96159b3fc11f9e08321/aiohttp-3.13.3-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:7a4a94eb787e606d0a09404b9c38c113d3b099d508021faa615d70a0131907ce", size = 1767852 }, + { url = "https://files.pythonhosted.org/packages/9d/f2/a07a75173124f31f11ea6f863dc44e6f09afe2bca45dd4e64979490deab1/aiohttp-3.13.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:87797e645d9d8e222e04160ee32aa06bc5c163e8499f24db719e7852ec23093a", size = 1722379 }, + { url = "https://files.pythonhosted.org/packages/3c/4a/1a3fee7c21350cac78e5c5cef711bac1b94feca07399f3d406972e2d8fcd/aiohttp-3.13.3-cp312-cp312-win32.whl", hash = "sha256:b04be762396457bef43f3597c991e192ee7da460a4953d7e647ee4b1c28e7046", size = 428253 }, + { url = "https://files.pythonhosted.org/packages/d9/b7/76175c7cb4eb73d91ad63c34e29fc4f77c9386bba4a65b53ba8e05ee3c39/aiohttp-3.13.3-cp312-cp312-win_amd64.whl", hash = "sha256:e3531d63d3bdfa7e3ac5e9b27b2dd7ec9df3206a98e0b3445fa906f233264c57", size = 455407 }, + { url = "https://files.pythonhosted.org/packages/97/8a/12ca489246ca1faaf5432844adbfce7ff2cc4997733e0af120869345643a/aiohttp-3.13.3-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:5dff64413671b0d3e7d5918ea490bdccb97a4ad29b3f311ed423200b2203e01c", size = 734190 }, + { url = "https://files.pythonhosted.org/packages/32/08/de43984c74ed1fca5c014808963cc83cb00d7bb06af228f132d33862ca76/aiohttp-3.13.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:87b9aab6d6ed88235aa2970294f496ff1a1f9adcd724d800e9b952395a80ffd9", size = 491783 }, + { url = "https://files.pythonhosted.org/packages/17/f8/8dd2cf6112a5a76f81f81a5130c57ca829d101ad583ce57f889179accdda/aiohttp-3.13.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:425c126c0dc43861e22cb1c14ba4c8e45d09516d0a3ae0a3f7494b79f5f233a3", size = 490704 }, + { url = "https://files.pythonhosted.org/packages/6d/40/a46b03ca03936f832bc7eaa47cfbb1ad012ba1be4790122ee4f4f8cba074/aiohttp-3.13.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7f9120f7093c2a32d9647abcaf21e6ad275b4fbec5b55969f978b1a97c7c86bf", size = 1720652 }, + { url = "https://files.pythonhosted.org/packages/f7/7e/917fe18e3607af92657e4285498f500dca797ff8c918bd7d90b05abf6c2a/aiohttp-3.13.3-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:697753042d57f4bf7122cab985bf15d0cef23c770864580f5af4f52023a56bd6", size = 1692014 }, + { url = "https://files.pythonhosted.org/packages/71/b6/cefa4cbc00d315d68973b671cf105b21a609c12b82d52e5d0c9ae61d2a09/aiohttp-3.13.3-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:6de499a1a44e7de70735d0b39f67c8f25eb3d91eb3103be99ca0fa882cdd987d", size = 1759777 }, + { url = "https://files.pythonhosted.org/packages/fb/e3/e06ee07b45e59e6d81498b591fc589629be1553abb2a82ce33efe2a7b068/aiohttp-3.13.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:37239e9f9a7ea9ac5bf6b92b0260b01f8a22281996da609206a84df860bc1261", size = 1861276 }, + { url = "https://files.pythonhosted.org/packages/7c/24/75d274228acf35ceeb2850b8ce04de9dd7355ff7a0b49d607ee60c29c518/aiohttp-3.13.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f76c1e3fe7d7c8afad7ed193f89a292e1999608170dcc9751a7462a87dfd5bc0", size = 1743131 }, + { url = "https://files.pythonhosted.org/packages/04/98/3d21dde21889b17ca2eea54fdcff21b27b93f45b7bb94ca029c31ab59dc3/aiohttp-3.13.3-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:fc290605db2a917f6e81b0e1e0796469871f5af381ce15c604a3c5c7e51cb730", size = 1556863 }, + { url = "https://files.pythonhosted.org/packages/9e/84/da0c3ab1192eaf64782b03971ab4055b475d0db07b17eff925e8c93b3aa5/aiohttp-3.13.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4021b51936308aeea0367b8f006dc999ca02bc118a0cc78c303f50a2ff6afb91", size = 1682793 }, + { url = "https://files.pythonhosted.org/packages/ff/0f/5802ada182f575afa02cbd0ec5180d7e13a402afb7c2c03a9aa5e5d49060/aiohttp-3.13.3-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:49a03727c1bba9a97d3e93c9f93ca03a57300f484b6e935463099841261195d3", size = 1716676 }, + { url = "https://files.pythonhosted.org/packages/3f/8c/714d53bd8b5a4560667f7bbbb06b20c2382f9c7847d198370ec6526af39c/aiohttp-3.13.3-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:3d9908a48eb7416dc1f4524e69f1d32e5d90e3981e4e37eb0aa1cd18f9cfa2a4", size = 1733217 }, + { url = "https://files.pythonhosted.org/packages/7d/79/e2176f46d2e963facea939f5be2d26368ce543622be6f00a12844d3c991f/aiohttp-3.13.3-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:2712039939ec963c237286113c68dbad80a82a4281543f3abf766d9d73228998", size = 1552303 }, + { url = "https://files.pythonhosted.org/packages/ab/6a/28ed4dea1759916090587d1fe57087b03e6c784a642b85ef48217b0277ae/aiohttp-3.13.3-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:7bfdc049127717581866fa4708791220970ce291c23e28ccf3922c700740fdc0", size = 1763673 }, + { url = "https://files.pythonhosted.org/packages/e8/35/4a3daeb8b9fab49240d21c04d50732313295e4bd813a465d840236dd0ce1/aiohttp-3.13.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8057c98e0c8472d8846b9c79f56766bcc57e3e8ac7bfd510482332366c56c591", size = 1721120 }, + { url = "https://files.pythonhosted.org/packages/bc/9f/d643bb3c5fb99547323e635e251c609fbbc660d983144cfebec529e09264/aiohttp-3.13.3-cp313-cp313-win32.whl", hash = "sha256:1449ceddcdbcf2e0446957863af03ebaaa03f94c090f945411b61269e2cb5daf", size = 427383 }, + { url = "https://files.pythonhosted.org/packages/4e/f1/ab0395f8a79933577cdd996dd2f9aa6014af9535f65dddcf88204682fe62/aiohttp-3.13.3-cp313-cp313-win_amd64.whl", hash = "sha256:693781c45a4033d31d4187d2436f5ac701e7bbfe5df40d917736108c1cc7436e", size = 453899 }, + { url = "https://files.pythonhosted.org/packages/99/36/5b6514a9f5d66f4e2597e40dea2e3db271e023eb7a5d22defe96ba560996/aiohttp-3.13.3-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:ea37047c6b367fd4bd632bff8077449b8fa034b69e812a18e0132a00fae6e808", size = 737238 }, + { url = "https://files.pythonhosted.org/packages/f7/49/459327f0d5bcd8c6c9ca69e60fdeebc3622861e696490d8674a6d0cb90a6/aiohttp-3.13.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:6fc0e2337d1a4c3e6acafda6a78a39d4c14caea625124817420abceed36e2415", size = 492292 }, + { url = "https://files.pythonhosted.org/packages/e8/0b/b97660c5fd05d3495b4eb27f2d0ef18dc1dc4eff7511a9bf371397ff0264/aiohttp-3.13.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c685f2d80bb67ca8c3837823ad76196b3694b0159d232206d1e461d3d434666f", size = 493021 }, + { url = "https://files.pythonhosted.org/packages/54/d4/438efabdf74e30aeceb890c3290bbaa449780583b1270b00661126b8aae4/aiohttp-3.13.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:48e377758516d262bde50c2584fc6c578af272559c409eecbdd2bae1601184d6", size = 1717263 }, + { url = "https://files.pythonhosted.org/packages/71/f2/7bddc7fd612367d1459c5bcf598a9e8f7092d6580d98de0e057eb42697ad/aiohttp-3.13.3-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:34749271508078b261c4abb1767d42b8d0c0cc9449c73a4df494777dc55f0687", size = 1669107 }, + { url = "https://files.pythonhosted.org/packages/00/5a/1aeaecca40e22560f97610a329e0e5efef5e0b5afdf9f857f0d93839ab2e/aiohttp-3.13.3-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:82611aeec80eb144416956ec85b6ca45a64d76429c1ed46ae1b5f86c6e0c9a26", size = 1760196 }, + { url = "https://files.pythonhosted.org/packages/f8/f8/0ff6992bea7bd560fc510ea1c815f87eedd745fe035589c71ce05612a19a/aiohttp-3.13.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:2fff83cfc93f18f215896e3a190e8e5cb413ce01553901aca925176e7568963a", size = 1843591 }, + { url = "https://files.pythonhosted.org/packages/e3/d1/e30e537a15f53485b61f5be525f2157da719819e8377298502aebac45536/aiohttp-3.13.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bbe7d4cecacb439e2e2a8a1a7b935c25b812af7a5fd26503a66dadf428e79ec1", size = 1720277 }, + { url = "https://files.pythonhosted.org/packages/84/45/23f4c451d8192f553d38d838831ebbc156907ea6e05557f39563101b7717/aiohttp-3.13.3-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:b928f30fe49574253644b1ca44b1b8adbd903aa0da4b9054a6c20fc7f4092a25", size = 1548575 }, + { url = "https://files.pythonhosted.org/packages/6a/ed/0a42b127a43712eda7807e7892c083eadfaf8429ca8fb619662a530a3aab/aiohttp-3.13.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7b5e8fe4de30df199155baaf64f2fcd604f4c678ed20910db8e2c66dc4b11603", size = 1679455 }, + { url = "https://files.pythonhosted.org/packages/2e/b5/c05f0c2b4b4fe2c9d55e73b6d3ed4fd6c9dc2684b1d81cbdf77e7fad9adb/aiohttp-3.13.3-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:8542f41a62bcc58fc7f11cf7c90e0ec324ce44950003feb70640fc2a9092c32a", size = 1687417 }, + { url = "https://files.pythonhosted.org/packages/c9/6b/915bc5dad66aef602b9e459b5a973529304d4e89ca86999d9d75d80cbd0b/aiohttp-3.13.3-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:5e1d8c8b8f1d91cd08d8f4a3c2b067bfca6ec043d3ff36de0f3a715feeedf926", size = 1729968 }, + { url = "https://files.pythonhosted.org/packages/11/3b/e84581290a9520024a08640b63d07673057aec5ca548177a82026187ba73/aiohttp-3.13.3-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:90455115e5da1c3c51ab619ac57f877da8fd6d73c05aacd125c5ae9819582aba", size = 1545690 }, + { url = "https://files.pythonhosted.org/packages/f5/04/0c3655a566c43fd647c81b895dfe361b9f9ad6d58c19309d45cff52d6c3b/aiohttp-3.13.3-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:042e9e0bcb5fba81886c8b4fbb9a09d6b8a00245fd8d88e4d989c1f96c74164c", size = 1746390 }, + { url = "https://files.pythonhosted.org/packages/1f/53/71165b26978f719c3419381514c9690bd5980e764a09440a10bb816ea4ab/aiohttp-3.13.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:2eb752b102b12a76ca02dff751a801f028b4ffbbc478840b473597fc91a9ed43", size = 1702188 }, + { url = "https://files.pythonhosted.org/packages/29/a7/cbe6c9e8e136314fa1980da388a59d2f35f35395948a08b6747baebb6aa6/aiohttp-3.13.3-cp314-cp314-win32.whl", hash = "sha256:b556c85915d8efaed322bf1bdae9486aa0f3f764195a0fb6ee962e5c71ef5ce1", size = 433126 }, + { url = "https://files.pythonhosted.org/packages/de/56/982704adea7d3b16614fc5936014e9af85c0e34b58f9046655817f04306e/aiohttp-3.13.3-cp314-cp314-win_amd64.whl", hash = "sha256:9bf9f7a65e7aa20dd764151fb3d616c81088f91f8df39c3893a536e279b4b984", size = 459128 }, + { url = "https://files.pythonhosted.org/packages/6c/2a/3c79b638a9c3d4658d345339d22070241ea341ed4e07b5ac60fb0f418003/aiohttp-3.13.3-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:05861afbbec40650d8a07ea324367cb93e9e8cc7762e04dd4405df99fa65159c", size = 769512 }, + { url = "https://files.pythonhosted.org/packages/29/b9/3e5014d46c0ab0db8707e0ac2711ed28c4da0218c358a4e7c17bae0d8722/aiohttp-3.13.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:2fc82186fadc4a8316768d61f3722c230e2c1dcab4200d52d2ebdf2482e47592", size = 506444 }, + { url = "https://files.pythonhosted.org/packages/90/03/c1d4ef9a054e151cd7839cdc497f2638f00b93cbe8043983986630d7a80c/aiohttp-3.13.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:0add0900ff220d1d5c5ebbf99ed88b0c1bbf87aa7e4262300ed1376a6b13414f", size = 510798 }, + { url = "https://files.pythonhosted.org/packages/ea/76/8c1e5abbfe8e127c893fe7ead569148a4d5a799f7cf958d8c09f3eedf097/aiohttp-3.13.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:568f416a4072fbfae453dcf9a99194bbb8bdeab718e08ee13dfa2ba0e4bebf29", size = 1868835 }, + { url = "https://files.pythonhosted.org/packages/8e/ac/984c5a6f74c363b01ff97adc96a3976d9c98940b8969a1881575b279ac5d/aiohttp-3.13.3-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:add1da70de90a2569c5e15249ff76a631ccacfe198375eead4aadf3b8dc849dc", size = 1720486 }, + { url = "https://files.pythonhosted.org/packages/b2/9a/b7039c5f099c4eb632138728828b33428585031a1e658d693d41d07d89d1/aiohttp-3.13.3-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:10b47b7ba335d2e9b1239fa571131a87e2d8ec96b333e68b2a305e7a98b0bae2", size = 1847951 }, + { url = "https://files.pythonhosted.org/packages/3c/02/3bec2b9a1ba3c19ff89a43a19324202b8eb187ca1e928d8bdac9bbdddebd/aiohttp-3.13.3-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:3dd4dce1c718e38081c8f35f323209d4c1df7d4db4bab1b5c88a6b4d12b74587", size = 1941001 }, + { url = "https://files.pythonhosted.org/packages/37/df/d879401cedeef27ac4717f6426c8c36c3091c6e9f08a9178cc87549c537f/aiohttp-3.13.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:34bac00a67a812570d4a460447e1e9e06fae622946955f939051e7cc895cfab8", size = 1797246 }, + { url = "https://files.pythonhosted.org/packages/8d/15/be122de1f67e6953add23335c8ece6d314ab67c8bebb3f181063010795a7/aiohttp-3.13.3-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:a19884d2ee70b06d9204b2727a7b9f983d0c684c650254679e716b0b77920632", size = 1627131 }, + { url = "https://files.pythonhosted.org/packages/12/12/70eedcac9134cfa3219ab7af31ea56bc877395b1ac30d65b1bc4b27d0438/aiohttp-3.13.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:5f8ca7f2bb6ba8348a3614c7918cc4bb73268c5ac2a207576b7afea19d3d9f64", size = 1795196 }, + { url = "https://files.pythonhosted.org/packages/32/11/b30e1b1cd1f3054af86ebe60df96989c6a414dd87e27ad16950eee420bea/aiohttp-3.13.3-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:b0d95340658b9d2f11d9697f59b3814a9d3bb4b7a7c20b131df4bcef464037c0", size = 1782841 }, + { url = "https://files.pythonhosted.org/packages/88/0d/d98a9367b38912384a17e287850f5695c528cff0f14f791ce8ee2e4f7796/aiohttp-3.13.3-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:a1e53262fd202e4b40b70c3aff944a8155059beedc8a89bba9dc1f9ef06a1b56", size = 1795193 }, + { url = "https://files.pythonhosted.org/packages/43/a5/a2dfd1f5ff5581632c7f6a30e1744deda03808974f94f6534241ef60c751/aiohttp-3.13.3-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:d60ac9663f44168038586cab2157e122e46bdef09e9368b37f2d82d354c23f72", size = 1621979 }, + { url = "https://files.pythonhosted.org/packages/fa/f0/12973c382ae7c1cccbc4417e129c5bf54c374dfb85af70893646e1f0e749/aiohttp-3.13.3-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:90751b8eed69435bac9ff4e3d2f6b3af1f57e37ecb0fbeee59c0174c9e2d41df", size = 1822193 }, + { url = "https://files.pythonhosted.org/packages/3c/5f/24155e30ba7f8c96918af1350eb0663e2430aad9e001c0489d89cd708ab1/aiohttp-3.13.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:fc353029f176fd2b3ec6cfc71be166aba1936fe5d73dd1992ce289ca6647a9aa", size = 1769801 }, + { url = "https://files.pythonhosted.org/packages/eb/f8/7314031ff5c10e6ece114da79b338ec17eeff3a079e53151f7e9f43c4723/aiohttp-3.13.3-cp314-cp314t-win32.whl", hash = "sha256:2e41b18a58da1e474a057b3d35248d8320029f61d70a37629535b16a0c8f3767", size = 466523 }, + { url = "https://files.pythonhosted.org/packages/b4/63/278a98c715ae467624eafe375542d8ba9b4383a016df8fdefe0ae28382a7/aiohttp-3.13.3-cp314-cp314t-win_amd64.whl", hash = "sha256:44531a36aa2264a1860089ffd4dce7baf875ee5a6079d5fb42e261c704ef7344", size = 499694 }, +] + +[[package]] +name = "aiosignal" +version = "1.4.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "frozenlist" }, + { name = "typing-extensions", marker = "python_full_version < '3.13'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/61/62/06741b579156360248d1ec624842ad0edf697050bbaf7c3e46394e106ad1/aiosignal-1.4.0.tar.gz", hash = "sha256:f47eecd9468083c2029cc99945502cb7708b082c232f9aca65da147157b251c7", size = 25007 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fb/76/641ae371508676492379f16e2fa48f4e2c11741bd63c48be4b12a6b09cba/aiosignal-1.4.0-py3-none-any.whl", hash = "sha256:053243f8b92b990551949e63930a839ff0cf0b0ebbe0597b0f3fb19e1a0fe82e", size = 7490 }, +] + +[[package]] +name = "annotated-types" +version = "0.7.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ee/67/531ea369ba64dcff5ec9c3402f9f51bf748cec26dde048a2f973a4eea7f5/annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89", size = 16081 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53", size = 13643 }, +] + +[[package]] +name = "anthropic" +version = "0.76.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio" }, + { name = "distro" }, + { name = "docstring-parser" }, + { name = "httpx" }, + { name = "jiter" }, + { name = "pydantic" }, + { name = "sniffio" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/6e/be/d11abafaa15d6304826438170f7574d750218f49a106c54424a40cef4494/anthropic-0.76.0.tar.gz", hash = "sha256:e0cae6a368986d5cf6df743dfbb1b9519e6a9eee9c6c942ad8121c0b34416ffe", size = 495483 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e5/70/7b0fd9c1a738f59d3babe2b4212031c34ab7d0fda4ffef15b58a55c5bcea/anthropic-0.76.0-py3-none-any.whl", hash = "sha256:81efa3113901192af2f0fe977d3ec73fdadb1e691586306c4256cd6d5ccc331c", size = 390309 }, +] + +[[package]] +name = "anyio" +version = "4.12.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "idna" }, + { name = "typing-extensions", marker = "python_full_version < '3.13'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/96/f0/5eb65b2bb0d09ac6776f2eb54adee6abe8228ea05b20a5ad0e4945de8aac/anyio-4.12.1.tar.gz", hash = "sha256:41cfcc3a4c85d3f05c932da7c26d0201ac36f72abd4435ba90d0464a3ffed703", size = 228685 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/38/0e/27be9fdef66e72d64c0cdc3cc2823101b80585f8119b5c112c2e8f5f7dab/anyio-4.12.1-py3-none-any.whl", hash = "sha256:d405828884fc140aa80a3c667b8beed277f1dfedec42ba031bd6ac3db606ab6c", size = 113592 }, +] + +[[package]] +name = "attrs" +version = "25.4.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/6b/5c/685e6633917e101e5dcb62b9dd76946cbb57c26e133bae9e0cd36033c0a9/attrs-25.4.0.tar.gz", hash = "sha256:16d5969b87f0859ef33a48b35d55ac1be6e42ae49d5e853b597db70c35c57e11", size = 934251 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3a/2a/7cc015f5b9f5db42b7d48157e23356022889fc354a2813c15934b7cb5c0e/attrs-25.4.0-py3-none-any.whl", hash = "sha256:adcf7e2a1fb3b36ac48d97835bb6d8ade15b8dcce26aba8bf1d14847b57a3373", size = 67615 }, +] + +[[package]] +name = "certifi" +version = "2026.1.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e0/2d/a891ca51311197f6ad14a7ef42e2399f36cf2f9bd44752b3dc4eab60fdc5/certifi-2026.1.4.tar.gz", hash = "sha256:ac726dd470482006e014ad384921ed6438c457018f4b3d204aea4281258b2120", size = 154268 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e6/ad/3cc14f097111b4de0040c83a525973216457bbeeb63739ef1ed275c1c021/certifi-2026.1.4-py3-none-any.whl", hash = "sha256:9943707519e4add1115f44c2bc244f782c0249876bf51b6599fee1ffbedd685c", size = 152900 }, +] + +[[package]] +name = "charset-normalizer" +version = "3.4.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/13/69/33ddede1939fdd074bce5434295f38fae7136463422fe4fd3e0e89b98062/charset_normalizer-3.4.4.tar.gz", hash = "sha256:94537985111c35f28720e43603b8e7b43a6ecfb2ce1d3058bbe955b73404e21a", size = 129418 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ed/27/c6491ff4954e58a10f69ad90aca8a1b6fe9c5d3c6f380907af3c37435b59/charset_normalizer-3.4.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:6e1fcf0720908f200cd21aa4e6750a48ff6ce4afe7ff5a79a90d5ed8a08296f8", size = 206988 }, + { url = "https://files.pythonhosted.org/packages/94/59/2e87300fe67ab820b5428580a53cad894272dbb97f38a7a814a2a1ac1011/charset_normalizer-3.4.4-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5f819d5fe9234f9f82d75bdfa9aef3a3d72c4d24a6e57aeaebba32a704553aa0", size = 147324 }, + { url = "https://files.pythonhosted.org/packages/07/fb/0cf61dc84b2b088391830f6274cb57c82e4da8bbc2efeac8c025edb88772/charset_normalizer-3.4.4-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:a59cb51917aa591b1c4e6a43c132f0cdc3c76dbad6155df4e28ee626cc77a0a3", size = 142742 }, + { url = "https://files.pythonhosted.org/packages/62/8b/171935adf2312cd745d290ed93cf16cf0dfe320863ab7cbeeae1dcd6535f/charset_normalizer-3.4.4-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:8ef3c867360f88ac904fd3f5e1f902f13307af9052646963ee08ff4f131adafc", size = 160863 }, + { url = "https://files.pythonhosted.org/packages/09/73/ad875b192bda14f2173bfc1bc9a55e009808484a4b256748d931b6948442/charset_normalizer-3.4.4-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d9e45d7faa48ee908174d8fe84854479ef838fc6a705c9315372eacbc2f02897", size = 157837 }, + { url = "https://files.pythonhosted.org/packages/6d/fc/de9cce525b2c5b94b47c70a4b4fb19f871b24995c728e957ee68ab1671ea/charset_normalizer-3.4.4-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:840c25fb618a231545cbab0564a799f101b63b9901f2569faecd6b222ac72381", size = 151550 }, + { url = "https://files.pythonhosted.org/packages/55/c2/43edd615fdfba8c6f2dfbd459b25a6b3b551f24ea21981e23fb768503ce1/charset_normalizer-3.4.4-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:ca5862d5b3928c4940729dacc329aa9102900382fea192fc5e52eb69d6093815", size = 149162 }, + { url = "https://files.pythonhosted.org/packages/03/86/bde4ad8b4d0e9429a4e82c1e8f5c659993a9a863ad62c7df05cf7b678d75/charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d9c7f57c3d666a53421049053eaacdd14bbd0a528e2186fcb2e672effd053bb0", size = 150019 }, + { url = "https://files.pythonhosted.org/packages/1f/86/a151eb2af293a7e7bac3a739b81072585ce36ccfb4493039f49f1d3cae8c/charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:277e970e750505ed74c832b4bf75dac7476262ee2a013f5574dd49075879e161", size = 143310 }, + { url = "https://files.pythonhosted.org/packages/b5/fe/43dae6144a7e07b87478fdfc4dbe9efd5defb0e7ec29f5f58a55aeef7bf7/charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:31fd66405eaf47bb62e8cd575dc621c56c668f27d46a61d975a249930dd5e2a4", size = 162022 }, + { url = "https://files.pythonhosted.org/packages/80/e6/7aab83774f5d2bca81f42ac58d04caf44f0cc2b65fc6db2b3b2e8a05f3b3/charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:0d3d8f15c07f86e9ff82319b3d9ef6f4bf907608f53fe9d92b28ea9ae3d1fd89", size = 149383 }, + { url = "https://files.pythonhosted.org/packages/4f/e8/b289173b4edae05c0dde07f69f8db476a0b511eac556dfe0d6bda3c43384/charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:9f7fcd74d410a36883701fafa2482a6af2ff5ba96b9a620e9e0721e28ead5569", size = 159098 }, + { url = "https://files.pythonhosted.org/packages/d8/df/fe699727754cae3f8478493c7f45f777b17c3ef0600e28abfec8619eb49c/charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ebf3e58c7ec8a8bed6d66a75d7fb37b55e5015b03ceae72a8e7c74495551e224", size = 152991 }, + { url = "https://files.pythonhosted.org/packages/1a/86/584869fe4ddb6ffa3bd9f491b87a01568797fb9bd8933f557dba9771beaf/charset_normalizer-3.4.4-cp311-cp311-win32.whl", hash = "sha256:eecbc200c7fd5ddb9a7f16c7decb07b566c29fa2161a16cf67b8d068bd21690a", size = 99456 }, + { url = "https://files.pythonhosted.org/packages/65/f6/62fdd5feb60530f50f7e38b4f6a1d5203f4d16ff4f9f0952962c044e919a/charset_normalizer-3.4.4-cp311-cp311-win_amd64.whl", hash = "sha256:5ae497466c7901d54b639cf42d5b8c1b6a4fead55215500d2f486d34db48d016", size = 106978 }, + { url = "https://files.pythonhosted.org/packages/7a/9d/0710916e6c82948b3be62d9d398cb4fcf4e97b56d6a6aeccd66c4b2f2bd5/charset_normalizer-3.4.4-cp311-cp311-win_arm64.whl", hash = "sha256:65e2befcd84bc6f37095f5961e68a6f077bf44946771354a28ad434c2cce0ae1", size = 99969 }, + { url = "https://files.pythonhosted.org/packages/f3/85/1637cd4af66fa687396e757dec650f28025f2a2f5a5531a3208dc0ec43f2/charset_normalizer-3.4.4-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:0a98e6759f854bd25a58a73fa88833fba3b7c491169f86ce1180c948ab3fd394", size = 208425 }, + { url = "https://files.pythonhosted.org/packages/9d/6a/04130023fef2a0d9c62d0bae2649b69f7b7d8d24ea5536feef50551029df/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b5b290ccc2a263e8d185130284f8501e3e36c5e02750fc6b6bdeb2e9e96f1e25", size = 148162 }, + { url = "https://files.pythonhosted.org/packages/78/29/62328d79aa60da22c9e0b9a66539feae06ca0f5a4171ac4f7dc285b83688/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:74bb723680f9f7a6234dcf67aea57e708ec1fbdf5699fb91dfd6f511b0a320ef", size = 144558 }, + { url = "https://files.pythonhosted.org/packages/86/bb/b32194a4bf15b88403537c2e120b817c61cd4ecffa9b6876e941c3ee38fe/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f1e34719c6ed0b92f418c7c780480b26b5d9c50349e9a9af7d76bf757530350d", size = 161497 }, + { url = "https://files.pythonhosted.org/packages/19/89/a54c82b253d5b9b111dc74aca196ba5ccfcca8242d0fb64146d4d3183ff1/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:2437418e20515acec67d86e12bf70056a33abdacb5cb1655042f6538d6b085a8", size = 159240 }, + { url = "https://files.pythonhosted.org/packages/c0/10/d20b513afe03acc89ec33948320a5544d31f21b05368436d580dec4e234d/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:11d694519d7f29d6cd09f6ac70028dba10f92f6cdd059096db198c283794ac86", size = 153471 }, + { url = "https://files.pythonhosted.org/packages/61/fa/fbf177b55bdd727010f9c0a3c49eefa1d10f960e5f09d1d887bf93c2e698/charset_normalizer-3.4.4-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:ac1c4a689edcc530fc9d9aa11f5774b9e2f33f9a0c6a57864e90908f5208d30a", size = 150864 }, + { url = "https://files.pythonhosted.org/packages/05/12/9fbc6a4d39c0198adeebbde20b619790e9236557ca59fc40e0e3cebe6f40/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:21d142cc6c0ec30d2efee5068ca36c128a30b0f2c53c1c07bd78cb6bc1d3be5f", size = 150647 }, + { url = "https://files.pythonhosted.org/packages/ad/1f/6a9a593d52e3e8c5d2b167daf8c6b968808efb57ef4c210acb907c365bc4/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:5dbe56a36425d26d6cfb40ce79c314a2e4dd6211d51d6d2191c00bed34f354cc", size = 145110 }, + { url = "https://files.pythonhosted.org/packages/30/42/9a52c609e72471b0fc54386dc63c3781a387bb4fe61c20231a4ebcd58bdd/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:5bfbb1b9acf3334612667b61bd3002196fe2a1eb4dd74d247e0f2a4d50ec9bbf", size = 162839 }, + { url = "https://files.pythonhosted.org/packages/c4/5b/c0682bbf9f11597073052628ddd38344a3d673fda35a36773f7d19344b23/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:d055ec1e26e441f6187acf818b73564e6e6282709e9bcb5b63f5b23068356a15", size = 150667 }, + { url = "https://files.pythonhosted.org/packages/e4/24/a41afeab6f990cf2daf6cb8c67419b63b48cf518e4f56022230840c9bfb2/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:af2d8c67d8e573d6de5bc30cdb27e9b95e49115cd9baad5ddbd1a6207aaa82a9", size = 160535 }, + { url = "https://files.pythonhosted.org/packages/2a/e5/6a4ce77ed243c4a50a1fecca6aaaab419628c818a49434be428fe24c9957/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:780236ac706e66881f3b7f2f32dfe90507a09e67d1d454c762cf642e6e1586e0", size = 154816 }, + { url = "https://files.pythonhosted.org/packages/a8/ef/89297262b8092b312d29cdb2517cb1237e51db8ecef2e9af5edbe7b683b1/charset_normalizer-3.4.4-cp312-cp312-win32.whl", hash = "sha256:5833d2c39d8896e4e19b689ffc198f08ea58116bee26dea51e362ecc7cd3ed26", size = 99694 }, + { url = "https://files.pythonhosted.org/packages/3d/2d/1e5ed9dd3b3803994c155cd9aacb60c82c331bad84daf75bcb9c91b3295e/charset_normalizer-3.4.4-cp312-cp312-win_amd64.whl", hash = "sha256:a79cfe37875f822425b89a82333404539ae63dbdddf97f84dcbc3d339aae9525", size = 107131 }, + { url = "https://files.pythonhosted.org/packages/d0/d9/0ed4c7098a861482a7b6a95603edce4c0d9db2311af23da1fb2b75ec26fc/charset_normalizer-3.4.4-cp312-cp312-win_arm64.whl", hash = "sha256:376bec83a63b8021bb5c8ea75e21c4ccb86e7e45ca4eb81146091b56599b80c3", size = 100390 }, + { url = "https://files.pythonhosted.org/packages/97/45/4b3a1239bbacd321068ea6e7ac28875b03ab8bc0aa0966452db17cd36714/charset_normalizer-3.4.4-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:e1f185f86a6f3403aa2420e815904c67b2f9ebc443f045edd0de921108345794", size = 208091 }, + { url = "https://files.pythonhosted.org/packages/7d/62/73a6d7450829655a35bb88a88fca7d736f9882a27eacdca2c6d505b57e2e/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6b39f987ae8ccdf0d2642338faf2abb1862340facc796048b604ef14919e55ed", size = 147936 }, + { url = "https://files.pythonhosted.org/packages/89/c5/adb8c8b3d6625bef6d88b251bbb0d95f8205831b987631ab0c8bb5d937c2/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3162d5d8ce1bb98dd51af660f2121c55d0fa541b46dff7bb9b9f86ea1d87de72", size = 144180 }, + { url = "https://files.pythonhosted.org/packages/91/ed/9706e4070682d1cc219050b6048bfd293ccf67b3d4f5a4f39207453d4b99/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:81d5eb2a312700f4ecaa977a8235b634ce853200e828fbadf3a9c50bab278328", size = 161346 }, + { url = "https://files.pythonhosted.org/packages/d5/0d/031f0d95e4972901a2f6f09ef055751805ff541511dc1252ba3ca1f80cf5/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5bd2293095d766545ec1a8f612559f6b40abc0eb18bb2f5d1171872d34036ede", size = 158874 }, + { url = "https://files.pythonhosted.org/packages/f5/83/6ab5883f57c9c801ce5e5677242328aa45592be8a00644310a008d04f922/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a8a8b89589086a25749f471e6a900d3f662d1d3b6e2e59dcecf787b1cc3a1894", size = 153076 }, + { url = "https://files.pythonhosted.org/packages/75/1e/5ff781ddf5260e387d6419959ee89ef13878229732732ee73cdae01800f2/charset_normalizer-3.4.4-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:bc7637e2f80d8530ee4a78e878bce464f70087ce73cf7c1caf142416923b98f1", size = 150601 }, + { url = "https://files.pythonhosted.org/packages/d7/57/71be810965493d3510a6ca79b90c19e48696fb1ff964da319334b12677f0/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f8bf04158c6b607d747e93949aa60618b61312fe647a6369f88ce2ff16043490", size = 150376 }, + { url = "https://files.pythonhosted.org/packages/e5/d5/c3d057a78c181d007014feb7e9f2e65905a6c4ef182c0ddf0de2924edd65/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:554af85e960429cf30784dd47447d5125aaa3b99a6f0683589dbd27e2f45da44", size = 144825 }, + { url = "https://files.pythonhosted.org/packages/e6/8c/d0406294828d4976f275ffbe66f00266c4b3136b7506941d87c00cab5272/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:74018750915ee7ad843a774364e13a3db91682f26142baddf775342c3f5b1133", size = 162583 }, + { url = "https://files.pythonhosted.org/packages/d7/24/e2aa1f18c8f15c4c0e932d9287b8609dd30ad56dbe41d926bd846e22fb8d/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:c0463276121fdee9c49b98908b3a89c39be45d86d1dbaa22957e38f6321d4ce3", size = 150366 }, + { url = "https://files.pythonhosted.org/packages/e4/5b/1e6160c7739aad1e2df054300cc618b06bf784a7a164b0f238360721ab86/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:362d61fd13843997c1c446760ef36f240cf81d3ebf74ac62652aebaf7838561e", size = 160300 }, + { url = "https://files.pythonhosted.org/packages/7a/10/f882167cd207fbdd743e55534d5d9620e095089d176d55cb22d5322f2afd/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9a26f18905b8dd5d685d6d07b0cdf98a79f3c7a918906af7cc143ea2e164c8bc", size = 154465 }, + { url = "https://files.pythonhosted.org/packages/89/66/c7a9e1b7429be72123441bfdbaf2bc13faab3f90b933f664db506dea5915/charset_normalizer-3.4.4-cp313-cp313-win32.whl", hash = "sha256:9b35f4c90079ff2e2edc5b26c0c77925e5d2d255c42c74fdb70fb49b172726ac", size = 99404 }, + { url = "https://files.pythonhosted.org/packages/c4/26/b9924fa27db384bdcd97ab83b4f0a8058d96ad9626ead570674d5e737d90/charset_normalizer-3.4.4-cp313-cp313-win_amd64.whl", hash = "sha256:b435cba5f4f750aa6c0a0d92c541fb79f69a387c91e61f1795227e4ed9cece14", size = 107092 }, + { url = "https://files.pythonhosted.org/packages/af/8f/3ed4bfa0c0c72a7ca17f0380cd9e4dd842b09f664e780c13cff1dcf2ef1b/charset_normalizer-3.4.4-cp313-cp313-win_arm64.whl", hash = "sha256:542d2cee80be6f80247095cc36c418f7bddd14f4a6de45af91dfad36d817bba2", size = 100408 }, + { url = "https://files.pythonhosted.org/packages/2a/35/7051599bd493e62411d6ede36fd5af83a38f37c4767b92884df7301db25d/charset_normalizer-3.4.4-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:da3326d9e65ef63a817ecbcc0df6e94463713b754fe293eaa03da99befb9a5bd", size = 207746 }, + { url = "https://files.pythonhosted.org/packages/10/9a/97c8d48ef10d6cd4fcead2415523221624bf58bcf68a802721a6bc807c8f/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8af65f14dc14a79b924524b1e7fffe304517b2bff5a58bf64f30b98bbc5079eb", size = 147889 }, + { url = "https://files.pythonhosted.org/packages/10/bf/979224a919a1b606c82bd2c5fa49b5c6d5727aa47b4312bb27b1734f53cd/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:74664978bb272435107de04e36db5a9735e78232b85b77d45cfb38f758efd33e", size = 143641 }, + { url = "https://files.pythonhosted.org/packages/ba/33/0ad65587441fc730dc7bd90e9716b30b4702dc7b617e6ba4997dc8651495/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:752944c7ffbfdd10c074dc58ec2d5a8a4cd9493b314d367c14d24c17684ddd14", size = 160779 }, + { url = "https://files.pythonhosted.org/packages/67/ed/331d6b249259ee71ddea93f6f2f0a56cfebd46938bde6fcc6f7b9a3d0e09/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d1f13550535ad8cff21b8d757a3257963e951d96e20ec82ab44bc64aeb62a191", size = 159035 }, + { url = "https://files.pythonhosted.org/packages/67/ff/f6b948ca32e4f2a4576aa129d8bed61f2e0543bf9f5f2b7fc3758ed005c9/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ecaae4149d99b1c9e7b88bb03e3221956f68fd6d50be2ef061b2381b61d20838", size = 152542 }, + { url = "https://files.pythonhosted.org/packages/16/85/276033dcbcc369eb176594de22728541a925b2632f9716428c851b149e83/charset_normalizer-3.4.4-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:cb6254dc36b47a990e59e1068afacdcd02958bdcce30bb50cc1700a8b9d624a6", size = 149524 }, + { url = "https://files.pythonhosted.org/packages/9e/f2/6a2a1f722b6aba37050e626530a46a68f74e63683947a8acff92569f979a/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:c8ae8a0f02f57a6e61203a31428fa1d677cbe50c93622b4149d5c0f319c1d19e", size = 150395 }, + { url = "https://files.pythonhosted.org/packages/60/bb/2186cb2f2bbaea6338cad15ce23a67f9b0672929744381e28b0592676824/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:47cc91b2f4dd2833fddaedd2893006b0106129d4b94fdb6af1f4ce5a9965577c", size = 143680 }, + { url = "https://files.pythonhosted.org/packages/7d/a5/bf6f13b772fbb2a90360eb620d52ed8f796f3c5caee8398c3b2eb7b1c60d/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:82004af6c302b5d3ab2cfc4cc5f29db16123b1a8417f2e25f9066f91d4411090", size = 162045 }, + { url = "https://files.pythonhosted.org/packages/df/c5/d1be898bf0dc3ef9030c3825e5d3b83f2c528d207d246cbabe245966808d/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:2b7d8f6c26245217bd2ad053761201e9f9680f8ce52f0fcd8d0755aeae5b2152", size = 149687 }, + { url = "https://files.pythonhosted.org/packages/a5/42/90c1f7b9341eef50c8a1cb3f098ac43b0508413f33affd762855f67a410e/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:799a7a5e4fb2d5898c60b640fd4981d6a25f1c11790935a44ce38c54e985f828", size = 160014 }, + { url = "https://files.pythonhosted.org/packages/76/be/4d3ee471e8145d12795ab655ece37baed0929462a86e72372fd25859047c/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:99ae2cffebb06e6c22bdc25801d7b30f503cc87dbd283479e7b606f70aff57ec", size = 154044 }, + { url = "https://files.pythonhosted.org/packages/b0/6f/8f7af07237c34a1defe7defc565a9bc1807762f672c0fde711a4b22bf9c0/charset_normalizer-3.4.4-cp314-cp314-win32.whl", hash = "sha256:f9d332f8c2a2fcbffe1378594431458ddbef721c1769d78e2cbc06280d8155f9", size = 99940 }, + { url = "https://files.pythonhosted.org/packages/4b/51/8ade005e5ca5b0d80fb4aff72a3775b325bdc3d27408c8113811a7cbe640/charset_normalizer-3.4.4-cp314-cp314-win_amd64.whl", hash = "sha256:8a6562c3700cce886c5be75ade4a5db4214fda19fede41d9792d100288d8f94c", size = 107104 }, + { url = "https://files.pythonhosted.org/packages/da/5f/6b8f83a55bb8278772c5ae54a577f3099025f9ade59d0136ac24a0df4bde/charset_normalizer-3.4.4-cp314-cp314-win_arm64.whl", hash = "sha256:de00632ca48df9daf77a2c65a484531649261ec9f25489917f09e455cb09ddb2", size = 100743 }, + { url = "https://files.pythonhosted.org/packages/0a/4c/925909008ed5a988ccbb72dcc897407e5d6d3bd72410d69e051fc0c14647/charset_normalizer-3.4.4-py3-none-any.whl", hash = "sha256:7a32c560861a02ff789ad905a2fe94e3f840803362c84fecf1851cb4cf3dc37f", size = 53402 }, +] + +[[package]] +name = "click" +version = "8.3.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/3d/fa/656b739db8587d7b5dfa22e22ed02566950fbfbcdc20311993483657a5c0/click-8.3.1.tar.gz", hash = "sha256:12ff4785d337a1bb490bb7e9c2b1ee5da3112e94a8622f26a6c77f5d2fc6842a", size = 295065 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/98/78/01c019cdb5d6498122777c1a43056ebb3ebfeef2076d9d026bfe15583b2b/click-8.3.1-py3-none-any.whl", hash = "sha256:981153a64e25f12d547d3426c367a4857371575ee7ad18df2a6183ab0545b2a6", size = 108274 }, +] + +[[package]] +name = "colorama" +version = "0.4.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335 }, +] + +[[package]] +name = "distro" +version = "1.9.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/fc/f8/98eea607f65de6527f8a2e8885fc8015d3e6f5775df186e443e0964a11c3/distro-1.9.0.tar.gz", hash = "sha256:2fa77c6fd8940f116ee1d6b94a2f90b13b5ea8d019b98bc8bafdcabcdd9bdbed", size = 60722 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/12/b3/231ffd4ab1fc9d679809f356cebee130ac7daa00d6d6f3206dd4fd137e9e/distro-1.9.0-py3-none-any.whl", hash = "sha256:7bffd925d65168f85027d8da9af6bddab658135b840670a223589bc0c8ef02b2", size = 20277 }, +] + +[[package]] +name = "docstring-parser" +version = "0.17.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b2/9d/c3b43da9515bd270df0f80548d9944e389870713cc1fe2b8fb35fe2bcefd/docstring_parser-0.17.0.tar.gz", hash = "sha256:583de4a309722b3315439bb31d64ba3eebada841f2e2cee23b99df001434c912", size = 27442 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/55/e2/2537ebcff11c1ee1ff17d8d0b6f4db75873e3b0fb32c2d4a2ee31ecb310a/docstring_parser-0.17.0-py3-none-any.whl", hash = "sha256:cf2569abd23dce8099b300f9b4fa8191e9582dda731fd533daf54c4551658708", size = 36896 }, +] + +[[package]] +name = "fastuuid" +version = "0.14.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/c3/7d/d9daedf0f2ebcacd20d599928f8913e9d2aea1d56d2d355a93bfa2b611d7/fastuuid-0.14.0.tar.gz", hash = "sha256:178947fc2f995b38497a74172adee64fdeb8b7ec18f2a5934d037641ba265d26", size = 18232 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/98/f3/12481bda4e5b6d3e698fbf525df4443cc7dce746f246b86b6fcb2fba1844/fastuuid-0.14.0-cp311-cp311-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:73946cb950c8caf65127d4e9a325e2b6be0442a224fd51ba3b6ac44e1912ce34", size = 516386 }, + { url = "https://files.pythonhosted.org/packages/59/19/2fc58a1446e4d72b655648eb0879b04e88ed6fa70d474efcf550f640f6ec/fastuuid-0.14.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:12ac85024637586a5b69645e7ed986f7535106ed3013640a393a03e461740cb7", size = 264569 }, + { url = "https://files.pythonhosted.org/packages/78/29/3c74756e5b02c40cfcc8b1d8b5bac4edbd532b55917a6bcc9113550e99d1/fastuuid-0.14.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:05a8dde1f395e0c9b4be515b7a521403d1e8349443e7641761af07c7ad1624b1", size = 254366 }, + { url = "https://files.pythonhosted.org/packages/52/96/d761da3fccfa84f0f353ce6e3eb8b7f76b3aa21fd25e1b00a19f9c80a063/fastuuid-0.14.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:09378a05020e3e4883dfdab438926f31fea15fd17604908f3d39cbeb22a0b4dc", size = 278978 }, + { url = "https://files.pythonhosted.org/packages/fc/c2/f84c90167cc7765cb82b3ff7808057608b21c14a38531845d933a4637307/fastuuid-0.14.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bbb0c4b15d66b435d2538f3827f05e44e2baafcc003dd7d8472dc67807ab8fd8", size = 279692 }, + { url = "https://files.pythonhosted.org/packages/af/7b/4bacd03897b88c12348e7bd77943bac32ccf80ff98100598fcff74f75f2e/fastuuid-0.14.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:cd5a7f648d4365b41dbf0e38fe8da4884e57bed4e77c83598e076ac0c93995e7", size = 303384 }, + { url = "https://files.pythonhosted.org/packages/c0/a2/584f2c29641df8bd810d00c1f21d408c12e9ad0c0dafdb8b7b29e5ddf787/fastuuid-0.14.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:c0a94245afae4d7af8c43b3159d5e3934c53f47140be0be624b96acd672ceb73", size = 460921 }, + { url = "https://files.pythonhosted.org/packages/24/68/c6b77443bb7764c760e211002c8638c0c7cce11cb584927e723215ba1398/fastuuid-0.14.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:2b29e23c97e77c3a9514d70ce343571e469098ac7f5a269320a0f0b3e193ab36", size = 480575 }, + { url = "https://files.pythonhosted.org/packages/5a/87/93f553111b33f9bb83145be12868c3c475bf8ea87c107063d01377cc0e8e/fastuuid-0.14.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:1e690d48f923c253f28151b3a6b4e335f2b06bf669c68a02665bc150b7839e94", size = 452317 }, + { url = "https://files.pythonhosted.org/packages/9e/8c/a04d486ca55b5abb7eaa65b39df8d891b7b1635b22db2163734dc273579a/fastuuid-0.14.0-cp311-cp311-win32.whl", hash = "sha256:a6f46790d59ab38c6aa0e35c681c0484b50dc0acf9e2679c005d61e019313c24", size = 154804 }, + { url = "https://files.pythonhosted.org/packages/9c/b2/2d40bf00820de94b9280366a122cbaa60090c8cf59e89ac3938cf5d75895/fastuuid-0.14.0-cp311-cp311-win_amd64.whl", hash = "sha256:e150eab56c95dc9e3fefc234a0eedb342fac433dacc273cd4d150a5b0871e1fa", size = 156099 }, + { url = "https://files.pythonhosted.org/packages/02/a2/e78fcc5df65467f0d207661b7ef86c5b7ac62eea337c0c0fcedbeee6fb13/fastuuid-0.14.0-cp312-cp312-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:77e94728324b63660ebf8adb27055e92d2e4611645bf12ed9d88d30486471d0a", size = 510164 }, + { url = "https://files.pythonhosted.org/packages/2b/b3/c846f933f22f581f558ee63f81f29fa924acd971ce903dab1a9b6701816e/fastuuid-0.14.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:caa1f14d2102cb8d353096bc6ef6c13b2c81f347e6ab9d6fbd48b9dea41c153d", size = 261837 }, + { url = "https://files.pythonhosted.org/packages/54/ea/682551030f8c4fa9a769d9825570ad28c0c71e30cf34020b85c1f7ee7382/fastuuid-0.14.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d23ef06f9e67163be38cece704170486715b177f6baae338110983f99a72c070", size = 251370 }, + { url = "https://files.pythonhosted.org/packages/14/dd/5927f0a523d8e6a76b70968e6004966ee7df30322f5fc9b6cdfb0276646a/fastuuid-0.14.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0c9ec605ace243b6dbe3bd27ebdd5d33b00d8d1d3f580b39fdd15cd96fd71796", size = 277766 }, + { url = "https://files.pythonhosted.org/packages/16/6e/c0fb547eef61293153348f12e0f75a06abb322664b34a1573a7760501336/fastuuid-0.14.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:808527f2407f58a76c916d6aa15d58692a4a019fdf8d4c32ac7ff303b7d7af09", size = 278105 }, + { url = "https://files.pythonhosted.org/packages/2d/b1/b9c75e03b768f61cf2e84ee193dc18601aeaf89a4684b20f2f0e9f52b62c/fastuuid-0.14.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2fb3c0d7fef6674bbeacdd6dbd386924a7b60b26de849266d1ff6602937675c8", size = 301564 }, + { url = "https://files.pythonhosted.org/packages/fc/fa/f7395fdac07c7a54f18f801744573707321ca0cee082e638e36452355a9d/fastuuid-0.14.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:ab3f5d36e4393e628a4df337c2c039069344db5f4b9d2a3c9cea48284f1dd741", size = 459659 }, + { url = "https://files.pythonhosted.org/packages/66/49/c9fd06a4a0b1f0f048aacb6599e7d96e5d6bc6fa680ed0d46bf111929d1b/fastuuid-0.14.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:b9a0ca4f03b7e0b01425281ffd44e99d360e15c895f1907ca105854ed85e2057", size = 478430 }, + { url = "https://files.pythonhosted.org/packages/be/9c/909e8c95b494e8e140e8be6165d5fc3f61fdc46198c1554df7b3e1764471/fastuuid-0.14.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:3acdf655684cc09e60fb7e4cf524e8f42ea760031945aa8086c7eae2eeeabeb8", size = 450894 }, + { url = "https://files.pythonhosted.org/packages/90/eb/d29d17521976e673c55ef7f210d4cdd72091a9ec6755d0fd4710d9b3c871/fastuuid-0.14.0-cp312-cp312-win32.whl", hash = "sha256:9579618be6280700ae36ac42c3efd157049fe4dd40ca49b021280481c78c3176", size = 154374 }, + { url = "https://files.pythonhosted.org/packages/cc/fc/f5c799a6ea6d877faec0472d0b27c079b47c86b1cdc577720a5386483b36/fastuuid-0.14.0-cp312-cp312-win_amd64.whl", hash = "sha256:d9e4332dc4ba054434a9594cbfaf7823b57993d7d8e7267831c3e059857cf397", size = 156550 }, + { url = "https://files.pythonhosted.org/packages/a5/83/ae12dd39b9a39b55d7f90abb8971f1a5f3c321fd72d5aa83f90dc67fe9ed/fastuuid-0.14.0-cp313-cp313-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:77a09cb7427e7af74c594e409f7731a0cf887221de2f698e1ca0ebf0f3139021", size = 510720 }, + { url = "https://files.pythonhosted.org/packages/53/b0/a4b03ff5d00f563cc7546b933c28cb3f2a07344b2aec5834e874f7d44143/fastuuid-0.14.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:9bd57289daf7b153bfa3e8013446aa144ce5e8c825e9e366d455155ede5ea2dc", size = 262024 }, + { url = "https://files.pythonhosted.org/packages/9c/6d/64aee0a0f6a58eeabadd582e55d0d7d70258ffdd01d093b30c53d668303b/fastuuid-0.14.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:ac60fc860cdf3c3f327374db87ab8e064c86566ca8c49d2e30df15eda1b0c2d5", size = 251679 }, + { url = "https://files.pythonhosted.org/packages/60/f5/a7e9cda8369e4f7919d36552db9b2ae21db7915083bc6336f1b0082c8b2e/fastuuid-0.14.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ab32f74bd56565b186f036e33129da77db8be09178cd2f5206a5d4035fb2a23f", size = 277862 }, + { url = "https://files.pythonhosted.org/packages/f0/d3/8ce11827c783affffd5bd4d6378b28eb6cc6d2ddf41474006b8d62e7448e/fastuuid-0.14.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:33e678459cf4addaedd9936bbb038e35b3f6b2061330fd8f2f6a1d80414c0f87", size = 278278 }, + { url = "https://files.pythonhosted.org/packages/a2/51/680fb6352d0bbade04036da46264a8001f74b7484e2fd1f4da9e3db1c666/fastuuid-0.14.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1e3cc56742f76cd25ecb98e4b82a25f978ccffba02e4bdce8aba857b6d85d87b", size = 301788 }, + { url = "https://files.pythonhosted.org/packages/fa/7c/2014b5785bd8ebdab04ec857635ebd84d5ee4950186a577db9eff0fb8ff6/fastuuid-0.14.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:cb9a030f609194b679e1660f7e32733b7a0f332d519c5d5a6a0a580991290022", size = 459819 }, + { url = "https://files.pythonhosted.org/packages/01/d2/524d4ceeba9160e7a9bc2ea3e8f4ccf1ad78f3bde34090ca0c51f09a5e91/fastuuid-0.14.0-cp313-cp313-musllinux_1_1_i686.whl", hash = "sha256:09098762aad4f8da3a888eb9ae01c84430c907a297b97166b8abc07b640f2995", size = 478546 }, + { url = "https://files.pythonhosted.org/packages/bc/17/354d04951ce114bf4afc78e27a18cfbd6ee319ab1829c2d5fb5e94063ac6/fastuuid-0.14.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:1383fff584fa249b16329a059c68ad45d030d5a4b70fb7c73a08d98fd53bcdab", size = 450921 }, + { url = "https://files.pythonhosted.org/packages/fb/be/d7be8670151d16d88f15bb121c5b66cdb5ea6a0c2a362d0dcf30276ade53/fastuuid-0.14.0-cp313-cp313-win32.whl", hash = "sha256:a0809f8cc5731c066c909047f9a314d5f536c871a7a22e815cc4967c110ac9ad", size = 154559 }, + { url = "https://files.pythonhosted.org/packages/22/1d/5573ef3624ceb7abf4a46073d3554e37191c868abc3aecd5289a72f9810a/fastuuid-0.14.0-cp313-cp313-win_amd64.whl", hash = "sha256:0df14e92e7ad3276327631c9e7cec09e32572ce82089c55cb1bb8df71cf394ed", size = 156539 }, + { url = "https://files.pythonhosted.org/packages/16/c9/8c7660d1fe3862e3f8acabd9be7fc9ad71eb270f1c65cce9a2b7a31329ab/fastuuid-0.14.0-cp314-cp314-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:b852a870a61cfc26c884af205d502881a2e59cc07076b60ab4a951cc0c94d1ad", size = 510600 }, + { url = "https://files.pythonhosted.org/packages/4c/f4/a989c82f9a90d0ad995aa957b3e572ebef163c5299823b4027986f133dfb/fastuuid-0.14.0-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:c7502d6f54cd08024c3ea9b3514e2d6f190feb2f46e6dbcd3747882264bb5f7b", size = 262069 }, + { url = "https://files.pythonhosted.org/packages/da/6c/a1a24f73574ac995482b1326cf7ab41301af0fabaa3e37eeb6b3df00e6e2/fastuuid-0.14.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:1ca61b592120cf314cfd66e662a5b54a578c5a15b26305e1b8b618a6f22df714", size = 251543 }, + { url = "https://files.pythonhosted.org/packages/1a/20/2a9b59185ba7a6c7b37808431477c2d739fcbdabbf63e00243e37bd6bf49/fastuuid-0.14.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aa75b6657ec129d0abded3bec745e6f7ab642e6dba3a5272a68247e85f5f316f", size = 277798 }, + { url = "https://files.pythonhosted.org/packages/ef/33/4105ca574f6ded0af6a797d39add041bcfb468a1255fbbe82fcb6f592da2/fastuuid-0.14.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a8a0dfea3972200f72d4c7df02c8ac70bad1bb4c58d7e0ec1e6f341679073a7f", size = 278283 }, + { url = "https://files.pythonhosted.org/packages/fe/8c/fca59f8e21c4deb013f574eae05723737ddb1d2937ce87cb2a5d20992dc3/fastuuid-0.14.0-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1bf539a7a95f35b419f9ad105d5a8a35036df35fdafae48fb2fd2e5f318f0d75", size = 301627 }, + { url = "https://files.pythonhosted.org/packages/cb/e2/f78c271b909c034d429218f2798ca4e89eeda7983f4257d7865976ddbb6c/fastuuid-0.14.0-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:9a133bf9cc78fdbd1179cb58a59ad0100aa32d8675508150f3658814aeefeaa4", size = 459778 }, + { url = "https://files.pythonhosted.org/packages/1e/f0/5ff209d865897667a2ff3e7a572267a9ced8f7313919f6d6043aed8b1caa/fastuuid-0.14.0-cp314-cp314-musllinux_1_1_i686.whl", hash = "sha256:f54d5b36c56a2d5e1a31e73b950b28a0d83eb0c37b91d10408875a5a29494bad", size = 478605 }, + { url = "https://files.pythonhosted.org/packages/e0/c8/2ce1c78f983a2c4987ea865d9516dbdfb141a120fd3abb977ae6f02ba7ca/fastuuid-0.14.0-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:ec27778c6ca3393ef662e2762dba8af13f4ec1aaa32d08d77f71f2a70ae9feb8", size = 450837 }, + { url = "https://files.pythonhosted.org/packages/df/60/dad662ec9a33b4a5fe44f60699258da64172c39bd041da2994422cdc40fe/fastuuid-0.14.0-cp314-cp314-win32.whl", hash = "sha256:e23fc6a83f112de4be0cc1990e5b127c27663ae43f866353166f87df58e73d06", size = 154532 }, + { url = "https://files.pythonhosted.org/packages/1f/f6/da4db31001e854025ffd26bc9ba0740a9cbba2c3259695f7c5834908b336/fastuuid-0.14.0-cp314-cp314-win_amd64.whl", hash = "sha256:df61342889d0f5e7a32f7284e55ef95103f2110fee433c2ae7c2c0956d76ac8a", size = 156457 }, +] + +[[package]] +name = "filelock" +version = "3.20.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/1d/65/ce7f1b70157833bf3cb851b556a37d4547ceafc158aa9b34b36782f23696/filelock-3.20.3.tar.gz", hash = "sha256:18c57ee915c7ec61cff0ecf7f0f869936c7c30191bb0cf406f1341778d0834e1", size = 19485 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b5/36/7fb70f04bf00bc646cd5bb45aa9eddb15e19437a28b8fb2b4a5249fac770/filelock-3.20.3-py3-none-any.whl", hash = "sha256:4b0dda527ee31078689fc205ec4f1c1bf7d56cf88b6dc9426c4f230e46c2dce1", size = 16701 }, +] + +[[package]] +name = "frozenlist" +version = "1.8.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/2d/f5/c831fac6cc817d26fd54c7eaccd04ef7e0288806943f7cc5bbf69f3ac1f0/frozenlist-1.8.0.tar.gz", hash = "sha256:3ede829ed8d842f6cd48fc7081d7a41001a56f1f38603f9d49bf3020d59a31ad", size = 45875 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/bc/03/077f869d540370db12165c0aa51640a873fb661d8b315d1d4d67b284d7ac/frozenlist-1.8.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:09474e9831bc2b2199fad6da3c14c7b0fbdd377cce9d3d77131be28906cb7d84", size = 86912 }, + { url = "https://files.pythonhosted.org/packages/df/b5/7610b6bd13e4ae77b96ba85abea1c8cb249683217ef09ac9e0ae93f25a91/frozenlist-1.8.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:17c883ab0ab67200b5f964d2b9ed6b00971917d5d8a92df149dc2c9779208ee9", size = 50046 }, + { url = "https://files.pythonhosted.org/packages/6e/ef/0e8f1fe32f8a53dd26bdd1f9347efe0778b0fddf62789ea683f4cc7d787d/frozenlist-1.8.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:fa47e444b8ba08fffd1c18e8cdb9a75db1b6a27f17507522834ad13ed5922b93", size = 50119 }, + { url = "https://files.pythonhosted.org/packages/11/b1/71a477adc7c36e5fb628245dfbdea2166feae310757dea848d02bd0689fd/frozenlist-1.8.0-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:2552f44204b744fba866e573be4c1f9048d6a324dfe14475103fd51613eb1d1f", size = 231067 }, + { url = "https://files.pythonhosted.org/packages/45/7e/afe40eca3a2dc19b9904c0f5d7edfe82b5304cb831391edec0ac04af94c2/frozenlist-1.8.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:957e7c38f250991e48a9a73e6423db1bb9dd14e722a10f6b8bb8e16a0f55f695", size = 233160 }, + { url = "https://files.pythonhosted.org/packages/a6/aa/7416eac95603ce428679d273255ffc7c998d4132cfae200103f164b108aa/frozenlist-1.8.0-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:8585e3bb2cdea02fc88ffa245069c36555557ad3609e83be0ec71f54fd4abb52", size = 228544 }, + { url = "https://files.pythonhosted.org/packages/8b/3d/2a2d1f683d55ac7e3875e4263d28410063e738384d3adc294f5ff3d7105e/frozenlist-1.8.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:edee74874ce20a373d62dc28b0b18b93f645633c2943fd90ee9d898550770581", size = 243797 }, + { url = "https://files.pythonhosted.org/packages/78/1e/2d5565b589e580c296d3bb54da08d206e797d941a83a6fdea42af23be79c/frozenlist-1.8.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:c9a63152fe95756b85f31186bddf42e4c02c6321207fd6601a1c89ebac4fe567", size = 247923 }, + { url = "https://files.pythonhosted.org/packages/aa/c3/65872fcf1d326a7f101ad4d86285c403c87be7d832b7470b77f6d2ed5ddc/frozenlist-1.8.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:b6db2185db9be0a04fecf2f241c70b63b1a242e2805be291855078f2b404dd6b", size = 230886 }, + { url = "https://files.pythonhosted.org/packages/a0/76/ac9ced601d62f6956f03cc794f9e04c81719509f85255abf96e2510f4265/frozenlist-1.8.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:f4be2e3d8bc8aabd566f8d5b8ba7ecc09249d74ba3c9ed52e54dc23a293f0b92", size = 245731 }, + { url = "https://files.pythonhosted.org/packages/b9/49/ecccb5f2598daf0b4a1415497eba4c33c1e8ce07495eb07d2860c731b8d5/frozenlist-1.8.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:c8d1634419f39ea6f5c427ea2f90ca85126b54b50837f31497f3bf38266e853d", size = 241544 }, + { url = "https://files.pythonhosted.org/packages/53/4b/ddf24113323c0bbcc54cb38c8b8916f1da7165e07b8e24a717b4a12cbf10/frozenlist-1.8.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:1a7fa382a4a223773ed64242dbe1c9c326ec09457e6b8428efb4118c685c3dfd", size = 241806 }, + { url = "https://files.pythonhosted.org/packages/a7/fb/9b9a084d73c67175484ba2789a59f8eebebd0827d186a8102005ce41e1ba/frozenlist-1.8.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:11847b53d722050808926e785df837353bd4d75f1d494377e59b23594d834967", size = 229382 }, + { url = "https://files.pythonhosted.org/packages/95/a3/c8fb25aac55bf5e12dae5c5aa6a98f85d436c1dc658f21c3ac73f9fa95e5/frozenlist-1.8.0-cp311-cp311-win32.whl", hash = "sha256:27c6e8077956cf73eadd514be8fb04d77fc946a7fe9f7fe167648b0b9085cc25", size = 39647 }, + { url = "https://files.pythonhosted.org/packages/0a/f5/603d0d6a02cfd4c8f2a095a54672b3cf967ad688a60fb9faf04fc4887f65/frozenlist-1.8.0-cp311-cp311-win_amd64.whl", hash = "sha256:ac913f8403b36a2c8610bbfd25b8013488533e71e62b4b4adce9c86c8cea905b", size = 44064 }, + { url = "https://files.pythonhosted.org/packages/5d/16/c2c9ab44e181f043a86f9a8f84d5124b62dbcb3a02c0977ec72b9ac1d3e0/frozenlist-1.8.0-cp311-cp311-win_arm64.whl", hash = "sha256:d4d3214a0f8394edfa3e303136d0575eece0745ff2b47bd2cb2e66dd92d4351a", size = 39937 }, + { url = "https://files.pythonhosted.org/packages/69/29/948b9aa87e75820a38650af445d2ef2b6b8a6fab1a23b6bb9e4ef0be2d59/frozenlist-1.8.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:78f7b9e5d6f2fdb88cdde9440dc147259b62b9d3b019924def9f6478be254ac1", size = 87782 }, + { url = "https://files.pythonhosted.org/packages/64/80/4f6e318ee2a7c0750ed724fa33a4bdf1eacdc5a39a7a24e818a773cd91af/frozenlist-1.8.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:229bf37d2e4acdaf808fd3f06e854a4a7a3661e871b10dc1f8f1896a3b05f18b", size = 50594 }, + { url = "https://files.pythonhosted.org/packages/2b/94/5c8a2b50a496b11dd519f4a24cb5496cf125681dd99e94c604ccdea9419a/frozenlist-1.8.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f833670942247a14eafbb675458b4e61c82e002a148f49e68257b79296e865c4", size = 50448 }, + { url = "https://files.pythonhosted.org/packages/6a/bd/d91c5e39f490a49df14320f4e8c80161cfcce09f1e2cde1edd16a551abb3/frozenlist-1.8.0-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:494a5952b1c597ba44e0e78113a7266e656b9794eec897b19ead706bd7074383", size = 242411 }, + { url = "https://files.pythonhosted.org/packages/8f/83/f61505a05109ef3293dfb1ff594d13d64a2324ac3482be2cedc2be818256/frozenlist-1.8.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:96f423a119f4777a4a056b66ce11527366a8bb92f54e541ade21f2374433f6d4", size = 243014 }, + { url = "https://files.pythonhosted.org/packages/d8/cb/cb6c7b0f7d4023ddda30cf56b8b17494eb3a79e3fda666bf735f63118b35/frozenlist-1.8.0-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3462dd9475af2025c31cc61be6652dfa25cbfb56cbbf52f4ccfe029f38decaf8", size = 234909 }, + { url = "https://files.pythonhosted.org/packages/31/c5/cd7a1f3b8b34af009fb17d4123c5a778b44ae2804e3ad6b86204255f9ec5/frozenlist-1.8.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c4c800524c9cd9bac5166cd6f55285957fcfc907db323e193f2afcd4d9abd69b", size = 250049 }, + { url = "https://files.pythonhosted.org/packages/c0/01/2f95d3b416c584a1e7f0e1d6d31998c4a795f7544069ee2e0962a4b60740/frozenlist-1.8.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d6a5df73acd3399d893dafc71663ad22534b5aa4f94e8a2fabfe856c3c1b6a52", size = 256485 }, + { url = "https://files.pythonhosted.org/packages/ce/03/024bf7720b3abaebcff6d0793d73c154237b85bdf67b7ed55e5e9596dc9a/frozenlist-1.8.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:405e8fe955c2280ce66428b3ca55e12b3c4e9c336fb2103a4937e891c69a4a29", size = 237619 }, + { url = "https://files.pythonhosted.org/packages/69/fa/f8abdfe7d76b731f5d8bd217827cf6764d4f1d9763407e42717b4bed50a0/frozenlist-1.8.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:908bd3f6439f2fef9e85031b59fd4f1297af54415fb60e4254a95f75b3cab3f3", size = 250320 }, + { url = "https://files.pythonhosted.org/packages/f5/3c/b051329f718b463b22613e269ad72138cc256c540f78a6de89452803a47d/frozenlist-1.8.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:294e487f9ec720bd8ffcebc99d575f7eff3568a08a253d1ee1a0378754b74143", size = 246820 }, + { url = "https://files.pythonhosted.org/packages/0f/ae/58282e8f98e444b3f4dd42448ff36fa38bef29e40d40f330b22e7108f565/frozenlist-1.8.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:74c51543498289c0c43656701be6b077f4b265868fa7f8a8859c197006efb608", size = 250518 }, + { url = "https://files.pythonhosted.org/packages/8f/96/007e5944694d66123183845a106547a15944fbbb7154788cbf7272789536/frozenlist-1.8.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:776f352e8329135506a1d6bf16ac3f87bc25b28e765949282dcc627af36123aa", size = 239096 }, + { url = "https://files.pythonhosted.org/packages/66/bb/852b9d6db2fa40be96f29c0d1205c306288f0684df8fd26ca1951d461a56/frozenlist-1.8.0-cp312-cp312-win32.whl", hash = "sha256:433403ae80709741ce34038da08511d4a77062aa924baf411ef73d1146e74faf", size = 39985 }, + { url = "https://files.pythonhosted.org/packages/b8/af/38e51a553dd66eb064cdf193841f16f077585d4d28394c2fa6235cb41765/frozenlist-1.8.0-cp312-cp312-win_amd64.whl", hash = "sha256:34187385b08f866104f0c0617404c8eb08165ab1272e884abc89c112e9c00746", size = 44591 }, + { url = "https://files.pythonhosted.org/packages/a7/06/1dc65480ab147339fecc70797e9c2f69d9cea9cf38934ce08df070fdb9cb/frozenlist-1.8.0-cp312-cp312-win_arm64.whl", hash = "sha256:fe3c58d2f5db5fbd18c2987cba06d51b0529f52bc3a6cdc33d3f4eab725104bd", size = 40102 }, + { url = "https://files.pythonhosted.org/packages/2d/40/0832c31a37d60f60ed79e9dfb5a92e1e2af4f40a16a29abcc7992af9edff/frozenlist-1.8.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:8d92f1a84bb12d9e56f818b3a746f3efba93c1b63c8387a73dde655e1e42282a", size = 85717 }, + { url = "https://files.pythonhosted.org/packages/30/ba/b0b3de23f40bc55a7057bd38434e25c34fa48e17f20ee273bbde5e0650f3/frozenlist-1.8.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:96153e77a591c8adc2ee805756c61f59fef4cf4073a9275ee86fe8cba41241f7", size = 49651 }, + { url = "https://files.pythonhosted.org/packages/0c/ab/6e5080ee374f875296c4243c381bbdef97a9ac39c6e3ce1d5f7d42cb78d6/frozenlist-1.8.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f21f00a91358803399890ab167098c131ec2ddd5f8f5fd5fe9c9f2c6fcd91e40", size = 49417 }, + { url = "https://files.pythonhosted.org/packages/d5/4e/e4691508f9477ce67da2015d8c00acd751e6287739123113a9fca6f1604e/frozenlist-1.8.0-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:fb30f9626572a76dfe4293c7194a09fb1fe93ba94c7d4f720dfae3b646b45027", size = 234391 }, + { url = "https://files.pythonhosted.org/packages/40/76/c202df58e3acdf12969a7895fd6f3bc016c642e6726aa63bd3025e0fc71c/frozenlist-1.8.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:eaa352d7047a31d87dafcacbabe89df0aa506abb5b1b85a2fb91bc3faa02d822", size = 233048 }, + { url = "https://files.pythonhosted.org/packages/f9/c0/8746afb90f17b73ca5979c7a3958116e105ff796e718575175319b5bb4ce/frozenlist-1.8.0-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:03ae967b4e297f58f8c774c7eabcce57fe3c2434817d4385c50661845a058121", size = 226549 }, + { url = "https://files.pythonhosted.org/packages/7e/eb/4c7eefc718ff72f9b6c4893291abaae5fbc0c82226a32dcd8ef4f7a5dbef/frozenlist-1.8.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f6292f1de555ffcc675941d65fffffb0a5bcd992905015f85d0592201793e0e5", size = 239833 }, + { url = "https://files.pythonhosted.org/packages/c2/4e/e5c02187cf704224f8b21bee886f3d713ca379535f16893233b9d672ea71/frozenlist-1.8.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:29548f9b5b5e3460ce7378144c3010363d8035cea44bc0bf02d57f5a685e084e", size = 245363 }, + { url = "https://files.pythonhosted.org/packages/1f/96/cb85ec608464472e82ad37a17f844889c36100eed57bea094518bf270692/frozenlist-1.8.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ec3cc8c5d4084591b4237c0a272cc4f50a5b03396a47d9caaf76f5d7b38a4f11", size = 229314 }, + { url = "https://files.pythonhosted.org/packages/5d/6f/4ae69c550e4cee66b57887daeebe006fe985917c01d0fff9caab9883f6d0/frozenlist-1.8.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:517279f58009d0b1f2e7c1b130b377a349405da3f7621ed6bfae50b10adf20c1", size = 243365 }, + { url = "https://files.pythonhosted.org/packages/7a/58/afd56de246cf11780a40a2c28dc7cbabbf06337cc8ddb1c780a2d97e88d8/frozenlist-1.8.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:db1e72ede2d0d7ccb213f218df6a078a9c09a7de257c2fe8fcef16d5925230b1", size = 237763 }, + { url = "https://files.pythonhosted.org/packages/cb/36/cdfaf6ed42e2644740d4a10452d8e97fa1c062e2a8006e4b09f1b5fd7d63/frozenlist-1.8.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:b4dec9482a65c54a5044486847b8a66bf10c9cb4926d42927ec4e8fd5db7fed8", size = 240110 }, + { url = "https://files.pythonhosted.org/packages/03/a8/9ea226fbefad669f11b52e864c55f0bd57d3c8d7eb07e9f2e9a0b39502e1/frozenlist-1.8.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:21900c48ae04d13d416f0e1e0c4d81f7931f73a9dfa0b7a8746fb2fe7dd970ed", size = 233717 }, + { url = "https://files.pythonhosted.org/packages/1e/0b/1b5531611e83ba7d13ccc9988967ea1b51186af64c42b7a7af465dcc9568/frozenlist-1.8.0-cp313-cp313-win32.whl", hash = "sha256:8b7b94a067d1c504ee0b16def57ad5738701e4ba10cec90529f13fa03c833496", size = 39628 }, + { url = "https://files.pythonhosted.org/packages/d8/cf/174c91dbc9cc49bc7b7aab74d8b734e974d1faa8f191c74af9b7e80848e6/frozenlist-1.8.0-cp313-cp313-win_amd64.whl", hash = "sha256:878be833caa6a3821caf85eb39c5ba92d28e85df26d57afb06b35b2efd937231", size = 43882 }, + { url = "https://files.pythonhosted.org/packages/c1/17/502cd212cbfa96eb1388614fe39a3fc9ab87dbbe042b66f97acb57474834/frozenlist-1.8.0-cp313-cp313-win_arm64.whl", hash = "sha256:44389d135b3ff43ba8cc89ff7f51f5a0bb6b63d829c8300f79a2fe4fe61bcc62", size = 39676 }, + { url = "https://files.pythonhosted.org/packages/d2/5c/3bbfaa920dfab09e76946a5d2833a7cbdf7b9b4a91c714666ac4855b88b4/frozenlist-1.8.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:e25ac20a2ef37e91c1b39938b591457666a0fa835c7783c3a8f33ea42870db94", size = 89235 }, + { url = "https://files.pythonhosted.org/packages/d2/d6/f03961ef72166cec1687e84e8925838442b615bd0b8854b54923ce5b7b8a/frozenlist-1.8.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:07cdca25a91a4386d2e76ad992916a85038a9b97561bf7a3fd12d5d9ce31870c", size = 50742 }, + { url = "https://files.pythonhosted.org/packages/1e/bb/a6d12b7ba4c3337667d0e421f7181c82dda448ce4e7ad7ecd249a16fa806/frozenlist-1.8.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:4e0c11f2cc6717e0a741f84a527c52616140741cd812a50422f83dc31749fb52", size = 51725 }, + { url = "https://files.pythonhosted.org/packages/bc/71/d1fed0ffe2c2ccd70b43714c6cab0f4188f09f8a67a7914a6b46ee30f274/frozenlist-1.8.0-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:b3210649ee28062ea6099cfda39e147fa1bc039583c8ee4481cb7811e2448c51", size = 284533 }, + { url = "https://files.pythonhosted.org/packages/c9/1f/fb1685a7b009d89f9bf78a42d94461bc06581f6e718c39344754a5d9bada/frozenlist-1.8.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:581ef5194c48035a7de2aefc72ac6539823bb71508189e5de01d60c9dcd5fa65", size = 292506 }, + { url = "https://files.pythonhosted.org/packages/e6/3b/b991fe1612703f7e0d05c0cf734c1b77aaf7c7d321df4572e8d36e7048c8/frozenlist-1.8.0-cp313-cp313t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3ef2d026f16a2b1866e1d86fc4e1291e1ed8a387b2c333809419a2f8b3a77b82", size = 274161 }, + { url = "https://files.pythonhosted.org/packages/ca/ec/c5c618767bcdf66e88945ec0157d7f6c4a1322f1473392319b7a2501ded7/frozenlist-1.8.0-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:5500ef82073f599ac84d888e3a8c1f77ac831183244bfd7f11eaa0289fb30714", size = 294676 }, + { url = "https://files.pythonhosted.org/packages/7c/ce/3934758637d8f8a88d11f0585d6495ef54b2044ed6ec84492a91fa3b27aa/frozenlist-1.8.0-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:50066c3997d0091c411a66e710f4e11752251e6d2d73d70d8d5d4c76442a199d", size = 300638 }, + { url = "https://files.pythonhosted.org/packages/fc/4f/a7e4d0d467298f42de4b41cbc7ddaf19d3cfeabaf9ff97c20c6c7ee409f9/frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:5c1c8e78426e59b3f8005e9b19f6ff46e5845895adbde20ece9218319eca6506", size = 283067 }, + { url = "https://files.pythonhosted.org/packages/dc/48/c7b163063d55a83772b268e6d1affb960771b0e203b632cfe09522d67ea5/frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:eefdba20de0d938cec6a89bd4d70f346a03108a19b9df4248d3cf0d88f1b0f51", size = 292101 }, + { url = "https://files.pythonhosted.org/packages/9f/d0/2366d3c4ecdc2fd391e0afa6e11500bfba0ea772764d631bbf82f0136c9d/frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:cf253e0e1c3ceb4aaff6df637ce033ff6535fb8c70a764a8f46aafd3d6ab798e", size = 289901 }, + { url = "https://files.pythonhosted.org/packages/b8/94/daff920e82c1b70e3618a2ac39fbc01ae3e2ff6124e80739ce5d71c9b920/frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:032efa2674356903cd0261c4317a561a6850f3ac864a63fc1583147fb05a79b0", size = 289395 }, + { url = "https://files.pythonhosted.org/packages/e3/20/bba307ab4235a09fdcd3cc5508dbabd17c4634a1af4b96e0f69bfe551ebd/frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:6da155091429aeba16851ecb10a9104a108bcd32f6c1642867eadaee401c1c41", size = 283659 }, + { url = "https://files.pythonhosted.org/packages/fd/00/04ca1c3a7a124b6de4f8a9a17cc2fcad138b4608e7a3fc5877804b8715d7/frozenlist-1.8.0-cp313-cp313t-win32.whl", hash = "sha256:0f96534f8bfebc1a394209427d0f8a63d343c9779cda6fc25e8e121b5fd8555b", size = 43492 }, + { url = "https://files.pythonhosted.org/packages/59/5e/c69f733a86a94ab10f68e496dc6b7e8bc078ebb415281d5698313e3af3a1/frozenlist-1.8.0-cp313-cp313t-win_amd64.whl", hash = "sha256:5d63a068f978fc69421fb0e6eb91a9603187527c86b7cd3f534a5b77a592b888", size = 48034 }, + { url = "https://files.pythonhosted.org/packages/16/6c/be9d79775d8abe79b05fa6d23da99ad6e7763a1d080fbae7290b286093fd/frozenlist-1.8.0-cp313-cp313t-win_arm64.whl", hash = "sha256:bf0a7e10b077bf5fb9380ad3ae8ce20ef919a6ad93b4552896419ac7e1d8e042", size = 41749 }, + { url = "https://files.pythonhosted.org/packages/f1/c8/85da824b7e7b9b6e7f7705b2ecaf9591ba6f79c1177f324c2735e41d36a2/frozenlist-1.8.0-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:cee686f1f4cadeb2136007ddedd0aaf928ab95216e7691c63e50a8ec066336d0", size = 86127 }, + { url = "https://files.pythonhosted.org/packages/8e/e8/a1185e236ec66c20afd72399522f142c3724c785789255202d27ae992818/frozenlist-1.8.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:119fb2a1bd47307e899c2fac7f28e85b9a543864df47aa7ec9d3c1b4545f096f", size = 49698 }, + { url = "https://files.pythonhosted.org/packages/a1/93/72b1736d68f03fda5fdf0f2180fb6caaae3894f1b854d006ac61ecc727ee/frozenlist-1.8.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:4970ece02dbc8c3a92fcc5228e36a3e933a01a999f7094ff7c23fbd2beeaa67c", size = 49749 }, + { url = "https://files.pythonhosted.org/packages/a7/b2/fabede9fafd976b991e9f1b9c8c873ed86f202889b864756f240ce6dd855/frozenlist-1.8.0-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:cba69cb73723c3f329622e34bdbf5ce1f80c21c290ff04256cff1cd3c2036ed2", size = 231298 }, + { url = "https://files.pythonhosted.org/packages/3a/3b/d9b1e0b0eed36e70477ffb8360c49c85c8ca8ef9700a4e6711f39a6e8b45/frozenlist-1.8.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:778a11b15673f6f1df23d9586f83c4846c471a8af693a22e066508b77d201ec8", size = 232015 }, + { url = "https://files.pythonhosted.org/packages/dc/94/be719d2766c1138148564a3960fc2c06eb688da592bdc25adcf856101be7/frozenlist-1.8.0-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:0325024fe97f94c41c08872db482cf8ac4800d80e79222c6b0b7b162d5b13686", size = 225038 }, + { url = "https://files.pythonhosted.org/packages/e4/09/6712b6c5465f083f52f50cf74167b92d4ea2f50e46a9eea0523d658454ae/frozenlist-1.8.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:97260ff46b207a82a7567b581ab4190bd4dfa09f4db8a8b49d1a958f6aa4940e", size = 240130 }, + { url = "https://files.pythonhosted.org/packages/f8/d4/cd065cdcf21550b54f3ce6a22e143ac9e4836ca42a0de1022da8498eac89/frozenlist-1.8.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:54b2077180eb7f83dd52c40b2750d0a9f175e06a42e3213ce047219de902717a", size = 242845 }, + { url = "https://files.pythonhosted.org/packages/62/c3/f57a5c8c70cd1ead3d5d5f776f89d33110b1addae0ab010ad774d9a44fb9/frozenlist-1.8.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:2f05983daecab868a31e1da44462873306d3cbfd76d1f0b5b69c473d21dbb128", size = 229131 }, + { url = "https://files.pythonhosted.org/packages/6c/52/232476fe9cb64f0742f3fde2b7d26c1dac18b6d62071c74d4ded55e0ef94/frozenlist-1.8.0-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:33f48f51a446114bc5d251fb2954ab0164d5be02ad3382abcbfe07e2531d650f", size = 240542 }, + { url = "https://files.pythonhosted.org/packages/5f/85/07bf3f5d0fb5414aee5f47d33c6f5c77bfe49aac680bfece33d4fdf6a246/frozenlist-1.8.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:154e55ec0655291b5dd1b8731c637ecdb50975a2ae70c606d100750a540082f7", size = 237308 }, + { url = "https://files.pythonhosted.org/packages/11/99/ae3a33d5befd41ac0ca2cc7fd3aa707c9c324de2e89db0e0f45db9a64c26/frozenlist-1.8.0-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:4314debad13beb564b708b4a496020e5306c7333fa9a3ab90374169a20ffab30", size = 238210 }, + { url = "https://files.pythonhosted.org/packages/b2/60/b1d2da22f4970e7a155f0adde9b1435712ece01b3cd45ba63702aea33938/frozenlist-1.8.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:073f8bf8becba60aa931eb3bc420b217bb7d5b8f4750e6f8b3be7f3da85d38b7", size = 231972 }, + { url = "https://files.pythonhosted.org/packages/3f/ab/945b2f32de889993b9c9133216c068b7fcf257d8595a0ac420ac8677cab0/frozenlist-1.8.0-cp314-cp314-win32.whl", hash = "sha256:bac9c42ba2ac65ddc115d930c78d24ab8d4f465fd3fc473cdedfccadb9429806", size = 40536 }, + { url = "https://files.pythonhosted.org/packages/59/ad/9caa9b9c836d9ad6f067157a531ac48b7d36499f5036d4141ce78c230b1b/frozenlist-1.8.0-cp314-cp314-win_amd64.whl", hash = "sha256:3e0761f4d1a44f1d1a47996511752cf3dcec5bbdd9cc2b4fe595caf97754b7a0", size = 44330 }, + { url = "https://files.pythonhosted.org/packages/82/13/e6950121764f2676f43534c555249f57030150260aee9dcf7d64efda11dd/frozenlist-1.8.0-cp314-cp314-win_arm64.whl", hash = "sha256:d1eaff1d00c7751b7c6662e9c5ba6eb2c17a2306ba5e2a37f24ddf3cc953402b", size = 40627 }, + { url = "https://files.pythonhosted.org/packages/c0/c7/43200656ecc4e02d3f8bc248df68256cd9572b3f0017f0a0c4e93440ae23/frozenlist-1.8.0-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:d3bb933317c52d7ea5004a1c442eef86f426886fba134ef8cf4226ea6ee1821d", size = 89238 }, + { url = "https://files.pythonhosted.org/packages/d1/29/55c5f0689b9c0fb765055629f472c0de484dcaf0acee2f7707266ae3583c/frozenlist-1.8.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:8009897cdef112072f93a0efdce29cd819e717fd2f649ee3016efd3cd885a7ed", size = 50738 }, + { url = "https://files.pythonhosted.org/packages/ba/7d/b7282a445956506fa11da8c2db7d276adcbf2b17d8bb8407a47685263f90/frozenlist-1.8.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:2c5dcbbc55383e5883246d11fd179782a9d07a986c40f49abe89ddf865913930", size = 51739 }, + { url = "https://files.pythonhosted.org/packages/62/1c/3d8622e60d0b767a5510d1d3cf21065b9db874696a51ea6d7a43180a259c/frozenlist-1.8.0-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:39ecbc32f1390387d2aa4f5a995e465e9e2f79ba3adcac92d68e3e0afae6657c", size = 284186 }, + { url = "https://files.pythonhosted.org/packages/2d/14/aa36d5f85a89679a85a1d44cd7a6657e0b1c75f61e7cad987b203d2daca8/frozenlist-1.8.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:92db2bf818d5cc8d9c1f1fc56b897662e24ea5adb36ad1f1d82875bd64e03c24", size = 292196 }, + { url = "https://files.pythonhosted.org/packages/05/23/6bde59eb55abd407d34f77d39a5126fb7b4f109a3f611d3929f14b700c66/frozenlist-1.8.0-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:2dc43a022e555de94c3b68a4ef0b11c4f747d12c024a520c7101709a2144fb37", size = 273830 }, + { url = "https://files.pythonhosted.org/packages/d2/3f/22cff331bfad7a8afa616289000ba793347fcd7bc275f3b28ecea2a27909/frozenlist-1.8.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:cb89a7f2de3602cfed448095bab3f178399646ab7c61454315089787df07733a", size = 294289 }, + { url = "https://files.pythonhosted.org/packages/a4/89/5b057c799de4838b6c69aa82b79705f2027615e01be996d2486a69ca99c4/frozenlist-1.8.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:33139dc858c580ea50e7e60a1b0ea003efa1fd42e6ec7fdbad78fff65fad2fd2", size = 300318 }, + { url = "https://files.pythonhosted.org/packages/30/de/2c22ab3eb2a8af6d69dc799e48455813bab3690c760de58e1bf43b36da3e/frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:168c0969a329b416119507ba30b9ea13688fafffac1b7822802537569a1cb0ef", size = 282814 }, + { url = "https://files.pythonhosted.org/packages/59/f7/970141a6a8dbd7f556d94977858cfb36fa9b66e0892c6dd780d2219d8cd8/frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:28bd570e8e189d7f7b001966435f9dac6718324b5be2990ac496cf1ea9ddb7fe", size = 291762 }, + { url = "https://files.pythonhosted.org/packages/c1/15/ca1adae83a719f82df9116d66f5bb28bb95557b3951903d39135620ef157/frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:b2a095d45c5d46e5e79ba1e5b9cb787f541a8dee0433836cea4b96a2c439dcd8", size = 289470 }, + { url = "https://files.pythonhosted.org/packages/ac/83/dca6dc53bf657d371fbc88ddeb21b79891e747189c5de990b9dfff2ccba1/frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:eab8145831a0d56ec9c4139b6c3e594c7a83c2c8be25d5bcf2d86136a532287a", size = 289042 }, + { url = "https://files.pythonhosted.org/packages/96/52/abddd34ca99be142f354398700536c5bd315880ed0a213812bc491cff5e4/frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:974b28cf63cc99dfb2188d8d222bc6843656188164848c4f679e63dae4b0708e", size = 283148 }, + { url = "https://files.pythonhosted.org/packages/af/d3/76bd4ed4317e7119c2b7f57c3f6934aba26d277acc6309f873341640e21f/frozenlist-1.8.0-cp314-cp314t-win32.whl", hash = "sha256:342c97bf697ac5480c0a7ec73cd700ecfa5a8a40ac923bd035484616efecc2df", size = 44676 }, + { url = "https://files.pythonhosted.org/packages/89/76/c615883b7b521ead2944bb3480398cbb07e12b7b4e4d073d3752eb721558/frozenlist-1.8.0-cp314-cp314t-win_amd64.whl", hash = "sha256:06be8f67f39c8b1dc671f5d83aaefd3358ae5cdcf8314552c57e7ed3e6475bdd", size = 49451 }, + { url = "https://files.pythonhosted.org/packages/e0/a3/5982da14e113d07b325230f95060e2169f5311b1017ea8af2a29b374c289/frozenlist-1.8.0-cp314-cp314t-win_arm64.whl", hash = "sha256:102e6314ca4da683dca92e3b1355490fed5f313b768500084fbe6371fddfdb79", size = 42507 }, + { url = "https://files.pythonhosted.org/packages/9a/9a/e35b4a917281c0b8419d4207f4334c8e8c5dbf4f3f5f9ada73958d937dcc/frozenlist-1.8.0-py3-none-any.whl", hash = "sha256:0c18a16eab41e82c295618a77502e17b195883241c563b00f0aa5106fc4eaa0d", size = 13409 }, +] + +[[package]] +name = "fsspec" +version = "2026.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d5/7d/5df2650c57d47c57232af5ef4b4fdbff182070421e405e0d62c6cdbfaa87/fsspec-2026.1.0.tar.gz", hash = "sha256:e987cb0496a0d81bba3a9d1cee62922fb395e7d4c3b575e57f547953334fe07b", size = 310496 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/01/c9/97cc5aae1648dcb851958a3ddf73ccd7dbe5650d95203ecb4d7720b4cdbf/fsspec-2026.1.0-py3-none-any.whl", hash = "sha256:cb76aa913c2285a3b49bdd5fc55b1d7c708d7208126b60f2eb8194fe1b4cbdcc", size = 201838 }, +] + +[[package]] +name = "grpcio" +version = "1.76.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b6/e0/318c1ce3ae5a17894d5791e87aea147587c9e702f24122cc7a5c8bbaeeb1/grpcio-1.76.0.tar.gz", hash = "sha256:7be78388d6da1a25c0d5ec506523db58b18be22d9c37d8d3a32c08be4987bd73", size = 12785182 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a0/00/8163a1beeb6971f66b4bbe6ac9457b97948beba8dd2fc8e1281dce7f79ec/grpcio-1.76.0-cp311-cp311-linux_armv7l.whl", hash = "sha256:2e1743fbd7f5fa713a1b0a8ac8ebabf0ec980b5d8809ec358d488e273b9cf02a", size = 5843567 }, + { url = "https://files.pythonhosted.org/packages/10/c1/934202f5cf335e6d852530ce14ddb0fef21be612ba9ecbbcbd4d748ca32d/grpcio-1.76.0-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:a8c2cf1209497cf659a667d7dea88985e834c24b7c3b605e6254cbb5076d985c", size = 11848017 }, + { url = "https://files.pythonhosted.org/packages/11/0b/8dec16b1863d74af6eb3543928600ec2195af49ca58b16334972f6775663/grpcio-1.76.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:08caea849a9d3c71a542827d6df9d5a69067b0a1efbea8a855633ff5d9571465", size = 6412027 }, + { url = "https://files.pythonhosted.org/packages/d7/64/7b9e6e7ab910bea9d46f2c090380bab274a0b91fb0a2fe9b0cd399fffa12/grpcio-1.76.0-cp311-cp311-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:f0e34c2079d47ae9f6188211db9e777c619a21d4faba6977774e8fa43b085e48", size = 7075913 }, + { url = "https://files.pythonhosted.org/packages/68/86/093c46e9546073cefa789bd76d44c5cb2abc824ca62af0c18be590ff13ba/grpcio-1.76.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:8843114c0cfce61b40ad48df65abcfc00d4dba82eae8718fab5352390848c5da", size = 6615417 }, + { url = "https://files.pythonhosted.org/packages/f7/b6/5709a3a68500a9c03da6fb71740dcdd5ef245e39266461a03f31a57036d8/grpcio-1.76.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8eddfb4d203a237da6f3cc8a540dad0517d274b5a1e9e636fd8d2c79b5c1d397", size = 7199683 }, + { url = "https://files.pythonhosted.org/packages/91/d3/4b1f2bf16ed52ce0b508161df3a2d186e4935379a159a834cb4a7d687429/grpcio-1.76.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:32483fe2aab2c3794101c2a159070584e5db11d0aa091b2c0ea9c4fc43d0d749", size = 8163109 }, + { url = "https://files.pythonhosted.org/packages/5c/61/d9043f95f5f4cf085ac5dd6137b469d41befb04bd80280952ffa2a4c3f12/grpcio-1.76.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:dcfe41187da8992c5f40aa8c5ec086fa3672834d2be57a32384c08d5a05b4c00", size = 7626676 }, + { url = "https://files.pythonhosted.org/packages/36/95/fd9a5152ca02d8881e4dd419cdd790e11805979f499a2e5b96488b85cf27/grpcio-1.76.0-cp311-cp311-win32.whl", hash = "sha256:2107b0c024d1b35f4083f11245c0e23846ae64d02f40b2b226684840260ed054", size = 3997688 }, + { url = "https://files.pythonhosted.org/packages/60/9c/5c359c8d4c9176cfa3c61ecd4efe5affe1f38d9bae81e81ac7186b4c9cc8/grpcio-1.76.0-cp311-cp311-win_amd64.whl", hash = "sha256:522175aba7af9113c48ec10cc471b9b9bd4f6ceb36aeb4544a8e2c80ed9d252d", size = 4709315 }, + { url = "https://files.pythonhosted.org/packages/bf/05/8e29121994b8d959ffa0afd28996d452f291b48cfc0875619de0bde2c50c/grpcio-1.76.0-cp312-cp312-linux_armv7l.whl", hash = "sha256:81fd9652b37b36f16138611c7e884eb82e0cec137c40d3ef7c3f9b3ed00f6ed8", size = 5799718 }, + { url = "https://files.pythonhosted.org/packages/d9/75/11d0e66b3cdf998c996489581bdad8900db79ebd83513e45c19548f1cba4/grpcio-1.76.0-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:04bbe1bfe3a68bbfd4e52402ab7d4eb59d72d02647ae2042204326cf4bbad280", size = 11825627 }, + { url = "https://files.pythonhosted.org/packages/28/50/2f0aa0498bc188048f5d9504dcc5c2c24f2eb1a9337cd0fa09a61a2e75f0/grpcio-1.76.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d388087771c837cdb6515539f43b9d4bf0b0f23593a24054ac16f7a960be16f4", size = 6359167 }, + { url = "https://files.pythonhosted.org/packages/66/e5/bbf0bb97d29ede1d59d6588af40018cfc345b17ce979b7b45424628dc8bb/grpcio-1.76.0-cp312-cp312-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:9f8f757bebaaea112c00dba718fc0d3260052ce714e25804a03f93f5d1c6cc11", size = 7044267 }, + { url = "https://files.pythonhosted.org/packages/f5/86/f6ec2164f743d9609691115ae8ece098c76b894ebe4f7c94a655c6b03e98/grpcio-1.76.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:980a846182ce88c4f2f7e2c22c56aefd515daeb36149d1c897f83cf57999e0b6", size = 6573963 }, + { url = "https://files.pythonhosted.org/packages/60/bc/8d9d0d8505feccfdf38a766d262c71e73639c165b311c9457208b56d92ae/grpcio-1.76.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:f92f88e6c033db65a5ae3d97905c8fea9c725b63e28d5a75cb73b49bda5024d8", size = 7164484 }, + { url = "https://files.pythonhosted.org/packages/67/e6/5d6c2fc10b95edf6df9b8f19cf10a34263b7fd48493936fffd5085521292/grpcio-1.76.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:4baf3cbe2f0be3289eb68ac8ae771156971848bb8aaff60bad42005539431980", size = 8127777 }, + { url = "https://files.pythonhosted.org/packages/3f/c8/dce8ff21c86abe025efe304d9e31fdb0deaaa3b502b6a78141080f206da0/grpcio-1.76.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:615ba64c208aaceb5ec83bfdce7728b80bfeb8be97562944836a7a0a9647d882", size = 7594014 }, + { url = "https://files.pythonhosted.org/packages/e0/42/ad28191ebf983a5d0ecef90bab66baa5a6b18f2bfdef9d0a63b1973d9f75/grpcio-1.76.0-cp312-cp312-win32.whl", hash = "sha256:45d59a649a82df5718fd9527ce775fd66d1af35e6d31abdcdc906a49c6822958", size = 3984750 }, + { url = "https://files.pythonhosted.org/packages/9e/00/7bd478cbb851c04a48baccaa49b75abaa8e4122f7d86da797500cccdd771/grpcio-1.76.0-cp312-cp312-win_amd64.whl", hash = "sha256:c088e7a90b6017307f423efbb9d1ba97a22aa2170876223f9709e9d1de0b5347", size = 4704003 }, + { url = "https://files.pythonhosted.org/packages/fc/ed/71467ab770effc9e8cef5f2e7388beb2be26ed642d567697bb103a790c72/grpcio-1.76.0-cp313-cp313-linux_armv7l.whl", hash = "sha256:26ef06c73eb53267c2b319f43e6634c7556ea37672029241a056629af27c10e2", size = 5807716 }, + { url = "https://files.pythonhosted.org/packages/2c/85/c6ed56f9817fab03fa8a111ca91469941fb514e3e3ce6d793cb8f1e1347b/grpcio-1.76.0-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:45e0111e73f43f735d70786557dc38141185072d7ff8dc1829d6a77ac1471468", size = 11821522 }, + { url = "https://files.pythonhosted.org/packages/ac/31/2b8a235ab40c39cbc141ef647f8a6eb7b0028f023015a4842933bc0d6831/grpcio-1.76.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:83d57312a58dcfe2a3a0f9d1389b299438909a02db60e2f2ea2ae2d8034909d3", size = 6362558 }, + { url = "https://files.pythonhosted.org/packages/bd/64/9784eab483358e08847498ee56faf8ff6ea8e0a4592568d9f68edc97e9e9/grpcio-1.76.0-cp313-cp313-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:3e2a27c89eb9ac3d81ec8835e12414d73536c6e620355d65102503064a4ed6eb", size = 7049990 }, + { url = "https://files.pythonhosted.org/packages/2b/94/8c12319a6369434e7a184b987e8e9f3b49a114c489b8315f029e24de4837/grpcio-1.76.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:61f69297cba3950a524f61c7c8ee12e55c486cb5f7db47ff9dcee33da6f0d3ae", size = 6575387 }, + { url = "https://files.pythonhosted.org/packages/15/0f/f12c32b03f731f4a6242f771f63039df182c8b8e2cf8075b245b409259d4/grpcio-1.76.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:6a15c17af8839b6801d554263c546c69c4d7718ad4321e3166175b37eaacca77", size = 7166668 }, + { url = "https://files.pythonhosted.org/packages/ff/2d/3ec9ce0c2b1d92dd59d1c3264aaec9f0f7c817d6e8ac683b97198a36ed5a/grpcio-1.76.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:25a18e9810fbc7e7f03ec2516addc116a957f8cbb8cbc95ccc80faa072743d03", size = 8124928 }, + { url = "https://files.pythonhosted.org/packages/1a/74/fd3317be5672f4856bcdd1a9e7b5e17554692d3db9a3b273879dc02d657d/grpcio-1.76.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:931091142fd8cc14edccc0845a79248bc155425eee9a98b2db2ea4f00a235a42", size = 7589983 }, + { url = "https://files.pythonhosted.org/packages/45/bb/ca038cf420f405971f19821c8c15bcbc875505f6ffadafe9ffd77871dc4c/grpcio-1.76.0-cp313-cp313-win32.whl", hash = "sha256:5e8571632780e08526f118f74170ad8d50fb0a48c23a746bef2a6ebade3abd6f", size = 3984727 }, + { url = "https://files.pythonhosted.org/packages/41/80/84087dc56437ced7cdd4b13d7875e7439a52a261e3ab4e06488ba6173b0a/grpcio-1.76.0-cp313-cp313-win_amd64.whl", hash = "sha256:f9f7bd5faab55f47231ad8dba7787866b69f5e93bc306e3915606779bbfb4ba8", size = 4702799 }, + { url = "https://files.pythonhosted.org/packages/b4/46/39adac80de49d678e6e073b70204091e76631e03e94928b9ea4ecf0f6e0e/grpcio-1.76.0-cp314-cp314-linux_armv7l.whl", hash = "sha256:ff8a59ea85a1f2191a0ffcc61298c571bc566332f82e5f5be1b83c9d8e668a62", size = 5808417 }, + { url = "https://files.pythonhosted.org/packages/9c/f5/a4531f7fb8b4e2a60b94e39d5d924469b7a6988176b3422487be61fe2998/grpcio-1.76.0-cp314-cp314-macosx_11_0_universal2.whl", hash = "sha256:06c3d6b076e7b593905d04fdba6a0525711b3466f43b3400266f04ff735de0cd", size = 11828219 }, + { url = "https://files.pythonhosted.org/packages/4b/1c/de55d868ed7a8bd6acc6b1d6ddc4aa36d07a9f31d33c912c804adb1b971b/grpcio-1.76.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:fd5ef5932f6475c436c4a55e4336ebbe47bd3272be04964a03d316bbf4afbcbc", size = 6367826 }, + { url = "https://files.pythonhosted.org/packages/59/64/99e44c02b5adb0ad13ab3adc89cb33cb54bfa90c74770f2607eea629b86f/grpcio-1.76.0-cp314-cp314-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:b331680e46239e090f5b3cead313cc772f6caa7d0fc8de349337563125361a4a", size = 7049550 }, + { url = "https://files.pythonhosted.org/packages/43/28/40a5be3f9a86949b83e7d6a2ad6011d993cbe9b6bd27bea881f61c7788b6/grpcio-1.76.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:2229ae655ec4e8999599469559e97630185fdd53ae1e8997d147b7c9b2b72cba", size = 6575564 }, + { url = "https://files.pythonhosted.org/packages/4b/a9/1be18e6055b64467440208a8559afac243c66a8b904213af6f392dc2212f/grpcio-1.76.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:490fa6d203992c47c7b9e4a9d39003a0c2bcc1c9aa3c058730884bbbb0ee9f09", size = 7176236 }, + { url = "https://files.pythonhosted.org/packages/0f/55/dba05d3fcc151ce6e81327541d2cc8394f442f6b350fead67401661bf041/grpcio-1.76.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:479496325ce554792dba6548fae3df31a72cef7bad71ca2e12b0e58f9b336bfc", size = 8125795 }, + { url = "https://files.pythonhosted.org/packages/4a/45/122df922d05655f63930cf42c9e3f72ba20aadb26c100ee105cad4ce4257/grpcio-1.76.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:1c9b93f79f48b03ada57ea24725d83a30284a012ec27eab2cf7e50a550cbbbcc", size = 7592214 }, + { url = "https://files.pythonhosted.org/packages/4a/6e/0b899b7f6b66e5af39e377055fb4a6675c9ee28431df5708139df2e93233/grpcio-1.76.0-cp314-cp314-win32.whl", hash = "sha256:747fa73efa9b8b1488a95d0ba1039c8e2dca0f741612d80415b1e1c560febf4e", size = 4062961 }, + { url = "https://files.pythonhosted.org/packages/19/41/0b430b01a2eb38ee887f88c1f07644a1df8e289353b78e82b37ef988fb64/grpcio-1.76.0-cp314-cp314-win_amd64.whl", hash = "sha256:922fa70ba549fce362d2e2871ab542082d66e2aaf0c19480ea453905b01f384e", size = 4834462 }, +] + +[[package]] +name = "h11" +version = "0.16.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/01/ee/02a2c011bdab74c6fb3c75474d40b3052059d95df7e73351460c8588d963/h11-0.16.0.tar.gz", hash = "sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1", size = 101250 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/04/4b/29cac41a4d98d144bf5f6d33995617b185d14b22401f75ca86f384e87ff1/h11-0.16.0-py3-none-any.whl", hash = "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86", size = 37515 }, +] + +[[package]] +name = "hf-xet" +version = "1.2.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/5e/6e/0f11bacf08a67f7fb5ee09740f2ca54163863b07b70d579356e9222ce5d8/hf_xet-1.2.0.tar.gz", hash = "sha256:a8c27070ca547293b6890c4bf389f713f80e8c478631432962bb7f4bc0bd7d7f", size = 506020 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9e/a5/85ef910a0aa034a2abcfadc360ab5ac6f6bc4e9112349bd40ca97551cff0/hf_xet-1.2.0-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:ceeefcd1b7aed4956ae8499e2199607765fbd1c60510752003b6cc0b8413b649", size = 2861870 }, + { url = "https://files.pythonhosted.org/packages/ea/40/e2e0a7eb9a51fe8828ba2d47fe22a7e74914ea8a0db68a18c3aa7449c767/hf_xet-1.2.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:b70218dd548e9840224df5638fdc94bd033552963cfa97f9170829381179c813", size = 2717584 }, + { url = "https://files.pythonhosted.org/packages/a5/7d/daf7f8bc4594fdd59a8a596f9e3886133fdc68e675292218a5e4c1b7e834/hf_xet-1.2.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7d40b18769bb9a8bc82a9ede575ce1a44c75eb80e7375a01d76259089529b5dc", size = 3315004 }, + { url = "https://files.pythonhosted.org/packages/b1/ba/45ea2f605fbf6d81c8b21e4d970b168b18a53515923010c312c06cd83164/hf_xet-1.2.0-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:cd3a6027d59cfb60177c12d6424e31f4b5ff13d8e3a1247b3a584bf8977e6df5", size = 3222636 }, + { url = "https://files.pythonhosted.org/packages/4a/1d/04513e3cab8f29ab8c109d309ddd21a2705afab9d52f2ba1151e0c14f086/hf_xet-1.2.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:6de1fc44f58f6dd937956c8d304d8c2dea264c80680bcfa61ca4a15e7b76780f", size = 3408448 }, + { url = "https://files.pythonhosted.org/packages/f0/7c/60a2756d7feec7387db3a1176c632357632fbe7849fce576c5559d4520c7/hf_xet-1.2.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:f182f264ed2acd566c514e45da9f2119110e48a87a327ca271027904c70c5832", size = 3503401 }, + { url = "https://files.pythonhosted.org/packages/4e/64/48fffbd67fb418ab07451e4ce641a70de1c40c10a13e25325e24858ebe5a/hf_xet-1.2.0-cp313-cp313t-win_amd64.whl", hash = "sha256:293a7a3787e5c95d7be1857358a9130694a9c6021de3f27fa233f37267174382", size = 2900866 }, + { url = "https://files.pythonhosted.org/packages/e2/51/f7e2caae42f80af886db414d4e9885fac959330509089f97cccb339c6b87/hf_xet-1.2.0-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:10bfab528b968c70e062607f663e21e34e2bba349e8038db546646875495179e", size = 2861861 }, + { url = "https://files.pythonhosted.org/packages/6e/1d/a641a88b69994f9371bd347f1dd35e5d1e2e2460a2e350c8d5165fc62005/hf_xet-1.2.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:2a212e842647b02eb6a911187dc878e79c4aa0aa397e88dd3b26761676e8c1f8", size = 2717699 }, + { url = "https://files.pythonhosted.org/packages/df/e0/e5e9bba7d15f0318955f7ec3f4af13f92e773fbb368c0b8008a5acbcb12f/hf_xet-1.2.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:30e06daccb3a7d4c065f34fc26c14c74f4653069bb2b194e7f18f17cbe9939c0", size = 3314885 }, + { url = "https://files.pythonhosted.org/packages/21/90/b7fe5ff6f2b7b8cbdf1bd56145f863c90a5807d9758a549bf3d916aa4dec/hf_xet-1.2.0-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:29c8fc913a529ec0a91867ce3d119ac1aac966e098cf49501800c870328cc090", size = 3221550 }, + { url = "https://files.pythonhosted.org/packages/6f/cb/73f276f0a7ce46cc6a6ec7d6c7d61cbfe5f2e107123d9bbd0193c355f106/hf_xet-1.2.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:66e159cbfcfbb29f920db2c09ed8b660eb894640d284f102ada929b6e3dc410a", size = 3408010 }, + { url = "https://files.pythonhosted.org/packages/b8/1e/d642a12caa78171f4be64f7cd9c40e3ca5279d055d0873188a58c0f5fbb9/hf_xet-1.2.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:9c91d5ae931510107f148874e9e2de8a16052b6f1b3ca3c1b12f15ccb491390f", size = 3503264 }, + { url = "https://files.pythonhosted.org/packages/17/b5/33764714923fa1ff922770f7ed18c2daae034d21ae6e10dbf4347c854154/hf_xet-1.2.0-cp314-cp314t-win_amd64.whl", hash = "sha256:210d577732b519ac6ede149d2f2f34049d44e8622bf14eb3d63bbcd2d4b332dc", size = 2901071 }, + { url = "https://files.pythonhosted.org/packages/96/2d/22338486473df5923a9ab7107d375dbef9173c338ebef5098ef593d2b560/hf_xet-1.2.0-cp37-abi3-macosx_10_12_x86_64.whl", hash = "sha256:46740d4ac024a7ca9b22bebf77460ff43332868b661186a8e46c227fdae01848", size = 2866099 }, + { url = "https://files.pythonhosted.org/packages/7f/8c/c5becfa53234299bc2210ba314eaaae36c2875e0045809b82e40a9544f0c/hf_xet-1.2.0-cp37-abi3-macosx_11_0_arm64.whl", hash = "sha256:27df617a076420d8845bea087f59303da8be17ed7ec0cd7ee3b9b9f579dff0e4", size = 2722178 }, + { url = "https://files.pythonhosted.org/packages/9a/92/cf3ab0b652b082e66876d08da57fcc6fa2f0e6c70dfbbafbd470bb73eb47/hf_xet-1.2.0-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3651fd5bfe0281951b988c0facbe726aa5e347b103a675f49a3fa8144c7968fd", size = 3320214 }, + { url = "https://files.pythonhosted.org/packages/46/92/3f7ec4a1b6a65bf45b059b6d4a5d38988f63e193056de2f420137e3c3244/hf_xet-1.2.0-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:d06fa97c8562fb3ee7a378dd9b51e343bc5bc8190254202c9771029152f5e08c", size = 3229054 }, + { url = "https://files.pythonhosted.org/packages/0b/dd/7ac658d54b9fb7999a0ccb07ad863b413cbaf5cf172f48ebcd9497ec7263/hf_xet-1.2.0-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:4c1428c9ae73ec0939410ec73023c4f842927f39db09b063b9482dac5a3bb737", size = 3413812 }, + { url = "https://files.pythonhosted.org/packages/92/68/89ac4e5b12a9ff6286a12174c8538a5930e2ed662091dd2572bbe0a18c8a/hf_xet-1.2.0-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:a55558084c16b09b5ed32ab9ed38421e2d87cf3f1f89815764d1177081b99865", size = 3508920 }, + { url = "https://files.pythonhosted.org/packages/cb/44/870d44b30e1dcfb6a65932e3e1506c103a8a5aea9103c337e7a53180322c/hf_xet-1.2.0-cp37-abi3-win_amd64.whl", hash = "sha256:e6584a52253f72c9f52f9e549d5895ca7a471608495c4ecaa6cc73dba2b24d69", size = 2905735 }, +] + +[[package]] +name = "httpcore" +version = "1.0.9" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "certifi" }, + { name = "h11" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/06/94/82699a10bca87a5556c9c59b5963f2d039dbd239f25bc2a63907a05a14cb/httpcore-1.0.9.tar.gz", hash = "sha256:6e34463af53fd2ab5d807f399a9b45ea31c3dfa2276f15a2c3f00afff6e176e8", size = 85484 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7e/f5/f66802a942d491edb555dd61e3a9961140fd64c90bce1eafd741609d334d/httpcore-1.0.9-py3-none-any.whl", hash = "sha256:2d400746a40668fc9dec9810239072b40b4484b640a8c38fd654a024c7a1bf55", size = 78784 }, +] + +[[package]] +name = "httpx" +version = "0.28.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio" }, + { name = "certifi" }, + { name = "httpcore" }, + { name = "idna" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b1/df/48c586a5fe32a0f01324ee087459e112ebb7224f646c0b5023f5e79e9956/httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc", size = 141406 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad", size = 73517 }, +] + +[[package]] +name = "huggingface-hub" +version = "1.3.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "filelock" }, + { name = "fsspec" }, + { name = "hf-xet", marker = "platform_machine == 'AMD64' or platform_machine == 'aarch64' or platform_machine == 'amd64' or platform_machine == 'arm64' or platform_machine == 'x86_64'" }, + { name = "httpx" }, + { name = "packaging" }, + { name = "pyyaml" }, + { name = "shellingham" }, + { name = "tqdm" }, + { name = "typer-slim" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ba/d6/02d1c505e1d3364230e5fa16d2b58c8f36a39c5efe8e99bc4d03d06fd0ca/huggingface_hub-1.3.2.tar.gz", hash = "sha256:15d7902e154f04174a0816d1e9594adcf15cdad57596920a5dc70fadb5d896c7", size = 624018 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/88/1d/acd3ef8aabb7813c6ef2f91785d855583ac5cd7c3599e5c1a1a2ed1ec2e5/huggingface_hub-1.3.2-py3-none-any.whl", hash = "sha256:b552b9562a5532102a041fa31a6966bb9de95138fc7aa578bb3703198c25d1b6", size = 534504 }, +] + +[[package]] +name = "idna" +version = "3.11" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/6f/6d/0703ccc57f3a7233505399edb88de3cbd678da106337b9fcde432b65ed60/idna-3.11.tar.gz", hash = "sha256:795dafcc9c04ed0c1fb032c2aa73654d8e8c5023a7df64a53f39190ada629902", size = 194582 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0e/61/66938bbb5fc52dbdf84594873d5b51fb1f7c7794e9c0f5bd885f30bc507b/idna-3.11-py3-none-any.whl", hash = "sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea", size = 71008 }, +] + +[[package]] +name = "importlib-metadata" +version = "8.7.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "zipp" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/f3/49/3b30cad09e7771a4982d9975a8cbf64f00d4a1ececb53297f1d9a7be1b10/importlib_metadata-8.7.1.tar.gz", hash = "sha256:49fef1ae6440c182052f407c8d34a68f72efc36db9ca90dc0113398f2fdde8bb", size = 57107 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fa/5e/f8e9a1d23b9c20a551a8a02ea3637b4642e22c2626e3a13a9a29cdea99eb/importlib_metadata-8.7.1-py3-none-any.whl", hash = "sha256:5a1f80bf1daa489495071efbb095d75a634cf28a8bc299581244063b53176151", size = 27865 }, +] + +[[package]] +name = "iniconfig" +version = "2.3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/72/34/14ca021ce8e5dfedc35312d08ba8bf51fdd999c576889fc2c24cb97f4f10/iniconfig-2.3.0.tar.gz", hash = "sha256:c76315c77db068650d49c5b56314774a7804df16fee4402c1f19d6d15d8c4730", size = 20503 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/cb/b1/3846dd7f199d53cb17f49cba7e651e9ce294d8497c8c150530ed11865bb8/iniconfig-2.3.0-py3-none-any.whl", hash = "sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12", size = 7484 }, +] + +[[package]] +name = "jinja2" +version = "3.1.6" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "markupsafe" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/df/bf/f7da0350254c0ed7c72f3e33cef02e048281fec7ecec5f032d4aac52226b/jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d", size = 245115 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67", size = 134899 }, +] + +[[package]] +name = "jiter" +version = "0.12.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/45/9d/e0660989c1370e25848bb4c52d061c71837239738ad937e83edca174c273/jiter-0.12.0.tar.gz", hash = "sha256:64dfcd7d5c168b38d3f9f8bba7fc639edb3418abcc74f22fdbe6b8938293f30b", size = 168294 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/32/f9/eaca4633486b527ebe7e681c431f529b63fe2709e7c5242fc0f43f77ce63/jiter-0.12.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:d8f8a7e317190b2c2d60eb2e8aa835270b008139562d70fe732e1c0020ec53c9", size = 316435 }, + { url = "https://files.pythonhosted.org/packages/10/c1/40c9f7c22f5e6ff715f28113ebaba27ab85f9af2660ad6e1dd6425d14c19/jiter-0.12.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2218228a077e784c6c8f1a8e5d6b8cb1dea62ce25811c356364848554b2056cd", size = 320548 }, + { url = "https://files.pythonhosted.org/packages/6b/1b/efbb68fe87e7711b00d2cfd1f26bb4bfc25a10539aefeaa7727329ffb9cb/jiter-0.12.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9354ccaa2982bf2188fd5f57f79f800ef622ec67beb8329903abf6b10da7d423", size = 351915 }, + { url = "https://files.pythonhosted.org/packages/15/2d/c06e659888c128ad1e838123d0638f0efad90cc30860cb5f74dd3f2fc0b3/jiter-0.12.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8f2607185ea89b4af9a604d4c7ec40e45d3ad03ee66998b031134bc510232bb7", size = 368966 }, + { url = "https://files.pythonhosted.org/packages/6b/20/058db4ae5fb07cf6a4ab2e9b9294416f606d8e467fb74c2184b2a1eeacba/jiter-0.12.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3a585a5e42d25f2e71db5f10b171f5e5ea641d3aa44f7df745aa965606111cc2", size = 482047 }, + { url = "https://files.pythonhosted.org/packages/49/bb/dc2b1c122275e1de2eb12905015d61e8316b2f888bdaac34221c301495d6/jiter-0.12.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bd9e21d34edff5a663c631f850edcb786719c960ce887a5661e9c828a53a95d9", size = 380835 }, + { url = "https://files.pythonhosted.org/packages/23/7d/38f9cd337575349de16da575ee57ddb2d5a64d425c9367f5ef9e4612e32e/jiter-0.12.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4a612534770470686cd5431478dc5a1b660eceb410abade6b1b74e320ca98de6", size = 364587 }, + { url = "https://files.pythonhosted.org/packages/f0/a3/b13e8e61e70f0bb06085099c4e2462647f53cc2ca97614f7fedcaa2bb9f3/jiter-0.12.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:3985aea37d40a908f887b34d05111e0aae822943796ebf8338877fee2ab67725", size = 390492 }, + { url = "https://files.pythonhosted.org/packages/07/71/e0d11422ed027e21422f7bc1883c61deba2d9752b720538430c1deadfbca/jiter-0.12.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:b1207af186495f48f72529f8d86671903c8c10127cac6381b11dddc4aaa52df6", size = 522046 }, + { url = "https://files.pythonhosted.org/packages/9f/59/b968a9aa7102a8375dbbdfbd2aeebe563c7e5dddf0f47c9ef1588a97e224/jiter-0.12.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:ef2fb241de583934c9915a33120ecc06d94aa3381a134570f59eed784e87001e", size = 513392 }, + { url = "https://files.pythonhosted.org/packages/ca/e4/7df62002499080dbd61b505c5cb351aa09e9959d176cac2aa8da6f93b13b/jiter-0.12.0-cp311-cp311-win32.whl", hash = "sha256:453b6035672fecce8007465896a25b28a6b59cfe8fbc974b2563a92f5a92a67c", size = 206096 }, + { url = "https://files.pythonhosted.org/packages/bb/60/1032b30ae0572196b0de0e87dce3b6c26a1eff71aad5fe43dee3082d32e0/jiter-0.12.0-cp311-cp311-win_amd64.whl", hash = "sha256:ca264b9603973c2ad9435c71a8ec8b49f8f715ab5ba421c85a51cde9887e421f", size = 204899 }, + { url = "https://files.pythonhosted.org/packages/49/d5/c145e526fccdb834063fb45c071df78b0cc426bbaf6de38b0781f45d956f/jiter-0.12.0-cp311-cp311-win_arm64.whl", hash = "sha256:cb00ef392e7d684f2754598c02c409f376ddcef857aae796d559e6cacc2d78a5", size = 188070 }, + { url = "https://files.pythonhosted.org/packages/92/c9/5b9f7b4983f1b542c64e84165075335e8a236fa9e2ea03a0c79780062be8/jiter-0.12.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:305e061fa82f4680607a775b2e8e0bcb071cd2205ac38e6ef48c8dd5ebe1cf37", size = 314449 }, + { url = "https://files.pythonhosted.org/packages/98/6e/e8efa0e78de00db0aee82c0cf9e8b3f2027efd7f8a71f859d8f4be8e98ef/jiter-0.12.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5c1860627048e302a528333c9307c818c547f214d8659b0705d2195e1a94b274", size = 319855 }, + { url = "https://files.pythonhosted.org/packages/20/26/894cd88e60b5d58af53bec5c6759d1292bd0b37a8b5f60f07abf7a63ae5f/jiter-0.12.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:df37577a4f8408f7e0ec3205d2a8f87672af8f17008358063a4d6425b6081ce3", size = 350171 }, + { url = "https://files.pythonhosted.org/packages/f5/27/a7b818b9979ac31b3763d25f3653ec3a954044d5e9f5d87f2f247d679fd1/jiter-0.12.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:75fdd787356c1c13a4f40b43c2156276ef7a71eb487d98472476476d803fb2cf", size = 365590 }, + { url = "https://files.pythonhosted.org/packages/ba/7e/e46195801a97673a83746170b17984aa8ac4a455746354516d02ca5541b4/jiter-0.12.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1eb5db8d9c65b112aacf14fcd0faae9913d07a8afea5ed06ccdd12b724e966a1", size = 479462 }, + { url = "https://files.pythonhosted.org/packages/ca/75/f833bfb009ab4bd11b1c9406d333e3b4357709ed0570bb48c7c06d78c7dd/jiter-0.12.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:73c568cc27c473f82480abc15d1301adf333a7ea4f2e813d6a2c7d8b6ba8d0df", size = 378983 }, + { url = "https://files.pythonhosted.org/packages/71/b3/7a69d77943cc837d30165643db753471aff5df39692d598da880a6e51c24/jiter-0.12.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4321e8a3d868919bcb1abb1db550d41f2b5b326f72df29e53b2df8b006eb9403", size = 361328 }, + { url = "https://files.pythonhosted.org/packages/b0/ac/a78f90caf48d65ba70d8c6efc6f23150bc39dc3389d65bbec2a95c7bc628/jiter-0.12.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:0a51bad79f8cc9cac2b4b705039f814049142e0050f30d91695a2d9a6611f126", size = 386740 }, + { url = "https://files.pythonhosted.org/packages/39/b6/5d31c2cc8e1b6a6bcf3c5721e4ca0a3633d1ab4754b09bc7084f6c4f5327/jiter-0.12.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:2a67b678f6a5f1dd6c36d642d7db83e456bc8b104788262aaefc11a22339f5a9", size = 520875 }, + { url = "https://files.pythonhosted.org/packages/30/b5/4df540fae4e9f68c54b8dab004bd8c943a752f0b00efd6e7d64aa3850339/jiter-0.12.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:efe1a211fe1fd14762adea941e3cfd6c611a136e28da6c39272dbb7a1bbe6a86", size = 511457 }, + { url = "https://files.pythonhosted.org/packages/07/65/86b74010e450a1a77b2c1aabb91d4a91dd3cd5afce99f34d75fd1ac64b19/jiter-0.12.0-cp312-cp312-win32.whl", hash = "sha256:d779d97c834b4278276ec703dc3fc1735fca50af63eb7262f05bdb4e62203d44", size = 204546 }, + { url = "https://files.pythonhosted.org/packages/1c/c7/6659f537f9562d963488e3e55573498a442503ced01f7e169e96a6110383/jiter-0.12.0-cp312-cp312-win_amd64.whl", hash = "sha256:e8269062060212b373316fe69236096aaf4c49022d267c6736eebd66bbbc60bb", size = 205196 }, + { url = "https://files.pythonhosted.org/packages/21/f4/935304f5169edadfec7f9c01eacbce4c90bb9a82035ac1de1f3bd2d40be6/jiter-0.12.0-cp312-cp312-win_arm64.whl", hash = "sha256:06cb970936c65de926d648af0ed3d21857f026b1cf5525cb2947aa5e01e05789", size = 186100 }, + { url = "https://files.pythonhosted.org/packages/3d/a6/97209693b177716e22576ee1161674d1d58029eb178e01866a0422b69224/jiter-0.12.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:6cc49d5130a14b732e0612bc76ae8db3b49898732223ef8b7599aa8d9810683e", size = 313658 }, + { url = "https://files.pythonhosted.org/packages/06/4d/125c5c1537c7d8ee73ad3d530a442d6c619714b95027143f1b61c0b4dfe0/jiter-0.12.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:37f27a32ce36364d2fa4f7fdc507279db604d27d239ea2e044c8f148410defe1", size = 318605 }, + { url = "https://files.pythonhosted.org/packages/99/bf/a840b89847885064c41a5f52de6e312e91fa84a520848ee56c97e4fa0205/jiter-0.12.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bbc0944aa3d4b4773e348cda635252824a78f4ba44328e042ef1ff3f6080d1cf", size = 349803 }, + { url = "https://files.pythonhosted.org/packages/8a/88/e63441c28e0db50e305ae23e19c1d8fae012d78ed55365da392c1f34b09c/jiter-0.12.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:da25c62d4ee1ffbacb97fac6dfe4dcd6759ebdc9015991e92a6eae5816287f44", size = 365120 }, + { url = "https://files.pythonhosted.org/packages/0a/7c/49b02714af4343970eb8aca63396bc1c82fa01197dbb1e9b0d274b550d4e/jiter-0.12.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:048485c654b838140b007390b8182ba9774621103bd4d77c9c3f6f117474ba45", size = 479918 }, + { url = "https://files.pythonhosted.org/packages/69/ba/0a809817fdd5a1db80490b9150645f3aae16afad166960bcd562be194f3b/jiter-0.12.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:635e737fbb7315bef0037c19b88b799143d2d7d3507e61a76751025226b3ac87", size = 379008 }, + { url = "https://files.pythonhosted.org/packages/5f/c3/c9fc0232e736c8877d9e6d83d6eeb0ba4e90c6c073835cc2e8f73fdeef51/jiter-0.12.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4e017c417b1ebda911bd13b1e40612704b1f5420e30695112efdbed8a4b389ed", size = 361785 }, + { url = "https://files.pythonhosted.org/packages/96/61/61f69b7e442e97ca6cd53086ddc1cf59fb830549bc72c0a293713a60c525/jiter-0.12.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:89b0bfb8b2bf2351fba36bb211ef8bfceba73ef58e7f0c68fb67b5a2795ca2f9", size = 386108 }, + { url = "https://files.pythonhosted.org/packages/e9/2e/76bb3332f28550c8f1eba3bf6e5efe211efda0ddbbaf24976bc7078d42a5/jiter-0.12.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:f5aa5427a629a824a543672778c9ce0c5e556550d1569bb6ea28a85015287626", size = 519937 }, + { url = "https://files.pythonhosted.org/packages/84/d6/fa96efa87dc8bff2094fb947f51f66368fa56d8d4fc9e77b25d7fbb23375/jiter-0.12.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:ed53b3d6acbcb0fd0b90f20c7cb3b24c357fe82a3518934d4edfa8c6898e498c", size = 510853 }, + { url = "https://files.pythonhosted.org/packages/8a/28/93f67fdb4d5904a708119a6ab58a8f1ec226ff10a94a282e0215402a8462/jiter-0.12.0-cp313-cp313-win32.whl", hash = "sha256:4747de73d6b8c78f2e253a2787930f4fffc68da7fa319739f57437f95963c4de", size = 204699 }, + { url = "https://files.pythonhosted.org/packages/c4/1f/30b0eb087045a0abe2a5c9c0c0c8da110875a1d3be83afd4a9a4e548be3c/jiter-0.12.0-cp313-cp313-win_amd64.whl", hash = "sha256:e25012eb0c456fcc13354255d0338cd5397cce26c77b2832b3c4e2e255ea5d9a", size = 204258 }, + { url = "https://files.pythonhosted.org/packages/2c/f4/2b4daf99b96bce6fc47971890b14b2a36aef88d7beb9f057fafa032c6141/jiter-0.12.0-cp313-cp313-win_arm64.whl", hash = "sha256:c97b92c54fe6110138c872add030a1f99aea2401ddcdaa21edf74705a646dd60", size = 185503 }, + { url = "https://files.pythonhosted.org/packages/39/ca/67bb15a7061d6fe20b9b2a2fd783e296a1e0f93468252c093481a2f00efa/jiter-0.12.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:53839b35a38f56b8be26a7851a48b89bc47e5d88e900929df10ed93b95fea3d6", size = 317965 }, + { url = "https://files.pythonhosted.org/packages/18/af/1788031cd22e29c3b14bc6ca80b16a39a0b10e611367ffd480c06a259831/jiter-0.12.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:94f669548e55c91ab47fef8bddd9c954dab1938644e715ea49d7e117015110a4", size = 345831 }, + { url = "https://files.pythonhosted.org/packages/05/17/710bf8472d1dff0d3caf4ced6031060091c1320f84ee7d5dcbed1f352417/jiter-0.12.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:351d54f2b09a41600ffea43d081522d792e81dcfb915f6d2d242744c1cc48beb", size = 361272 }, + { url = "https://files.pythonhosted.org/packages/fb/f1/1dcc4618b59761fef92d10bcbb0b038b5160be653b003651566a185f1a5c/jiter-0.12.0-cp313-cp313t-win_amd64.whl", hash = "sha256:2a5e90604620f94bf62264e7c2c038704d38217b7465b863896c6d7c902b06c7", size = 204604 }, + { url = "https://files.pythonhosted.org/packages/d9/32/63cb1d9f1c5c6632a783c0052cde9ef7ba82688f7065e2f0d5f10a7e3edb/jiter-0.12.0-cp313-cp313t-win_arm64.whl", hash = "sha256:88ef757017e78d2860f96250f9393b7b577b06a956ad102c29c8237554380db3", size = 185628 }, + { url = "https://files.pythonhosted.org/packages/a8/99/45c9f0dbe4a1416b2b9a8a6d1236459540f43d7fb8883cff769a8db0612d/jiter-0.12.0-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:c46d927acd09c67a9fb1416df45c5a04c27e83aae969267e98fba35b74e99525", size = 312478 }, + { url = "https://files.pythonhosted.org/packages/4c/a7/54ae75613ba9e0f55fcb0bc5d1f807823b5167cc944e9333ff322e9f07dd/jiter-0.12.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:774ff60b27a84a85b27b88cd5583899c59940bcc126caca97eb2a9df6aa00c49", size = 318706 }, + { url = "https://files.pythonhosted.org/packages/59/31/2aa241ad2c10774baf6c37f8b8e1f39c07db358f1329f4eb40eba179c2a2/jiter-0.12.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c5433fab222fb072237df3f637d01b81f040a07dcac1cb4a5c75c7aa9ed0bef1", size = 351894 }, + { url = "https://files.pythonhosted.org/packages/54/4f/0f2759522719133a9042781b18cc94e335b6d290f5e2d3e6899d6af933e3/jiter-0.12.0-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f8c593c6e71c07866ec6bfb790e202a833eeec885022296aff6b9e0b92d6a70e", size = 365714 }, + { url = "https://files.pythonhosted.org/packages/dc/6f/806b895f476582c62a2f52c453151edd8a0fde5411b0497baaa41018e878/jiter-0.12.0-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:90d32894d4c6877a87ae00c6b915b609406819dce8bc0d4e962e4de2784e567e", size = 478989 }, + { url = "https://files.pythonhosted.org/packages/86/6c/012d894dc6e1033acd8db2b8346add33e413ec1c7c002598915278a37f79/jiter-0.12.0-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:798e46eed9eb10c3adbbacbd3bdb5ecd4cf7064e453d00dbef08802dae6937ff", size = 378615 }, + { url = "https://files.pythonhosted.org/packages/87/30/d718d599f6700163e28e2c71c0bbaf6dace692e7df2592fd793ac9276717/jiter-0.12.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b3f1368f0a6719ea80013a4eb90ba72e75d7ea67cfc7846db2ca504f3df0169a", size = 364745 }, + { url = "https://files.pythonhosted.org/packages/8f/85/315b45ce4b6ddc7d7fceca24068543b02bdc8782942f4ee49d652e2cc89f/jiter-0.12.0-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:65f04a9d0b4406f7e51279710b27484af411896246200e461d80d3ba0caa901a", size = 386502 }, + { url = "https://files.pythonhosted.org/packages/74/0b/ce0434fb40c5b24b368fe81b17074d2840748b4952256bab451b72290a49/jiter-0.12.0-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:fd990541982a24281d12b67a335e44f117e4c6cbad3c3b75c7dea68bf4ce3a67", size = 519845 }, + { url = "https://files.pythonhosted.org/packages/e8/a3/7a7a4488ba052767846b9c916d208b3ed114e3eb670ee984e4c565b9cf0d/jiter-0.12.0-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:b111b0e9152fa7df870ecaebb0bd30240d9f7fff1f2003bcb4ed0f519941820b", size = 510701 }, + { url = "https://files.pythonhosted.org/packages/c3/16/052ffbf9d0467b70af24e30f91e0579e13ded0c17bb4a8eb2aed3cb60131/jiter-0.12.0-cp314-cp314-win32.whl", hash = "sha256:a78befb9cc0a45b5a5a0d537b06f8544c2ebb60d19d02c41ff15da28a9e22d42", size = 205029 }, + { url = "https://files.pythonhosted.org/packages/e4/18/3cf1f3f0ccc789f76b9a754bdb7a6977e5d1d671ee97a9e14f7eb728d80e/jiter-0.12.0-cp314-cp314-win_amd64.whl", hash = "sha256:e1fe01c082f6aafbe5c8faf0ff074f38dfb911d53f07ec333ca03f8f6226debf", size = 204960 }, + { url = "https://files.pythonhosted.org/packages/02/68/736821e52ecfdeeb0f024b8ab01b5a229f6b9293bbdb444c27efade50b0f/jiter-0.12.0-cp314-cp314-win_arm64.whl", hash = "sha256:d72f3b5a432a4c546ea4bedc84cce0c3404874f1d1676260b9c7f048a9855451", size = 185529 }, + { url = "https://files.pythonhosted.org/packages/30/61/12ed8ee7a643cce29ac97c2281f9ce3956eb76b037e88d290f4ed0d41480/jiter-0.12.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:e6ded41aeba3603f9728ed2b6196e4df875348ab97b28fc8afff115ed42ba7a7", size = 318974 }, + { url = "https://files.pythonhosted.org/packages/2d/c6/f3041ede6d0ed5e0e79ff0de4c8f14f401bbf196f2ef3971cdbe5fd08d1d/jiter-0.12.0-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a947920902420a6ada6ad51892082521978e9dd44a802663b001436e4b771684", size = 345932 }, + { url = "https://files.pythonhosted.org/packages/d5/5d/4d94835889edd01ad0e2dbfc05f7bdfaed46292e7b504a6ac7839aa00edb/jiter-0.12.0-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:add5e227e0554d3a52cf390a7635edaffdf4f8fce4fdbcef3cc2055bb396a30c", size = 367243 }, + { url = "https://files.pythonhosted.org/packages/fd/76/0051b0ac2816253a99d27baf3dda198663aff882fa6ea7deeb94046da24e/jiter-0.12.0-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3f9b1cda8fcb736250d7e8711d4580ebf004a46771432be0ae4796944b5dfa5d", size = 479315 }, + { url = "https://files.pythonhosted.org/packages/70/ae/83f793acd68e5cb24e483f44f482a1a15601848b9b6f199dacb970098f77/jiter-0.12.0-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:deeb12a2223fe0135c7ff1356a143d57f95bbf1f4a66584f1fc74df21d86b993", size = 380714 }, + { url = "https://files.pythonhosted.org/packages/b1/5e/4808a88338ad2c228b1126b93fcd8ba145e919e886fe910d578230dabe3b/jiter-0.12.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c596cc0f4cb574877550ce4ecd51f8037469146addd676d7c1a30ebe6391923f", size = 365168 }, + { url = "https://files.pythonhosted.org/packages/0c/d4/04619a9e8095b42aef436b5aeb4c0282b4ff1b27d1db1508df9f5dc82750/jiter-0.12.0-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:5ab4c823b216a4aeab3fdbf579c5843165756bd9ad87cc6b1c65919c4715f783", size = 387893 }, + { url = "https://files.pythonhosted.org/packages/17/ea/d3c7e62e4546fdc39197fa4a4315a563a89b95b6d54c0d25373842a59cbe/jiter-0.12.0-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:e427eee51149edf962203ff8db75a7514ab89be5cb623fb9cea1f20b54f1107b", size = 520828 }, + { url = "https://files.pythonhosted.org/packages/cc/0b/c6d3562a03fd767e31cb119d9041ea7958c3c80cb3d753eafb19b3b18349/jiter-0.12.0-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:edb868841f84c111255ba5e80339d386d937ec1fdce419518ce1bd9370fac5b6", size = 511009 }, + { url = "https://files.pythonhosted.org/packages/aa/51/2cb4468b3448a8385ebcd15059d325c9ce67df4e2758d133ab9442b19834/jiter-0.12.0-cp314-cp314t-win32.whl", hash = "sha256:8bbcfe2791dfdb7c5e48baf646d37a6a3dcb5a97a032017741dea9f817dca183", size = 205110 }, + { url = "https://files.pythonhosted.org/packages/b2/c5/ae5ec83dec9c2d1af805fd5fe8f74ebded9c8670c5210ec7820ce0dbeb1e/jiter-0.12.0-cp314-cp314t-win_amd64.whl", hash = "sha256:2fa940963bf02e1d8226027ef461e36af472dea85d36054ff835aeed944dd873", size = 205223 }, + { url = "https://files.pythonhosted.org/packages/97/9a/3c5391907277f0e55195550cf3fa8e293ae9ee0c00fb402fec1e38c0c82f/jiter-0.12.0-cp314-cp314t-win_arm64.whl", hash = "sha256:506c9708dd29b27288f9f8f1140c3cb0e3d8ddb045956d7757b1fa0e0f39a473", size = 185564 }, +] + +[[package]] +name = "jsonschema" +version = "4.26.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "attrs" }, + { name = "jsonschema-specifications" }, + { name = "referencing" }, + { name = "rpds-py" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b3/fc/e067678238fa451312d4c62bf6e6cf5ec56375422aee02f9cb5f909b3047/jsonschema-4.26.0.tar.gz", hash = "sha256:0c26707e2efad8aa1bfc5b7ce170f3fccc2e4918ff85989ba9ffa9facb2be326", size = 366583 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/69/90/f63fb5873511e014207a475e2bb4e8b2e570d655b00ac19a9a0ca0a385ee/jsonschema-4.26.0-py3-none-any.whl", hash = "sha256:d489f15263b8d200f8387e64b4c3a75f06629559fb73deb8fdfb525f2dab50ce", size = 90630 }, +] + +[[package]] +name = "jsonschema-specifications" +version = "2025.9.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "referencing" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/19/74/a633ee74eb36c44aa6d1095e7cc5569bebf04342ee146178e2d36600708b/jsonschema_specifications-2025.9.1.tar.gz", hash = "sha256:b540987f239e745613c7a9176f3edb72b832a4ac465cf02712288397832b5e8d", size = 32855 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/41/45/1a4ed80516f02155c51f51e8cedb3c1902296743db0bbc66608a0db2814f/jsonschema_specifications-2025.9.1-py3-none-any.whl", hash = "sha256:98802fee3a11ee76ecaca44429fda8a41bff98b00a0f2838151b113f210cc6fe", size = 18437 }, +] + +[[package]] +name = "litellm" +version = "1.81.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "aiohttp" }, + { name = "click" }, + { name = "fastuuid" }, + { name = "grpcio" }, + { name = "httpx" }, + { name = "importlib-metadata" }, + { name = "jinja2" }, + { name = "jsonschema" }, + { name = "openai" }, + { name = "pydantic" }, + { name = "python-dotenv" }, + { name = "tiktoken" }, + { name = "tokenizers" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c5/2b/299d54f95e02e9ed551c186e881a4ac0eaa5a948a6be93ecaa26a748f1be/litellm-1.81.1.tar.gz", hash = "sha256:9c758db8abff04a2f1f43582d042080e36f245fe34cfbafe2f8b7ca8f1de29b6", size = 13487469 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/59/73/8d100c4e48935f6a381df60f894ca9c063ea412ce354fbe7a17770ad4092/litellm-1.81.1-py3-none-any.whl", hash = "sha256:503512a8a7f3cddf9d8fed6182c14f1e77c5655635fe67b09efb09c75234bb87", size = 11795146 }, +] + +[[package]] +name = "markupsafe" +version = "3.0.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/7e/99/7690b6d4034fffd95959cbe0c02de8deb3098cc577c67bb6a24fe5d7caa7/markupsafe-3.0.3.tar.gz", hash = "sha256:722695808f4b6457b320fdc131280796bdceb04ab50fe1795cd540799ebe1698", size = 80313 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/08/db/fefacb2136439fc8dd20e797950e749aa1f4997ed584c62cfb8ef7c2be0e/markupsafe-3.0.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1cc7ea17a6824959616c525620e387f6dd30fec8cb44f649e31712db02123dad", size = 11631 }, + { url = "https://files.pythonhosted.org/packages/e1/2e/5898933336b61975ce9dc04decbc0a7f2fee78c30353c5efba7f2d6ff27a/markupsafe-3.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4bd4cd07944443f5a265608cc6aab442e4f74dff8088b0dfc8238647b8f6ae9a", size = 12058 }, + { url = "https://files.pythonhosted.org/packages/1d/09/adf2df3699d87d1d8184038df46a9c80d78c0148492323f4693df54e17bb/markupsafe-3.0.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6b5420a1d9450023228968e7e6a9ce57f65d148ab56d2313fcd589eee96a7a50", size = 24287 }, + { url = "https://files.pythonhosted.org/packages/30/ac/0273f6fcb5f42e314c6d8cd99effae6a5354604d461b8d392b5ec9530a54/markupsafe-3.0.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0bf2a864d67e76e5c9a34dc26ec616a66b9888e25e7b9460e1c76d3293bd9dbf", size = 22940 }, + { url = "https://files.pythonhosted.org/packages/19/ae/31c1be199ef767124c042c6c3e904da327a2f7f0cd63a0337e1eca2967a8/markupsafe-3.0.3-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:bc51efed119bc9cfdf792cdeaa4d67e8f6fcccab66ed4bfdd6bde3e59bfcbb2f", size = 21887 }, + { url = "https://files.pythonhosted.org/packages/b2/76/7edcab99d5349a4532a459e1fe64f0b0467a3365056ae550d3bcf3f79e1e/markupsafe-3.0.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:068f375c472b3e7acbe2d5318dea141359e6900156b5b2ba06a30b169086b91a", size = 23692 }, + { url = "https://files.pythonhosted.org/packages/a4/28/6e74cdd26d7514849143d69f0bf2399f929c37dc2b31e6829fd2045b2765/markupsafe-3.0.3-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:7be7b61bb172e1ed687f1754f8e7484f1c8019780f6f6b0786e76bb01c2ae115", size = 21471 }, + { url = "https://files.pythonhosted.org/packages/62/7e/a145f36a5c2945673e590850a6f8014318d5577ed7e5920a4b3448e0865d/markupsafe-3.0.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f9e130248f4462aaa8e2552d547f36ddadbeaa573879158d721bbd33dfe4743a", size = 22923 }, + { url = "https://files.pythonhosted.org/packages/0f/62/d9c46a7f5c9adbeeeda52f5b8d802e1094e9717705a645efc71b0913a0a8/markupsafe-3.0.3-cp311-cp311-win32.whl", hash = "sha256:0db14f5dafddbb6d9208827849fad01f1a2609380add406671a26386cdf15a19", size = 14572 }, + { url = "https://files.pythonhosted.org/packages/83/8a/4414c03d3f891739326e1783338e48fb49781cc915b2e0ee052aa490d586/markupsafe-3.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:de8a88e63464af587c950061a5e6a67d3632e36df62b986892331d4620a35c01", size = 15077 }, + { url = "https://files.pythonhosted.org/packages/35/73/893072b42e6862f319b5207adc9ae06070f095b358655f077f69a35601f0/markupsafe-3.0.3-cp311-cp311-win_arm64.whl", hash = "sha256:3b562dd9e9ea93f13d53989d23a7e775fdfd1066c33494ff43f5418bc8c58a5c", size = 13876 }, + { url = "https://files.pythonhosted.org/packages/5a/72/147da192e38635ada20e0a2e1a51cf8823d2119ce8883f7053879c2199b5/markupsafe-3.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d53197da72cc091b024dd97249dfc7794d6a56530370992a5e1a08983ad9230e", size = 11615 }, + { url = "https://files.pythonhosted.org/packages/9a/81/7e4e08678a1f98521201c3079f77db69fb552acd56067661f8c2f534a718/markupsafe-3.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1872df69a4de6aead3491198eaf13810b565bdbeec3ae2dc8780f14458ec73ce", size = 12020 }, + { url = "https://files.pythonhosted.org/packages/1e/2c/799f4742efc39633a1b54a92eec4082e4f815314869865d876824c257c1e/markupsafe-3.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3a7e8ae81ae39e62a41ec302f972ba6ae23a5c5396c8e60113e9066ef893da0d", size = 24332 }, + { url = "https://files.pythonhosted.org/packages/3c/2e/8d0c2ab90a8c1d9a24f0399058ab8519a3279d1bd4289511d74e909f060e/markupsafe-3.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d6dd0be5b5b189d31db7cda48b91d7e0a9795f31430b7f271219ab30f1d3ac9d", size = 22947 }, + { url = "https://files.pythonhosted.org/packages/2c/54/887f3092a85238093a0b2154bd629c89444f395618842e8b0c41783898ea/markupsafe-3.0.3-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:94c6f0bb423f739146aec64595853541634bde58b2135f27f61c1ffd1cd4d16a", size = 21962 }, + { url = "https://files.pythonhosted.org/packages/c9/2f/336b8c7b6f4a4d95e91119dc8521402461b74a485558d8f238a68312f11c/markupsafe-3.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:be8813b57049a7dc738189df53d69395eba14fb99345e0a5994914a3864c8a4b", size = 23760 }, + { url = "https://files.pythonhosted.org/packages/32/43/67935f2b7e4982ffb50a4d169b724d74b62a3964bc1a9a527f5ac4f1ee2b/markupsafe-3.0.3-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:83891d0e9fb81a825d9a6d61e3f07550ca70a076484292a70fde82c4b807286f", size = 21529 }, + { url = "https://files.pythonhosted.org/packages/89/e0/4486f11e51bbba8b0c041098859e869e304d1c261e59244baa3d295d47b7/markupsafe-3.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:77f0643abe7495da77fb436f50f8dab76dbc6e5fd25d39589a0f1fe6548bfa2b", size = 23015 }, + { url = "https://files.pythonhosted.org/packages/2f/e1/78ee7a023dac597a5825441ebd17170785a9dab23de95d2c7508ade94e0e/markupsafe-3.0.3-cp312-cp312-win32.whl", hash = "sha256:d88b440e37a16e651bda4c7c2b930eb586fd15ca7406cb39e211fcff3bf3017d", size = 14540 }, + { url = "https://files.pythonhosted.org/packages/aa/5b/bec5aa9bbbb2c946ca2733ef9c4ca91c91b6a24580193e891b5f7dbe8e1e/markupsafe-3.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:26a5784ded40c9e318cfc2bdb30fe164bdb8665ded9cd64d500a34fb42067b1c", size = 15105 }, + { url = "https://files.pythonhosted.org/packages/e5/f1/216fc1bbfd74011693a4fd837e7026152e89c4bcf3e77b6692fba9923123/markupsafe-3.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:35add3b638a5d900e807944a078b51922212fb3dedb01633a8defc4b01a3c85f", size = 13906 }, + { url = "https://files.pythonhosted.org/packages/38/2f/907b9c7bbba283e68f20259574b13d005c121a0fa4c175f9bed27c4597ff/markupsafe-3.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e1cf1972137e83c5d4c136c43ced9ac51d0e124706ee1c8aa8532c1287fa8795", size = 11622 }, + { url = "https://files.pythonhosted.org/packages/9c/d9/5f7756922cdd676869eca1c4e3c0cd0df60ed30199ffd775e319089cb3ed/markupsafe-3.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:116bb52f642a37c115f517494ea5feb03889e04df47eeff5b130b1808ce7c219", size = 12029 }, + { url = "https://files.pythonhosted.org/packages/00/07/575a68c754943058c78f30db02ee03a64b3c638586fba6a6dd56830b30a3/markupsafe-3.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:133a43e73a802c5562be9bbcd03d090aa5a1fe899db609c29e8c8d815c5f6de6", size = 24374 }, + { url = "https://files.pythonhosted.org/packages/a9/21/9b05698b46f218fc0e118e1f8168395c65c8a2c750ae2bab54fc4bd4e0e8/markupsafe-3.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ccfcd093f13f0f0b7fdd0f198b90053bf7b2f02a3927a30e63f3ccc9df56b676", size = 22980 }, + { url = "https://files.pythonhosted.org/packages/7f/71/544260864f893f18b6827315b988c146b559391e6e7e8f7252839b1b846a/markupsafe-3.0.3-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:509fa21c6deb7a7a273d629cf5ec029bc209d1a51178615ddf718f5918992ab9", size = 21990 }, + { url = "https://files.pythonhosted.org/packages/c2/28/b50fc2f74d1ad761af2f5dcce7492648b983d00a65b8c0e0cb457c82ebbe/markupsafe-3.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a4afe79fb3de0b7097d81da19090f4df4f8d3a2b3adaa8764138aac2e44f3af1", size = 23784 }, + { url = "https://files.pythonhosted.org/packages/ed/76/104b2aa106a208da8b17a2fb72e033a5a9d7073c68f7e508b94916ed47a9/markupsafe-3.0.3-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:795e7751525cae078558e679d646ae45574b47ed6e7771863fcc079a6171a0fc", size = 21588 }, + { url = "https://files.pythonhosted.org/packages/b5/99/16a5eb2d140087ebd97180d95249b00a03aa87e29cc224056274f2e45fd6/markupsafe-3.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8485f406a96febb5140bfeca44a73e3ce5116b2501ac54fe953e488fb1d03b12", size = 23041 }, + { url = "https://files.pythonhosted.org/packages/19/bc/e7140ed90c5d61d77cea142eed9f9c303f4c4806f60a1044c13e3f1471d0/markupsafe-3.0.3-cp313-cp313-win32.whl", hash = "sha256:bdd37121970bfd8be76c5fb069c7751683bdf373db1ed6c010162b2a130248ed", size = 14543 }, + { url = "https://files.pythonhosted.org/packages/05/73/c4abe620b841b6b791f2edc248f556900667a5a1cf023a6646967ae98335/markupsafe-3.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:9a1abfdc021a164803f4d485104931fb8f8c1efd55bc6b748d2f5774e78b62c5", size = 15113 }, + { url = "https://files.pythonhosted.org/packages/f0/3a/fa34a0f7cfef23cf9500d68cb7c32dd64ffd58a12b09225fb03dd37d5b80/markupsafe-3.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:7e68f88e5b8799aa49c85cd116c932a1ac15caaa3f5db09087854d218359e485", size = 13911 }, + { url = "https://files.pythonhosted.org/packages/e4/d7/e05cd7efe43a88a17a37b3ae96e79a19e846f3f456fe79c57ca61356ef01/markupsafe-3.0.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:218551f6df4868a8d527e3062d0fb968682fe92054e89978594c28e642c43a73", size = 11658 }, + { url = "https://files.pythonhosted.org/packages/99/9e/e412117548182ce2148bdeacdda3bb494260c0b0184360fe0d56389b523b/markupsafe-3.0.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:3524b778fe5cfb3452a09d31e7b5adefeea8c5be1d43c4f810ba09f2ceb29d37", size = 12066 }, + { url = "https://files.pythonhosted.org/packages/bc/e6/fa0ffcda717ef64a5108eaa7b4f5ed28d56122c9a6d70ab8b72f9f715c80/markupsafe-3.0.3-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4e885a3d1efa2eadc93c894a21770e4bc67899e3543680313b09f139e149ab19", size = 25639 }, + { url = "https://files.pythonhosted.org/packages/96/ec/2102e881fe9d25fc16cb4b25d5f5cde50970967ffa5dddafdb771237062d/markupsafe-3.0.3-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8709b08f4a89aa7586de0aadc8da56180242ee0ada3999749b183aa23df95025", size = 23569 }, + { url = "https://files.pythonhosted.org/packages/4b/30/6f2fce1f1f205fc9323255b216ca8a235b15860c34b6798f810f05828e32/markupsafe-3.0.3-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:b8512a91625c9b3da6f127803b166b629725e68af71f8184ae7e7d54686a56d6", size = 23284 }, + { url = "https://files.pythonhosted.org/packages/58/47/4a0ccea4ab9f5dcb6f79c0236d954acb382202721e704223a8aafa38b5c8/markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9b79b7a16f7fedff2495d684f2b59b0457c3b493778c9eed31111be64d58279f", size = 24801 }, + { url = "https://files.pythonhosted.org/packages/6a/70/3780e9b72180b6fecb83a4814d84c3bf4b4ae4bf0b19c27196104149734c/markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:12c63dfb4a98206f045aa9563db46507995f7ef6d83b2f68eda65c307c6829eb", size = 22769 }, + { url = "https://files.pythonhosted.org/packages/98/c5/c03c7f4125180fc215220c035beac6b9cb684bc7a067c84fc69414d315f5/markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:8f71bc33915be5186016f675cd83a1e08523649b0e33efdb898db577ef5bb009", size = 23642 }, + { url = "https://files.pythonhosted.org/packages/80/d6/2d1b89f6ca4bff1036499b1e29a1d02d282259f3681540e16563f27ebc23/markupsafe-3.0.3-cp313-cp313t-win32.whl", hash = "sha256:69c0b73548bc525c8cb9a251cddf1931d1db4d2258e9599c28c07ef3580ef354", size = 14612 }, + { url = "https://files.pythonhosted.org/packages/2b/98/e48a4bfba0a0ffcf9925fe2d69240bfaa19c6f7507b8cd09c70684a53c1e/markupsafe-3.0.3-cp313-cp313t-win_amd64.whl", hash = "sha256:1b4b79e8ebf6b55351f0d91fe80f893b4743f104bff22e90697db1590e47a218", size = 15200 }, + { url = "https://files.pythonhosted.org/packages/0e/72/e3cc540f351f316e9ed0f092757459afbc595824ca724cbc5a5d4263713f/markupsafe-3.0.3-cp313-cp313t-win_arm64.whl", hash = "sha256:ad2cf8aa28b8c020ab2fc8287b0f823d0a7d8630784c31e9ee5edea20f406287", size = 13973 }, + { url = "https://files.pythonhosted.org/packages/33/8a/8e42d4838cd89b7dde187011e97fe6c3af66d8c044997d2183fbd6d31352/markupsafe-3.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:eaa9599de571d72e2daf60164784109f19978b327a3910d3e9de8c97b5b70cfe", size = 11619 }, + { url = "https://files.pythonhosted.org/packages/b5/64/7660f8a4a8e53c924d0fa05dc3a55c9cee10bbd82b11c5afb27d44b096ce/markupsafe-3.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c47a551199eb8eb2121d4f0f15ae0f923d31350ab9280078d1e5f12b249e0026", size = 12029 }, + { url = "https://files.pythonhosted.org/packages/da/ef/e648bfd021127bef5fa12e1720ffed0c6cbb8310c8d9bea7266337ff06de/markupsafe-3.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f34c41761022dd093b4b6896d4810782ffbabe30f2d443ff5f083e0cbbb8c737", size = 24408 }, + { url = "https://files.pythonhosted.org/packages/41/3c/a36c2450754618e62008bf7435ccb0f88053e07592e6028a34776213d877/markupsafe-3.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:457a69a9577064c05a97c41f4e65148652db078a3a509039e64d3467b9e7ef97", size = 23005 }, + { url = "https://files.pythonhosted.org/packages/bc/20/b7fdf89a8456b099837cd1dc21974632a02a999ec9bf7ca3e490aacd98e7/markupsafe-3.0.3-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e8afc3f2ccfa24215f8cb28dcf43f0113ac3c37c2f0f0806d8c70e4228c5cf4d", size = 22048 }, + { url = "https://files.pythonhosted.org/packages/9a/a7/591f592afdc734f47db08a75793a55d7fbcc6902a723ae4cfbab61010cc5/markupsafe-3.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:ec15a59cf5af7be74194f7ab02d0f59a62bdcf1a537677ce67a2537c9b87fcda", size = 23821 }, + { url = "https://files.pythonhosted.org/packages/7d/33/45b24e4f44195b26521bc6f1a82197118f74df348556594bd2262bda1038/markupsafe-3.0.3-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:0eb9ff8191e8498cca014656ae6b8d61f39da5f95b488805da4bb029cccbfbaf", size = 21606 }, + { url = "https://files.pythonhosted.org/packages/ff/0e/53dfaca23a69fbfbbf17a4b64072090e70717344c52eaaaa9c5ddff1e5f0/markupsafe-3.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:2713baf880df847f2bece4230d4d094280f4e67b1e813eec43b4c0e144a34ffe", size = 23043 }, + { url = "https://files.pythonhosted.org/packages/46/11/f333a06fc16236d5238bfe74daccbca41459dcd8d1fa952e8fbd5dccfb70/markupsafe-3.0.3-cp314-cp314-win32.whl", hash = "sha256:729586769a26dbceff69f7a7dbbf59ab6572b99d94576a5592625d5b411576b9", size = 14747 }, + { url = "https://files.pythonhosted.org/packages/28/52/182836104b33b444e400b14f797212f720cbc9ed6ba34c800639d154e821/markupsafe-3.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:bdc919ead48f234740ad807933cdf545180bfbe9342c2bb451556db2ed958581", size = 15341 }, + { url = "https://files.pythonhosted.org/packages/6f/18/acf23e91bd94fd7b3031558b1f013adfa21a8e407a3fdb32745538730382/markupsafe-3.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:5a7d5dc5140555cf21a6fefbdbf8723f06fcd2f63ef108f2854de715e4422cb4", size = 14073 }, + { url = "https://files.pythonhosted.org/packages/3c/f0/57689aa4076e1b43b15fdfa646b04653969d50cf30c32a102762be2485da/markupsafe-3.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:1353ef0c1b138e1907ae78e2f6c63ff67501122006b0f9abad68fda5f4ffc6ab", size = 11661 }, + { url = "https://files.pythonhosted.org/packages/89/c3/2e67a7ca217c6912985ec766c6393b636fb0c2344443ff9d91404dc4c79f/markupsafe-3.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:1085e7fbddd3be5f89cc898938f42c0b3c711fdcb37d75221de2666af647c175", size = 12069 }, + { url = "https://files.pythonhosted.org/packages/f0/00/be561dce4e6ca66b15276e184ce4b8aec61fe83662cce2f7d72bd3249d28/markupsafe-3.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1b52b4fb9df4eb9ae465f8d0c228a00624de2334f216f178a995ccdcf82c4634", size = 25670 }, + { url = "https://files.pythonhosted.org/packages/50/09/c419f6f5a92e5fadde27efd190eca90f05e1261b10dbd8cbcb39cd8ea1dc/markupsafe-3.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fed51ac40f757d41b7c48425901843666a6677e3e8eb0abcff09e4ba6e664f50", size = 23598 }, + { url = "https://files.pythonhosted.org/packages/22/44/a0681611106e0b2921b3033fc19bc53323e0b50bc70cffdd19f7d679bb66/markupsafe-3.0.3-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f190daf01f13c72eac4efd5c430a8de82489d9cff23c364c3ea822545032993e", size = 23261 }, + { url = "https://files.pythonhosted.org/packages/5f/57/1b0b3f100259dc9fffe780cfb60d4be71375510e435efec3d116b6436d43/markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:e56b7d45a839a697b5eb268c82a71bd8c7f6c94d6fd50c3d577fa39a9f1409f5", size = 24835 }, + { url = "https://files.pythonhosted.org/packages/26/6a/4bf6d0c97c4920f1597cc14dd720705eca0bf7c787aebc6bb4d1bead5388/markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:f3e98bb3798ead92273dc0e5fd0f31ade220f59a266ffd8a4f6065e0a3ce0523", size = 22733 }, + { url = "https://files.pythonhosted.org/packages/14/c7/ca723101509b518797fedc2fdf79ba57f886b4aca8a7d31857ba3ee8281f/markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:5678211cb9333a6468fb8d8be0305520aa073f50d17f089b5b4b477ea6e67fdc", size = 23672 }, + { url = "https://files.pythonhosted.org/packages/fb/df/5bd7a48c256faecd1d36edc13133e51397e41b73bb77e1a69deab746ebac/markupsafe-3.0.3-cp314-cp314t-win32.whl", hash = "sha256:915c04ba3851909ce68ccc2b8e2cd691618c4dc4c4232fb7982bca3f41fd8c3d", size = 14819 }, + { url = "https://files.pythonhosted.org/packages/1a/8a/0402ba61a2f16038b48b39bccca271134be00c5c9f0f623208399333c448/markupsafe-3.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4faffd047e07c38848ce017e8725090413cd80cbc23d86e55c587bf979e579c9", size = 15426 }, + { url = "https://files.pythonhosted.org/packages/70/bc/6f1c2f612465f5fa89b95bead1f44dcb607670fd42891d8fdcd5d039f4f4/markupsafe-3.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:32001d6a8fc98c8cb5c947787c5d08b0a50663d139f1305bac5885d98d9b40fa", size = 14146 }, +] + +[[package]] +name = "multidict" +version = "6.7.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/80/1e/5492c365f222f907de1039b91f922b93fa4f764c713ee858d235495d8f50/multidict-6.7.0.tar.gz", hash = "sha256:c6e99d9a65ca282e578dfea819cfa9c0a62b2499d8677392e09feaf305e9e6f5", size = 101834 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/34/9e/5c727587644d67b2ed479041e4b1c58e30afc011e3d45d25bbe35781217c/multidict-6.7.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:4d409aa42a94c0b3fa617708ef5276dfe81012ba6753a0370fcc9d0195d0a1fc", size = 76604 }, + { url = "https://files.pythonhosted.org/packages/17/e4/67b5c27bd17c085a5ea8f1ec05b8a3e5cba0ca734bfcad5560fb129e70ca/multidict-6.7.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:14c9e076eede3b54c636f8ce1c9c252b5f057c62131211f0ceeec273810c9721", size = 44715 }, + { url = "https://files.pythonhosted.org/packages/4d/e1/866a5d77be6ea435711bef2a4291eed11032679b6b28b56b4776ab06ba3e/multidict-6.7.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4c09703000a9d0fa3c3404b27041e574cc7f4df4c6563873246d0e11812a94b6", size = 44332 }, + { url = "https://files.pythonhosted.org/packages/31/61/0c2d50241ada71ff61a79518db85ada85fdabfcf395d5968dae1cbda04e5/multidict-6.7.0-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:a265acbb7bb33a3a2d626afbe756371dce0279e7b17f4f4eda406459c2b5ff1c", size = 245212 }, + { url = "https://files.pythonhosted.org/packages/ac/e0/919666a4e4b57fff1b57f279be1c9316e6cdc5de8a8b525d76f6598fefc7/multidict-6.7.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:51cb455de290ae462593e5b1cb1118c5c22ea7f0d3620d9940bf695cea5a4bd7", size = 246671 }, + { url = "https://files.pythonhosted.org/packages/a1/cc/d027d9c5a520f3321b65adea289b965e7bcbd2c34402663f482648c716ce/multidict-6.7.0-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:db99677b4457c7a5c5a949353e125ba72d62b35f74e26da141530fbb012218a7", size = 225491 }, + { url = "https://files.pythonhosted.org/packages/75/c4/bbd633980ce6155a28ff04e6a6492dd3335858394d7bb752d8b108708558/multidict-6.7.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f470f68adc395e0183b92a2f4689264d1ea4b40504a24d9882c27375e6662bb9", size = 257322 }, + { url = "https://files.pythonhosted.org/packages/4c/6d/d622322d344f1f053eae47e033b0b3f965af01212de21b10bcf91be991fb/multidict-6.7.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:0db4956f82723cc1c270de9c6e799b4c341d327762ec78ef82bb962f79cc07d8", size = 254694 }, + { url = "https://files.pythonhosted.org/packages/a8/9f/78f8761c2705d4c6d7516faed63c0ebdac569f6db1bef95e0d5218fdc146/multidict-6.7.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3e56d780c238f9e1ae66a22d2adf8d16f485381878250db8d496623cd38b22bd", size = 246715 }, + { url = "https://files.pythonhosted.org/packages/78/59/950818e04f91b9c2b95aab3d923d9eabd01689d0dcd889563988e9ea0fd8/multidict-6.7.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:9d14baca2ee12c1a64740d4531356ba50b82543017f3ad6de0deb943c5979abb", size = 243189 }, + { url = "https://files.pythonhosted.org/packages/7a/3d/77c79e1934cad2ee74991840f8a0110966d9599b3af95964c0cd79bb905b/multidict-6.7.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:295a92a76188917c7f99cda95858c822f9e4aae5824246bba9b6b44004ddd0a6", size = 237845 }, + { url = "https://files.pythonhosted.org/packages/63/1b/834ce32a0a97a3b70f86437f685f880136677ac00d8bce0027e9fd9c2db7/multidict-6.7.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:39f1719f57adbb767ef592a50ae5ebb794220d1188f9ca93de471336401c34d2", size = 246374 }, + { url = "https://files.pythonhosted.org/packages/23/ef/43d1c3ba205b5dec93dc97f3fba179dfa47910fc73aaaea4f7ceb41cec2a/multidict-6.7.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:0a13fb8e748dfc94749f622de065dd5c1def7e0d2216dba72b1d8069a389c6ff", size = 253345 }, + { url = "https://files.pythonhosted.org/packages/6b/03/eaf95bcc2d19ead522001f6a650ef32811aa9e3624ff0ad37c445c7a588c/multidict-6.7.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:e3aa16de190d29a0ea1b48253c57d99a68492c8dd8948638073ab9e74dc9410b", size = 246940 }, + { url = "https://files.pythonhosted.org/packages/e8/df/ec8a5fd66ea6cd6f525b1fcbb23511b033c3e9bc42b81384834ffa484a62/multidict-6.7.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:a048ce45dcdaaf1defb76b2e684f997fb5abf74437b6cb7b22ddad934a964e34", size = 242229 }, + { url = "https://files.pythonhosted.org/packages/8a/a2/59b405d59fd39ec86d1142630e9049243015a5f5291ba49cadf3c090c541/multidict-6.7.0-cp311-cp311-win32.whl", hash = "sha256:a90af66facec4cebe4181b9e62a68be65e45ac9b52b67de9eec118701856e7ff", size = 41308 }, + { url = "https://files.pythonhosted.org/packages/32/0f/13228f26f8b882c34da36efa776c3b7348455ec383bab4a66390e42963ae/multidict-6.7.0-cp311-cp311-win_amd64.whl", hash = "sha256:95b5ffa4349df2887518bb839409bcf22caa72d82beec453216802f475b23c81", size = 46037 }, + { url = "https://files.pythonhosted.org/packages/84/1f/68588e31b000535a3207fd3c909ebeec4fb36b52c442107499c18a896a2a/multidict-6.7.0-cp311-cp311-win_arm64.whl", hash = "sha256:329aa225b085b6f004a4955271a7ba9f1087e39dcb7e65f6284a988264a63912", size = 43023 }, + { url = "https://files.pythonhosted.org/packages/c2/9e/9f61ac18d9c8b475889f32ccfa91c9f59363480613fc807b6e3023d6f60b/multidict-6.7.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:8a3862568a36d26e650a19bb5cbbba14b71789032aebc0423f8cc5f150730184", size = 76877 }, + { url = "https://files.pythonhosted.org/packages/38/6f/614f09a04e6184f8824268fce4bc925e9849edfa654ddd59f0b64508c595/multidict-6.7.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:960c60b5849b9b4f9dcc9bea6e3626143c252c74113df2c1540aebce70209b45", size = 45467 }, + { url = "https://files.pythonhosted.org/packages/b3/93/c4f67a436dd026f2e780c433277fff72be79152894d9fc36f44569cab1a6/multidict-6.7.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2049be98fb57a31b4ccf870bf377af2504d4ae35646a19037ec271e4c07998aa", size = 43834 }, + { url = "https://files.pythonhosted.org/packages/7f/f5/013798161ca665e4a422afbc5e2d9e4070142a9ff8905e482139cd09e4d0/multidict-6.7.0-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:0934f3843a1860dd465d38895c17fce1f1cb37295149ab05cd1b9a03afacb2a7", size = 250545 }, + { url = "https://files.pythonhosted.org/packages/71/2f/91dbac13e0ba94669ea5119ba267c9a832f0cb65419aca75549fcf09a3dc/multidict-6.7.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b3e34f3a1b8131ba06f1a73adab24f30934d148afcd5f5de9a73565a4404384e", size = 258305 }, + { url = "https://files.pythonhosted.org/packages/ef/b0/754038b26f6e04488b48ac621f779c341338d78503fb45403755af2df477/multidict-6.7.0-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:efbb54e98446892590dc2458c19c10344ee9a883a79b5cec4bc34d6656e8d546", size = 242363 }, + { url = "https://files.pythonhosted.org/packages/87/15/9da40b9336a7c9fa606c4cf2ed80a649dffeb42b905d4f63a1d7eb17d746/multidict-6.7.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a35c5fc61d4f51eb045061e7967cfe3123d622cd500e8868e7c0c592a09fedc4", size = 268375 }, + { url = "https://files.pythonhosted.org/packages/82/72/c53fcade0cc94dfaad583105fd92b3a783af2091eddcb41a6d5a52474000/multidict-6.7.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:29fe6740ebccba4175af1b9b87bf553e9c15cd5868ee967e010efcf94e4fd0f1", size = 269346 }, + { url = "https://files.pythonhosted.org/packages/0d/e2/9baffdae21a76f77ef8447f1a05a96ec4bc0a24dae08767abc0a2fe680b8/multidict-6.7.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:123e2a72e20537add2f33a79e605f6191fba2afda4cbb876e35c1a7074298a7d", size = 256107 }, + { url = "https://files.pythonhosted.org/packages/3c/06/3f06f611087dc60d65ef775f1fb5aca7c6d61c6db4990e7cda0cef9b1651/multidict-6.7.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:b284e319754366c1aee2267a2036248b24eeb17ecd5dc16022095e747f2f4304", size = 253592 }, + { url = "https://files.pythonhosted.org/packages/20/24/54e804ec7945b6023b340c412ce9c3f81e91b3bf5fa5ce65558740141bee/multidict-6.7.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:803d685de7be4303b5a657b76e2f6d1240e7e0a8aa2968ad5811fa2285553a12", size = 251024 }, + { url = "https://files.pythonhosted.org/packages/14/48/011cba467ea0b17ceb938315d219391d3e421dfd35928e5dbdc3f4ae76ef/multidict-6.7.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:c04a328260dfd5db8c39538f999f02779012268f54614902d0afc775d44e0a62", size = 251484 }, + { url = "https://files.pythonhosted.org/packages/0d/2f/919258b43bb35b99fa127435cfb2d91798eb3a943396631ef43e3720dcf4/multidict-6.7.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:8a19cdb57cd3df4cd865849d93ee14920fb97224300c88501f16ecfa2604b4e0", size = 263579 }, + { url = "https://files.pythonhosted.org/packages/31/22/a0e884d86b5242b5a74cf08e876bdf299e413016b66e55511f7a804a366e/multidict-6.7.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:9b2fd74c52accced7e75de26023b7dccee62511a600e62311b918ec5c168fc2a", size = 259654 }, + { url = "https://files.pythonhosted.org/packages/b2/e5/17e10e1b5c5f5a40f2fcbb45953c9b215f8a4098003915e46a93f5fcaa8f/multidict-6.7.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3e8bfdd0e487acf992407a140d2589fe598238eaeffa3da8448d63a63cd363f8", size = 251511 }, + { url = "https://files.pythonhosted.org/packages/e3/9a/201bb1e17e7af53139597069c375e7b0dcbd47594604f65c2d5359508566/multidict-6.7.0-cp312-cp312-win32.whl", hash = "sha256:dd32a49400a2c3d52088e120ee00c1e3576cbff7e10b98467962c74fdb762ed4", size = 41895 }, + { url = "https://files.pythonhosted.org/packages/46/e2/348cd32faad84eaf1d20cce80e2bb0ef8d312c55bca1f7fa9865e7770aaf/multidict-6.7.0-cp312-cp312-win_amd64.whl", hash = "sha256:92abb658ef2d7ef22ac9f8bb88e8b6c3e571671534e029359b6d9e845923eb1b", size = 46073 }, + { url = "https://files.pythonhosted.org/packages/25/ec/aad2613c1910dce907480e0c3aa306905830f25df2e54ccc9dea450cb5aa/multidict-6.7.0-cp312-cp312-win_arm64.whl", hash = "sha256:490dab541a6a642ce1a9d61a4781656b346a55c13038f0b1244653828e3a83ec", size = 43226 }, + { url = "https://files.pythonhosted.org/packages/d2/86/33272a544eeb36d66e4d9a920602d1a2f57d4ebea4ef3cdfe5a912574c95/multidict-6.7.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:bee7c0588aa0076ce77c0ea5d19a68d76ad81fcd9fe8501003b9a24f9d4000f6", size = 76135 }, + { url = "https://files.pythonhosted.org/packages/91/1c/eb97db117a1ebe46d457a3d235a7b9d2e6dcab174f42d1b67663dd9e5371/multidict-6.7.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:7ef6b61cad77091056ce0e7ce69814ef72afacb150b7ac6a3e9470def2198159", size = 45117 }, + { url = "https://files.pythonhosted.org/packages/f1/d8/6c3442322e41fb1dd4de8bd67bfd11cd72352ac131f6368315617de752f1/multidict-6.7.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:9c0359b1ec12b1d6849c59f9d319610b7f20ef990a6d454ab151aa0e3b9f78ca", size = 43472 }, + { url = "https://files.pythonhosted.org/packages/75/3f/e2639e80325af0b6c6febdf8e57cc07043ff15f57fa1ef808f4ccb5ac4cd/multidict-6.7.0-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:cd240939f71c64bd658f186330603aac1a9a81bf6273f523fca63673cb7378a8", size = 249342 }, + { url = "https://files.pythonhosted.org/packages/5d/cc/84e0585f805cbeaa9cbdaa95f9a3d6aed745b9d25700623ac89a6ecff400/multidict-6.7.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a60a4d75718a5efa473ebd5ab685786ba0c67b8381f781d1be14da49f1a2dc60", size = 257082 }, + { url = "https://files.pythonhosted.org/packages/b0/9c/ac851c107c92289acbbf5cfb485694084690c1b17e555f44952c26ddc5bd/multidict-6.7.0-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:53a42d364f323275126aff81fb67c5ca1b7a04fda0546245730a55c8c5f24bc4", size = 240704 }, + { url = "https://files.pythonhosted.org/packages/50/cc/5f93e99427248c09da95b62d64b25748a5f5c98c7c2ab09825a1d6af0e15/multidict-6.7.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:3b29b980d0ddbecb736735ee5bef69bb2ddca56eff603c86f3f29a1128299b4f", size = 266355 }, + { url = "https://files.pythonhosted.org/packages/ec/0c/2ec1d883ceb79c6f7f6d7ad90c919c898f5d1c6ea96d322751420211e072/multidict-6.7.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f8a93b1c0ed2d04b97a5e9336fd2d33371b9a6e29ab7dd6503d63407c20ffbaf", size = 267259 }, + { url = "https://files.pythonhosted.org/packages/c6/2d/f0b184fa88d6630aa267680bdb8623fb69cb0d024b8c6f0d23f9a0f406d3/multidict-6.7.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9ff96e8815eecacc6645da76c413eb3b3d34cfca256c70b16b286a687d013c32", size = 254903 }, + { url = "https://files.pythonhosted.org/packages/06/c9/11ea263ad0df7dfabcad404feb3c0dd40b131bc7f232d5537f2fb1356951/multidict-6.7.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:7516c579652f6a6be0e266aec0acd0db80829ca305c3d771ed898538804c2036", size = 252365 }, + { url = "https://files.pythonhosted.org/packages/41/88/d714b86ee2c17d6e09850c70c9d310abac3d808ab49dfa16b43aba9d53fd/multidict-6.7.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:040f393368e63fb0f3330e70c26bfd336656bed925e5cbe17c9da839a6ab13ec", size = 250062 }, + { url = "https://files.pythonhosted.org/packages/15/fe/ad407bb9e818c2b31383f6131ca19ea7e35ce93cf1310fce69f12e89de75/multidict-6.7.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:b3bc26a951007b1057a1c543af845f1c7e3e71cc240ed1ace7bf4484aa99196e", size = 249683 }, + { url = "https://files.pythonhosted.org/packages/8c/a4/a89abdb0229e533fb925e7c6e5c40201c2873efebc9abaf14046a4536ee6/multidict-6.7.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:7b022717c748dd1992a83e219587aabe45980d88969f01b316e78683e6285f64", size = 261254 }, + { url = "https://files.pythonhosted.org/packages/8d/aa/0e2b27bd88b40a4fb8dc53dd74eecac70edaa4c1dd0707eb2164da3675b3/multidict-6.7.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:9600082733859f00d79dee64effc7aef1beb26adb297416a4ad2116fd61374bd", size = 257967 }, + { url = "https://files.pythonhosted.org/packages/d0/8e/0c67b7120d5d5f6d874ed85a085f9dc770a7f9d8813e80f44a9fec820bb7/multidict-6.7.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:94218fcec4d72bc61df51c198d098ce2b378e0ccbac41ddbed5ef44092913288", size = 250085 }, + { url = "https://files.pythonhosted.org/packages/ba/55/b73e1d624ea4b8fd4dd07a3bb70f6e4c7c6c5d9d640a41c6ffe5cdbd2a55/multidict-6.7.0-cp313-cp313-win32.whl", hash = "sha256:a37bd74c3fa9d00be2d7b8eca074dc56bd8077ddd2917a839bd989612671ed17", size = 41713 }, + { url = "https://files.pythonhosted.org/packages/32/31/75c59e7d3b4205075b4c183fa4ca398a2daf2303ddf616b04ae6ef55cffe/multidict-6.7.0-cp313-cp313-win_amd64.whl", hash = "sha256:30d193c6cc6d559db42b6bcec8a5d395d34d60c9877a0b71ecd7c204fcf15390", size = 45915 }, + { url = "https://files.pythonhosted.org/packages/31/2a/8987831e811f1184c22bc2e45844934385363ee61c0a2dcfa8f71b87e608/multidict-6.7.0-cp313-cp313-win_arm64.whl", hash = "sha256:ea3334cabe4d41b7ccd01e4d349828678794edbc2d3ae97fc162a3312095092e", size = 43077 }, + { url = "https://files.pythonhosted.org/packages/e8/68/7b3a5170a382a340147337b300b9eb25a9ddb573bcdfff19c0fa3f31ffba/multidict-6.7.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:ad9ce259f50abd98a1ca0aa6e490b58c316a0fce0617f609723e40804add2c00", size = 83114 }, + { url = "https://files.pythonhosted.org/packages/55/5c/3fa2d07c84df4e302060f555bbf539310980362236ad49f50eeb0a1c1eb9/multidict-6.7.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:07f5594ac6d084cbb5de2df218d78baf55ef150b91f0ff8a21cc7a2e3a5a58eb", size = 48442 }, + { url = "https://files.pythonhosted.org/packages/fc/56/67212d33239797f9bd91962bb899d72bb0f4c35a8652dcdb8ed049bef878/multidict-6.7.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:0591b48acf279821a579282444814a2d8d0af624ae0bc600aa4d1b920b6e924b", size = 46885 }, + { url = "https://files.pythonhosted.org/packages/46/d1/908f896224290350721597a61a69cd19b89ad8ee0ae1f38b3f5cd12ea2ac/multidict-6.7.0-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:749a72584761531d2b9467cfbdfd29487ee21124c304c4b6cb760d8777b27f9c", size = 242588 }, + { url = "https://files.pythonhosted.org/packages/ab/67/8604288bbd68680eee0ab568fdcb56171d8b23a01bcd5cb0c8fedf6e5d99/multidict-6.7.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6b4c3d199f953acd5b446bf7c0de1fe25d94e09e79086f8dc2f48a11a129cdf1", size = 249966 }, + { url = "https://files.pythonhosted.org/packages/20/33/9228d76339f1ba51e3efef7da3ebd91964d3006217aae13211653193c3ff/multidict-6.7.0-cp313-cp313t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:9fb0211dfc3b51efea2f349ec92c114d7754dd62c01f81c3e32b765b70c45c9b", size = 228618 }, + { url = "https://files.pythonhosted.org/packages/f8/2d/25d9b566d10cab1c42b3b9e5b11ef79c9111eaf4463b8c257a3bd89e0ead/multidict-6.7.0-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a027ec240fe73a8d6281872690b988eed307cd7d91b23998ff35ff577ca688b5", size = 257539 }, + { url = "https://files.pythonhosted.org/packages/b6/b1/8d1a965e6637fc33de3c0d8f414485c2b7e4af00f42cab3d84e7b955c222/multidict-6.7.0-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d1d964afecdf3a8288789df2f5751dc0a8261138c3768d9af117ed384e538fad", size = 256345 }, + { url = "https://files.pythonhosted.org/packages/ba/0c/06b5a8adbdeedada6f4fb8d8f193d44a347223b11939b42953eeb6530b6b/multidict-6.7.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:caf53b15b1b7df9fbd0709aa01409000a2b4dd03a5f6f5cc548183c7c8f8b63c", size = 247934 }, + { url = "https://files.pythonhosted.org/packages/8f/31/b2491b5fe167ca044c6eb4b8f2c9f3b8a00b24c432c365358eadac5d7625/multidict-6.7.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:654030da3197d927f05a536a66186070e98765aa5142794c9904555d3a9d8fb5", size = 245243 }, + { url = "https://files.pythonhosted.org/packages/61/1a/982913957cb90406c8c94f53001abd9eafc271cb3e70ff6371590bec478e/multidict-6.7.0-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:2090d3718829d1e484706a2f525e50c892237b2bf9b17a79b059cb98cddc2f10", size = 235878 }, + { url = "https://files.pythonhosted.org/packages/be/c0/21435d804c1a1cf7a2608593f4d19bca5bcbd7a81a70b253fdd1c12af9c0/multidict-6.7.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:2d2cfeec3f6f45651b3d408c4acec0ebf3daa9bc8a112a084206f5db5d05b754", size = 243452 }, + { url = "https://files.pythonhosted.org/packages/54/0a/4349d540d4a883863191be6eb9a928846d4ec0ea007d3dcd36323bb058ac/multidict-6.7.0-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:4ef089f985b8c194d341eb2c24ae6e7408c9a0e2e5658699c92f497437d88c3c", size = 252312 }, + { url = "https://files.pythonhosted.org/packages/26/64/d5416038dbda1488daf16b676e4dbfd9674dde10a0cc8f4fc2b502d8125d/multidict-6.7.0-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:e93a0617cd16998784bf4414c7e40f17a35d2350e5c6f0bd900d3a8e02bd3762", size = 246935 }, + { url = "https://files.pythonhosted.org/packages/9f/8c/8290c50d14e49f35e0bd4abc25e1bc7711149ca9588ab7d04f886cdf03d9/multidict-6.7.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:f0feece2ef8ebc42ed9e2e8c78fc4aa3cf455733b507c09ef7406364c94376c6", size = 243385 }, + { url = "https://files.pythonhosted.org/packages/ef/a0/f83ae75e42d694b3fbad3e047670e511c138be747bc713cf1b10d5096416/multidict-6.7.0-cp313-cp313t-win32.whl", hash = "sha256:19a1d55338ec1be74ef62440ca9e04a2f001a04d0cc49a4983dc320ff0f3212d", size = 47777 }, + { url = "https://files.pythonhosted.org/packages/dc/80/9b174a92814a3830b7357307a792300f42c9e94664b01dee8e457551fa66/multidict-6.7.0-cp313-cp313t-win_amd64.whl", hash = "sha256:3da4fb467498df97e986af166b12d01f05d2e04f978a9c1c680ea1988e0bc4b6", size = 53104 }, + { url = "https://files.pythonhosted.org/packages/cc/28/04baeaf0428d95bb7a7bea0e691ba2f31394338ba424fb0679a9ed0f4c09/multidict-6.7.0-cp313-cp313t-win_arm64.whl", hash = "sha256:b4121773c49a0776461f4a904cdf6264c88e42218aaa8407e803ca8025872792", size = 45503 }, + { url = "https://files.pythonhosted.org/packages/e2/b1/3da6934455dd4b261d4c72f897e3a5728eba81db59959f3a639245891baa/multidict-6.7.0-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:3bab1e4aff7adaa34410f93b1f8e57c4b36b9af0426a76003f441ee1d3c7e842", size = 75128 }, + { url = "https://files.pythonhosted.org/packages/14/2c/f069cab5b51d175a1a2cb4ccdf7a2c2dabd58aa5bd933fa036a8d15e2404/multidict-6.7.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:b8512bac933afc3e45fb2b18da8e59b78d4f408399a960339598374d4ae3b56b", size = 44410 }, + { url = "https://files.pythonhosted.org/packages/42/e2/64bb41266427af6642b6b128e8774ed84c11b80a90702c13ac0a86bb10cc/multidict-6.7.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:79dcf9e477bc65414ebfea98ffd013cb39552b5ecd62908752e0e413d6d06e38", size = 43205 }, + { url = "https://files.pythonhosted.org/packages/02/68/6b086fef8a3f1a8541b9236c594f0c9245617c29841f2e0395d979485cde/multidict-6.7.0-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:31bae522710064b5cbeddaf2e9f32b1abab70ac6ac91d42572502299e9953128", size = 245084 }, + { url = "https://files.pythonhosted.org/packages/15/ee/f524093232007cd7a75c1d132df70f235cfd590a7c9eaccd7ff422ef4ae8/multidict-6.7.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4a0df7ff02397bb63e2fd22af2c87dfa39e8c7f12947bc524dbdc528282c7e34", size = 252667 }, + { url = "https://files.pythonhosted.org/packages/02/a5/eeb3f43ab45878f1895118c3ef157a480db58ede3f248e29b5354139c2c9/multidict-6.7.0-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:7a0222514e8e4c514660e182d5156a415c13ef0aabbd71682fc714e327b95e99", size = 233590 }, + { url = "https://files.pythonhosted.org/packages/6a/1e/76d02f8270b97269d7e3dbd45644b1785bda457b474315f8cf999525a193/multidict-6.7.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:2397ab4daaf2698eb51a76721e98db21ce4f52339e535725de03ea962b5a3202", size = 264112 }, + { url = "https://files.pythonhosted.org/packages/76/0b/c28a70ecb58963847c2a8efe334904cd254812b10e535aefb3bcce513918/multidict-6.7.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:8891681594162635948a636c9fe0ff21746aeb3dd5463f6e25d9bea3a8a39ca1", size = 261194 }, + { url = "https://files.pythonhosted.org/packages/b4/63/2ab26e4209773223159b83aa32721b4021ffb08102f8ac7d689c943fded1/multidict-6.7.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:18706cc31dbf402a7945916dd5cddf160251b6dab8a2c5f3d6d5a55949f676b3", size = 248510 }, + { url = "https://files.pythonhosted.org/packages/93/cd/06c1fa8282af1d1c46fd55c10a7930af652afdce43999501d4d68664170c/multidict-6.7.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:f844a1bbf1d207dd311a56f383f7eda2d0e134921d45751842d8235e7778965d", size = 248395 }, + { url = "https://files.pythonhosted.org/packages/99/ac/82cb419dd6b04ccf9e7e61befc00c77614fc8134362488b553402ecd55ce/multidict-6.7.0-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:d4393e3581e84e5645506923816b9cc81f5609a778c7e7534054091acc64d1c6", size = 239520 }, + { url = "https://files.pythonhosted.org/packages/fa/f3/a0f9bf09493421bd8716a362e0cd1d244f5a6550f5beffdd6b47e885b331/multidict-6.7.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:fbd18dc82d7bf274b37aa48d664534330af744e03bccf696d6f4c6042e7d19e7", size = 245479 }, + { url = "https://files.pythonhosted.org/packages/8d/01/476d38fc73a212843f43c852b0eee266b6971f0e28329c2184a8df90c376/multidict-6.7.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:b6234e14f9314731ec45c42fc4554b88133ad53a09092cc48a88e771c125dadb", size = 258903 }, + { url = "https://files.pythonhosted.org/packages/49/6d/23faeb0868adba613b817d0e69c5f15531b24d462af8012c4f6de4fa8dc3/multidict-6.7.0-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:08d4379f9744d8f78d98c8673c06e202ffa88296f009c71bbafe8a6bf847d01f", size = 252333 }, + { url = "https://files.pythonhosted.org/packages/1e/cc/48d02ac22b30fa247f7dad82866e4b1015431092f4ba6ebc7e77596e0b18/multidict-6.7.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:9fe04da3f79387f450fd0061d4dd2e45a72749d31bf634aecc9e27f24fdc4b3f", size = 243411 }, + { url = "https://files.pythonhosted.org/packages/4a/03/29a8bf5a18abf1fe34535c88adbdfa88c9fb869b5a3b120692c64abe8284/multidict-6.7.0-cp314-cp314-win32.whl", hash = "sha256:fbafe31d191dfa7c4c51f7a6149c9fb7e914dcf9ffead27dcfd9f1ae382b3885", size = 40940 }, + { url = "https://files.pythonhosted.org/packages/82/16/7ed27b680791b939de138f906d5cf2b4657b0d45ca6f5dd6236fdddafb1a/multidict-6.7.0-cp314-cp314-win_amd64.whl", hash = "sha256:2f67396ec0310764b9222a1728ced1ab638f61aadc6226f17a71dd9324f9a99c", size = 45087 }, + { url = "https://files.pythonhosted.org/packages/cd/3c/e3e62eb35a1950292fe39315d3c89941e30a9d07d5d2df42965ab041da43/multidict-6.7.0-cp314-cp314-win_arm64.whl", hash = "sha256:ba672b26069957ee369cfa7fc180dde1fc6f176eaf1e6beaf61fbebbd3d9c000", size = 42368 }, + { url = "https://files.pythonhosted.org/packages/8b/40/cd499bd0dbc5f1136726db3153042a735fffd0d77268e2ee20d5f33c010f/multidict-6.7.0-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:c1dcc7524066fa918c6a27d61444d4ee7900ec635779058571f70d042d86ed63", size = 82326 }, + { url = "https://files.pythonhosted.org/packages/13/8a/18e031eca251c8df76daf0288e6790561806e439f5ce99a170b4af30676b/multidict-6.7.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:27e0b36c2d388dc7b6ced3406671b401e84ad7eb0656b8f3a2f46ed0ce483718", size = 48065 }, + { url = "https://files.pythonhosted.org/packages/40/71/5e6701277470a87d234e433fb0a3a7deaf3bcd92566e421e7ae9776319de/multidict-6.7.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:2a7baa46a22e77f0988e3b23d4ede5513ebec1929e34ee9495be535662c0dfe2", size = 46475 }, + { url = "https://files.pythonhosted.org/packages/fe/6a/bab00cbab6d9cfb57afe1663318f72ec28289ea03fd4e8236bb78429893a/multidict-6.7.0-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:7bf77f54997a9166a2f5675d1201520586439424c2511723a7312bdb4bcc034e", size = 239324 }, + { url = "https://files.pythonhosted.org/packages/2a/5f/8de95f629fc22a7769ade8b41028e3e5a822c1f8904f618d175945a81ad3/multidict-6.7.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e011555abada53f1578d63389610ac8a5400fc70ce71156b0aa30d326f1a5064", size = 246877 }, + { url = "https://files.pythonhosted.org/packages/23/b4/38881a960458f25b89e9f4a4fdcb02ac101cfa710190db6e5528841e67de/multidict-6.7.0-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:28b37063541b897fd6a318007373930a75ca6d6ac7c940dbe14731ffdd8d498e", size = 225824 }, + { url = "https://files.pythonhosted.org/packages/1e/39/6566210c83f8a261575f18e7144736059f0c460b362e96e9cf797a24b8e7/multidict-6.7.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:05047ada7a2fde2631a0ed706f1fd68b169a681dfe5e4cf0f8e4cb6618bbc2cd", size = 253558 }, + { url = "https://files.pythonhosted.org/packages/00/a3/67f18315100f64c269f46e6c0319fa87ba68f0f64f2b8e7fd7c72b913a0b/multidict-6.7.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:716133f7d1d946a4e1b91b1756b23c088881e70ff180c24e864c26192ad7534a", size = 252339 }, + { url = "https://files.pythonhosted.org/packages/c8/2a/1cb77266afee2458d82f50da41beba02159b1d6b1f7973afc9a1cad1499b/multidict-6.7.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d1bed1b467ef657f2a0ae62844a607909ef1c6889562de5e1d505f74457d0b96", size = 244895 }, + { url = "https://files.pythonhosted.org/packages/dd/72/09fa7dd487f119b2eb9524946ddd36e2067c08510576d43ff68469563b3b/multidict-6.7.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:ca43bdfa5d37bd6aee89d85e1d0831fb86e25541be7e9d376ead1b28974f8e5e", size = 241862 }, + { url = "https://files.pythonhosted.org/packages/65/92/bc1f8bd0853d8669300f732c801974dfc3702c3eeadae2f60cef54dc69d7/multidict-6.7.0-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:44b546bd3eb645fd26fb949e43c02a25a2e632e2ca21a35e2e132c8105dc8599", size = 232376 }, + { url = "https://files.pythonhosted.org/packages/09/86/ac39399e5cb9d0c2ac8ef6e10a768e4d3bc933ac808d49c41f9dc23337eb/multidict-6.7.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:a6ef16328011d3f468e7ebc326f24c1445f001ca1dec335b2f8e66bed3006394", size = 240272 }, + { url = "https://files.pythonhosted.org/packages/3d/b6/fed5ac6b8563ec72df6cb1ea8dac6d17f0a4a1f65045f66b6d3bf1497c02/multidict-6.7.0-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:5aa873cbc8e593d361ae65c68f85faadd755c3295ea2c12040ee146802f23b38", size = 248774 }, + { url = "https://files.pythonhosted.org/packages/6b/8d/b954d8c0dc132b68f760aefd45870978deec6818897389dace00fcde32ff/multidict-6.7.0-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:3d7b6ccce016e29df4b7ca819659f516f0bc7a4b3efa3bb2012ba06431b044f9", size = 242731 }, + { url = "https://files.pythonhosted.org/packages/16/9d/a2dac7009125d3540c2f54e194829ea18ac53716c61b655d8ed300120b0f/multidict-6.7.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:171b73bd4ee683d307599b66793ac80981b06f069b62eea1c9e29c9241aa66b0", size = 240193 }, + { url = "https://files.pythonhosted.org/packages/39/ca/c05f144128ea232ae2178b008d5011d4e2cea86e4ee8c85c2631b1b94802/multidict-6.7.0-cp314-cp314t-win32.whl", hash = "sha256:b2d7f80c4e1fd010b07cb26820aae86b7e73b681ee4889684fb8d2d4537aab13", size = 48023 }, + { url = "https://files.pythonhosted.org/packages/ba/8f/0a60e501584145588be1af5cc829265701ba3c35a64aec8e07cbb71d39bb/multidict-6.7.0-cp314-cp314t-win_amd64.whl", hash = "sha256:09929cab6fcb68122776d575e03c6cc64ee0b8fca48d17e135474b042ce515cd", size = 53507 }, + { url = "https://files.pythonhosted.org/packages/7f/ae/3148b988a9c6239903e786eac19c889fab607c31d6efa7fb2147e5680f23/multidict-6.7.0-cp314-cp314t-win_arm64.whl", hash = "sha256:cc41db090ed742f32bd2d2c721861725e6109681eddf835d0a82bd3a5c382827", size = 44804 }, + { url = "https://files.pythonhosted.org/packages/b7/da/7d22601b625e241d4f23ef1ebff8acfc60da633c9e7e7922e24d10f592b3/multidict-6.7.0-py3-none-any.whl", hash = "sha256:394fc5c42a333c9ffc3e421a4c85e08580d990e08b99f6bf35b4132114c5dcb3", size = 12317 }, +] + +[[package]] +name = "openai" +version = "2.15.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio" }, + { name = "distro" }, + { name = "httpx" }, + { name = "jiter" }, + { name = "pydantic" }, + { name = "sniffio" }, + { name = "tqdm" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/94/f4/4690ecb5d70023ce6bfcfeabfe717020f654bde59a775058ec6ac4692463/openai-2.15.0.tar.gz", hash = "sha256:42eb8cbb407d84770633f31bf727d4ffb4138711c670565a41663d9439174fba", size = 627383 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b5/df/c306f7375d42bafb379934c2df4c2fa3964656c8c782bac75ee10c102818/openai-2.15.0-py3-none-any.whl", hash = "sha256:6ae23b932cd7230f7244e52954daa6602716d6b9bf235401a107af731baea6c3", size = 1067879 }, +] + +[[package]] +name = "packaging" +version = "26.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/65/ee/299d360cdc32edc7d2cf530f3accf79c4fca01e96ffc950d8a52213bd8e4/packaging-26.0.tar.gz", hash = "sha256:00243ae351a257117b6a241061796684b084ed1c516a08c48a3f7e147a9d80b4", size = 143416 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b7/b9/c538f279a4e237a006a2c98387d081e9eb060d203d8ed34467cc0f0b9b53/packaging-26.0-py3-none-any.whl", hash = "sha256:b36f1fef9334a5588b4166f8bcd26a14e521f2b55e6b9de3aaa80d3ff7a37529", size = 74366 }, +] + +[[package]] +name = "pluggy" +version = "1.6.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f9/e2/3e91f31a7d2b083fe6ef3fa267035b518369d9511ffab804f839851d2779/pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3", size = 69412 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746", size = 20538 }, +] + +[[package]] +name = "propcache" +version = "0.4.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/9e/da/e9fc233cf63743258bff22b3dfa7ea5baef7b5bc324af47a0ad89b8ffc6f/propcache-0.4.1.tar.gz", hash = "sha256:f48107a8c637e80362555f37ecf49abe20370e557cc4ab374f04ec4423c97c3d", size = 46442 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8c/d4/4e2c9aaf7ac2242b9358f98dccd8f90f2605402f5afeff6c578682c2c491/propcache-0.4.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:60a8fda9644b7dfd5dece8c61d8a85e271cb958075bfc4e01083c148b61a7caf", size = 80208 }, + { url = "https://files.pythonhosted.org/packages/c2/21/d7b68e911f9c8e18e4ae43bdbc1e1e9bbd971f8866eb81608947b6f585ff/propcache-0.4.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c30b53e7e6bda1d547cabb47c825f3843a0a1a42b0496087bb58d8fedf9f41b5", size = 45777 }, + { url = "https://files.pythonhosted.org/packages/d3/1d/11605e99ac8ea9435651ee71ab4cb4bf03f0949586246476a25aadfec54a/propcache-0.4.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6918ecbd897443087a3b7cd978d56546a812517dcaaca51b49526720571fa93e", size = 47647 }, + { url = "https://files.pythonhosted.org/packages/58/1a/3c62c127a8466c9c843bccb503d40a273e5cc69838805f322e2826509e0d/propcache-0.4.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3d902a36df4e5989763425a8ab9e98cd8ad5c52c823b34ee7ef307fd50582566", size = 214929 }, + { url = "https://files.pythonhosted.org/packages/56/b9/8fa98f850960b367c4b8fe0592e7fc341daa7a9462e925228f10a60cf74f/propcache-0.4.1-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a9695397f85973bb40427dedddf70d8dc4a44b22f1650dd4af9eedf443d45165", size = 221778 }, + { url = "https://files.pythonhosted.org/packages/46/a6/0ab4f660eb59649d14b3d3d65c439421cf2f87fe5dd68591cbe3c1e78a89/propcache-0.4.1-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:2bb07ffd7eaad486576430c89f9b215f9e4be68c4866a96e97db9e97fead85dc", size = 228144 }, + { url = "https://files.pythonhosted.org/packages/52/6a/57f43e054fb3d3a56ac9fc532bc684fc6169a26c75c353e65425b3e56eef/propcache-0.4.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fd6f30fdcf9ae2a70abd34da54f18da086160e4d7d9251f81f3da0ff84fc5a48", size = 210030 }, + { url = "https://files.pythonhosted.org/packages/40/e2/27e6feebb5f6b8408fa29f5efbb765cd54c153ac77314d27e457a3e993b7/propcache-0.4.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:fc38cba02d1acba4e2869eef1a57a43dfbd3d49a59bf90dda7444ec2be6a5570", size = 208252 }, + { url = "https://files.pythonhosted.org/packages/9e/f8/91c27b22ccda1dbc7967f921c42825564fa5336a01ecd72eb78a9f4f53c2/propcache-0.4.1-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:67fad6162281e80e882fb3ec355398cf72864a54069d060321f6cd0ade95fe85", size = 202064 }, + { url = "https://files.pythonhosted.org/packages/f2/26/7f00bd6bd1adba5aafe5f4a66390f243acab58eab24ff1a08bebb2ef9d40/propcache-0.4.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:f10207adf04d08bec185bae14d9606a1444715bc99180f9331c9c02093e1959e", size = 212429 }, + { url = "https://files.pythonhosted.org/packages/84/89/fd108ba7815c1117ddca79c228f3f8a15fc82a73bca8b142eb5de13b2785/propcache-0.4.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:e9b0d8d0845bbc4cfcdcbcdbf5086886bc8157aa963c31c777ceff7846c77757", size = 216727 }, + { url = "https://files.pythonhosted.org/packages/79/37/3ec3f7e3173e73f1d600495d8b545b53802cbf35506e5732dd8578db3724/propcache-0.4.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:981333cb2f4c1896a12f4ab92a9cc8f09ea664e9b7dbdc4eff74627af3a11c0f", size = 205097 }, + { url = "https://files.pythonhosted.org/packages/61/b0/b2631c19793f869d35f47d5a3a56fb19e9160d3c119f15ac7344fc3ccae7/propcache-0.4.1-cp311-cp311-win32.whl", hash = "sha256:f1d2f90aeec838a52f1c1a32fe9a619fefd5e411721a9117fbf82aea638fe8a1", size = 38084 }, + { url = "https://files.pythonhosted.org/packages/f4/78/6cce448e2098e9f3bfc91bb877f06aa24b6ccace872e39c53b2f707c4648/propcache-0.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:364426a62660f3f699949ac8c621aad6977be7126c5807ce48c0aeb8e7333ea6", size = 41637 }, + { url = "https://files.pythonhosted.org/packages/9c/e9/754f180cccd7f51a39913782c74717c581b9cc8177ad0e949f4d51812383/propcache-0.4.1-cp311-cp311-win_arm64.whl", hash = "sha256:e53f3a38d3510c11953f3e6a33f205c6d1b001129f972805ca9b42fc308bc239", size = 38064 }, + { url = "https://files.pythonhosted.org/packages/a2/0f/f17b1b2b221d5ca28b4b876e8bb046ac40466513960646bda8e1853cdfa2/propcache-0.4.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:e153e9cd40cc8945138822807139367f256f89c6810c2634a4f6902b52d3b4e2", size = 80061 }, + { url = "https://files.pythonhosted.org/packages/76/47/8ccf75935f51448ba9a16a71b783eb7ef6b9ee60f5d14c7f8a8a79fbeed7/propcache-0.4.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:cd547953428f7abb73c5ad82cbb32109566204260d98e41e5dfdc682eb7f8403", size = 46037 }, + { url = "https://files.pythonhosted.org/packages/0a/b6/5c9a0e42df4d00bfb4a3cbbe5cf9f54260300c88a0e9af1f47ca5ce17ac0/propcache-0.4.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f048da1b4f243fc44f205dfd320933a951b8d89e0afd4c7cacc762a8b9165207", size = 47324 }, + { url = "https://files.pythonhosted.org/packages/9e/d3/6c7ee328b39a81ee877c962469f1e795f9db87f925251efeb0545e0020d0/propcache-0.4.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ec17c65562a827bba85e3872ead335f95405ea1674860d96483a02f5c698fa72", size = 225505 }, + { url = "https://files.pythonhosted.org/packages/01/5d/1c53f4563490b1d06a684742cc6076ef944bc6457df6051b7d1a877c057b/propcache-0.4.1-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:405aac25c6394ef275dee4c709be43745d36674b223ba4eb7144bf4d691b7367", size = 230242 }, + { url = "https://files.pythonhosted.org/packages/20/e1/ce4620633b0e2422207c3cb774a0ee61cac13abc6217763a7b9e2e3f4a12/propcache-0.4.1-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:0013cb6f8dde4b2a2f66903b8ba740bdfe378c943c4377a200551ceb27f379e4", size = 238474 }, + { url = "https://files.pythonhosted.org/packages/46/4b/3aae6835b8e5f44ea6a68348ad90f78134047b503765087be2f9912140ea/propcache-0.4.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:15932ab57837c3368b024473a525e25d316d8353016e7cc0e5ba9eb343fbb1cf", size = 221575 }, + { url = "https://files.pythonhosted.org/packages/6e/a5/8a5e8678bcc9d3a1a15b9a29165640d64762d424a16af543f00629c87338/propcache-0.4.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:031dce78b9dc099f4c29785d9cf5577a3faf9ebf74ecbd3c856a7b92768c3df3", size = 216736 }, + { url = "https://files.pythonhosted.org/packages/f1/63/b7b215eddeac83ca1c6b934f89d09a625aa9ee4ba158338854c87210cc36/propcache-0.4.1-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:ab08df6c9a035bee56e31af99be621526bd237bea9f32def431c656b29e41778", size = 213019 }, + { url = "https://files.pythonhosted.org/packages/57/74/f580099a58c8af587cac7ba19ee7cb418506342fbbe2d4a4401661cca886/propcache-0.4.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:4d7af63f9f93fe593afbf104c21b3b15868efb2c21d07d8732c0c4287e66b6a6", size = 220376 }, + { url = "https://files.pythonhosted.org/packages/c4/ee/542f1313aff7eaf19c2bb758c5d0560d2683dac001a1c96d0774af799843/propcache-0.4.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:cfc27c945f422e8b5071b6e93169679e4eb5bf73bbcbf1ba3ae3a83d2f78ebd9", size = 226988 }, + { url = "https://files.pythonhosted.org/packages/8f/18/9c6b015dd9c6930f6ce2229e1f02fb35298b847f2087ea2b436a5bfa7287/propcache-0.4.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:35c3277624a080cc6ec6f847cbbbb5b49affa3598c4535a0a4682a697aaa5c75", size = 215615 }, + { url = "https://files.pythonhosted.org/packages/80/9e/e7b85720b98c45a45e1fca6a177024934dc9bc5f4d5dd04207f216fc33ed/propcache-0.4.1-cp312-cp312-win32.whl", hash = "sha256:671538c2262dadb5ba6395e26c1731e1d52534bfe9ae56d0b5573ce539266aa8", size = 38066 }, + { url = "https://files.pythonhosted.org/packages/54/09/d19cff2a5aaac632ec8fc03737b223597b1e347416934c1b3a7df079784c/propcache-0.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:cb2d222e72399fcf5890d1d5cc1060857b9b236adff2792ff48ca2dfd46c81db", size = 41655 }, + { url = "https://files.pythonhosted.org/packages/68/ab/6b5c191bb5de08036a8c697b265d4ca76148efb10fa162f14af14fb5f076/propcache-0.4.1-cp312-cp312-win_arm64.whl", hash = "sha256:204483131fb222bdaaeeea9f9e6c6ed0cac32731f75dfc1d4a567fc1926477c1", size = 37789 }, + { url = "https://files.pythonhosted.org/packages/bf/df/6d9c1b6ac12b003837dde8a10231a7344512186e87b36e855bef32241942/propcache-0.4.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:43eedf29202c08550aac1d14e0ee619b0430aaef78f85864c1a892294fbc28cf", size = 77750 }, + { url = "https://files.pythonhosted.org/packages/8b/e8/677a0025e8a2acf07d3418a2e7ba529c9c33caf09d3c1f25513023c1db56/propcache-0.4.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:d62cdfcfd89ccb8de04e0eda998535c406bf5e060ffd56be6c586cbcc05b3311", size = 44780 }, + { url = "https://files.pythonhosted.org/packages/89/a4/92380f7ca60f99ebae761936bc48a72a639e8a47b29050615eef757cb2a7/propcache-0.4.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:cae65ad55793da34db5f54e4029b89d3b9b9490d8abe1b4c7ab5d4b8ec7ebf74", size = 46308 }, + { url = "https://files.pythonhosted.org/packages/2d/48/c5ac64dee5262044348d1d78a5f85dd1a57464a60d30daee946699963eb3/propcache-0.4.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:333ddb9031d2704a301ee3e506dc46b1fe5f294ec198ed6435ad5b6a085facfe", size = 208182 }, + { url = "https://files.pythonhosted.org/packages/c6/0c/cd762dd011a9287389a6a3eb43aa30207bde253610cca06824aeabfe9653/propcache-0.4.1-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:fd0858c20f078a32cf55f7e81473d96dcf3b93fd2ccdb3d40fdf54b8573df3af", size = 211215 }, + { url = "https://files.pythonhosted.org/packages/30/3e/49861e90233ba36890ae0ca4c660e95df565b2cd15d4a68556ab5865974e/propcache-0.4.1-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:678ae89ebc632c5c204c794f8dab2837c5f159aeb59e6ed0539500400577298c", size = 218112 }, + { url = "https://files.pythonhosted.org/packages/f1/8b/544bc867e24e1bd48f3118cecd3b05c694e160a168478fa28770f22fd094/propcache-0.4.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d472aeb4fbf9865e0c6d622d7f4d54a4e101a89715d8904282bb5f9a2f476c3f", size = 204442 }, + { url = "https://files.pythonhosted.org/packages/50/a6/4282772fd016a76d3e5c0df58380a5ea64900afd836cec2c2f662d1b9bb3/propcache-0.4.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4d3df5fa7e36b3225954fba85589da77a0fe6a53e3976de39caf04a0db4c36f1", size = 199398 }, + { url = "https://files.pythonhosted.org/packages/3e/ec/d8a7cd406ee1ddb705db2139f8a10a8a427100347bd698e7014351c7af09/propcache-0.4.1-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:ee17f18d2498f2673e432faaa71698032b0127ebf23ae5974eeaf806c279df24", size = 196920 }, + { url = "https://files.pythonhosted.org/packages/f6/6c/f38ab64af3764f431e359f8baf9e0a21013e24329e8b85d2da32e8ed07ca/propcache-0.4.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:580e97762b950f993ae618e167e7be9256b8353c2dcd8b99ec100eb50f5286aa", size = 203748 }, + { url = "https://files.pythonhosted.org/packages/d6/e3/fa846bd70f6534d647886621388f0a265254d30e3ce47e5c8e6e27dbf153/propcache-0.4.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:501d20b891688eb8e7aa903021f0b72d5a55db40ffaab27edefd1027caaafa61", size = 205877 }, + { url = "https://files.pythonhosted.org/packages/e2/39/8163fc6f3133fea7b5f2827e8eba2029a0277ab2c5beee6c1db7b10fc23d/propcache-0.4.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9a0bd56e5b100aef69bd8562b74b46254e7c8812918d3baa700c8a8009b0af66", size = 199437 }, + { url = "https://files.pythonhosted.org/packages/93/89/caa9089970ca49c7c01662bd0eeedfe85494e863e8043565aeb6472ce8fe/propcache-0.4.1-cp313-cp313-win32.whl", hash = "sha256:bcc9aaa5d80322bc2fb24bb7accb4a30f81e90ab8d6ba187aec0744bc302ad81", size = 37586 }, + { url = "https://files.pythonhosted.org/packages/f5/ab/f76ec3c3627c883215b5c8080debb4394ef5a7a29be811f786415fc1e6fd/propcache-0.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:381914df18634f5494334d201e98245c0596067504b9372d8cf93f4bb23e025e", size = 40790 }, + { url = "https://files.pythonhosted.org/packages/59/1b/e71ae98235f8e2ba5004d8cb19765a74877abf189bc53fc0c80d799e56c3/propcache-0.4.1-cp313-cp313-win_arm64.whl", hash = "sha256:8873eb4460fd55333ea49b7d189749ecf6e55bf85080f11b1c4530ed3034cba1", size = 37158 }, + { url = "https://files.pythonhosted.org/packages/83/ce/a31bbdfc24ee0dcbba458c8175ed26089cf109a55bbe7b7640ed2470cfe9/propcache-0.4.1-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:92d1935ee1f8d7442da9c0c4fa7ac20d07e94064184811b685f5c4fada64553b", size = 81451 }, + { url = "https://files.pythonhosted.org/packages/25/9c/442a45a470a68456e710d96cacd3573ef26a1d0a60067e6a7d5e655621ed/propcache-0.4.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:473c61b39e1460d386479b9b2f337da492042447c9b685f28be4f74d3529e566", size = 46374 }, + { url = "https://files.pythonhosted.org/packages/f4/bf/b1d5e21dbc3b2e889ea4327044fb16312a736d97640fb8b6aa3f9c7b3b65/propcache-0.4.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:c0ef0aaafc66fbd87842a3fe3902fd889825646bc21149eafe47be6072725835", size = 48396 }, + { url = "https://files.pythonhosted.org/packages/f4/04/5b4c54a103d480e978d3c8a76073502b18db0c4bc17ab91b3cb5092ad949/propcache-0.4.1-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f95393b4d66bfae908c3ca8d169d5f79cd65636ae15b5e7a4f6e67af675adb0e", size = 275950 }, + { url = "https://files.pythonhosted.org/packages/b4/c1/86f846827fb969c4b78b0af79bba1d1ea2156492e1b83dea8b8a6ae27395/propcache-0.4.1-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c07fda85708bc48578467e85099645167a955ba093be0a2dcba962195676e859", size = 273856 }, + { url = "https://files.pythonhosted.org/packages/36/1d/fc272a63c8d3bbad6878c336c7a7dea15e8f2d23a544bda43205dfa83ada/propcache-0.4.1-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:af223b406d6d000830c6f65f1e6431783fc3f713ba3e6cc8c024d5ee96170a4b", size = 280420 }, + { url = "https://files.pythonhosted.org/packages/07/0c/01f2219d39f7e53d52e5173bcb09c976609ba30209912a0680adfb8c593a/propcache-0.4.1-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a78372c932c90ee474559c5ddfffd718238e8673c340dc21fe45c5b8b54559a0", size = 263254 }, + { url = "https://files.pythonhosted.org/packages/2d/18/cd28081658ce597898f0c4d174d4d0f3c5b6d4dc27ffafeef835c95eb359/propcache-0.4.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:564d9f0d4d9509e1a870c920a89b2fec951b44bf5ba7d537a9e7c1ccec2c18af", size = 261205 }, + { url = "https://files.pythonhosted.org/packages/7a/71/1f9e22eb8b8316701c2a19fa1f388c8a3185082607da8e406a803c9b954e/propcache-0.4.1-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:17612831fda0138059cc5546f4d12a2aacfb9e47068c06af35c400ba58ba7393", size = 247873 }, + { url = "https://files.pythonhosted.org/packages/4a/65/3d4b61f36af2b4eddba9def857959f1016a51066b4f1ce348e0cf7881f58/propcache-0.4.1-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:41a89040cb10bd345b3c1a873b2bf36413d48da1def52f268a055f7398514874", size = 262739 }, + { url = "https://files.pythonhosted.org/packages/2a/42/26746ab087faa77c1c68079b228810436ccd9a5ce9ac85e2b7307195fd06/propcache-0.4.1-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:e35b88984e7fa64aacecea39236cee32dd9bd8c55f57ba8a75cf2399553f9bd7", size = 263514 }, + { url = "https://files.pythonhosted.org/packages/94/13/630690fe201f5502d2403dd3cfd451ed8858fe3c738ee88d095ad2ff407b/propcache-0.4.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:6f8b465489f927b0df505cbe26ffbeed4d6d8a2bbc61ce90eb074ff129ef0ab1", size = 257781 }, + { url = "https://files.pythonhosted.org/packages/92/f7/1d4ec5841505f423469efbfc381d64b7b467438cd5a4bbcbb063f3b73d27/propcache-0.4.1-cp313-cp313t-win32.whl", hash = "sha256:2ad890caa1d928c7c2965b48f3a3815c853180831d0e5503d35cf00c472f4717", size = 41396 }, + { url = "https://files.pythonhosted.org/packages/48/f0/615c30622316496d2cbbc29f5985f7777d3ada70f23370608c1d3e081c1f/propcache-0.4.1-cp313-cp313t-win_amd64.whl", hash = "sha256:f7ee0e597f495cf415bcbd3da3caa3bd7e816b74d0d52b8145954c5e6fd3ff37", size = 44897 }, + { url = "https://files.pythonhosted.org/packages/fd/ca/6002e46eccbe0e33dcd4069ef32f7f1c9e243736e07adca37ae8c4830ec3/propcache-0.4.1-cp313-cp313t-win_arm64.whl", hash = "sha256:929d7cbe1f01bb7baffb33dc14eb5691c95831450a26354cd210a8155170c93a", size = 39789 }, + { url = "https://files.pythonhosted.org/packages/8e/5c/bca52d654a896f831b8256683457ceddd490ec18d9ec50e97dfd8fc726a8/propcache-0.4.1-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:3f7124c9d820ba5548d431afb4632301acf965db49e666aa21c305cbe8c6de12", size = 78152 }, + { url = "https://files.pythonhosted.org/packages/65/9b/03b04e7d82a5f54fb16113d839f5ea1ede58a61e90edf515f6577c66fa8f/propcache-0.4.1-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:c0d4b719b7da33599dfe3b22d3db1ef789210a0597bc650b7cee9c77c2be8c5c", size = 44869 }, + { url = "https://files.pythonhosted.org/packages/b2/fa/89a8ef0468d5833a23fff277b143d0573897cf75bd56670a6d28126c7d68/propcache-0.4.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:9f302f4783709a78240ebc311b793f123328716a60911d667e0c036bc5dcbded", size = 46596 }, + { url = "https://files.pythonhosted.org/packages/86/bd/47816020d337f4a746edc42fe8d53669965138f39ee117414c7d7a340cfe/propcache-0.4.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c80ee5802e3fb9ea37938e7eecc307fb984837091d5fd262bb37238b1ae97641", size = 206981 }, + { url = "https://files.pythonhosted.org/packages/df/f6/c5fa1357cc9748510ee55f37173eb31bfde6d94e98ccd9e6f033f2fc06e1/propcache-0.4.1-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ed5a841e8bb29a55fb8159ed526b26adc5bdd7e8bd7bf793ce647cb08656cdf4", size = 211490 }, + { url = "https://files.pythonhosted.org/packages/80/1e/e5889652a7c4a3846683401a48f0f2e5083ce0ec1a8a5221d8058fbd1adf/propcache-0.4.1-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:55c72fd6ea2da4c318e74ffdf93c4fe4e926051133657459131a95c846d16d44", size = 215371 }, + { url = "https://files.pythonhosted.org/packages/b2/f2/889ad4b2408f72fe1a4f6a19491177b30ea7bf1a0fd5f17050ca08cfc882/propcache-0.4.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8326e144341460402713f91df60ade3c999d601e7eb5ff8f6f7862d54de0610d", size = 201424 }, + { url = "https://files.pythonhosted.org/packages/27/73/033d63069b57b0812c8bd19f311faebeceb6ba31b8f32b73432d12a0b826/propcache-0.4.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:060b16ae65bc098da7f6d25bf359f1f31f688384858204fe5d652979e0015e5b", size = 197566 }, + { url = "https://files.pythonhosted.org/packages/dc/89/ce24f3dc182630b4e07aa6d15f0ff4b14ed4b9955fae95a0b54c58d66c05/propcache-0.4.1-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:89eb3fa9524f7bec9de6e83cf3faed9d79bffa560672c118a96a171a6f55831e", size = 193130 }, + { url = "https://files.pythonhosted.org/packages/a9/24/ef0d5fd1a811fb5c609278d0209c9f10c35f20581fcc16f818da959fc5b4/propcache-0.4.1-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:dee69d7015dc235f526fe80a9c90d65eb0039103fe565776250881731f06349f", size = 202625 }, + { url = "https://files.pythonhosted.org/packages/f5/02/98ec20ff5546f68d673df2f7a69e8c0d076b5abd05ca882dc7ee3a83653d/propcache-0.4.1-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:5558992a00dfd54ccbc64a32726a3357ec93825a418a401f5cc67df0ac5d9e49", size = 204209 }, + { url = "https://files.pythonhosted.org/packages/a0/87/492694f76759b15f0467a2a93ab68d32859672b646aa8a04ce4864e7932d/propcache-0.4.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:c9b822a577f560fbd9554812526831712c1436d2c046cedee4c3796d3543b144", size = 197797 }, + { url = "https://files.pythonhosted.org/packages/ee/36/66367de3575db1d2d3f3d177432bd14ee577a39d3f5d1b3d5df8afe3b6e2/propcache-0.4.1-cp314-cp314-win32.whl", hash = "sha256:ab4c29b49d560fe48b696cdcb127dd36e0bc2472548f3bf56cc5cb3da2b2984f", size = 38140 }, + { url = "https://files.pythonhosted.org/packages/0c/2a/a758b47de253636e1b8aef181c0b4f4f204bf0dd964914fb2af90a95b49b/propcache-0.4.1-cp314-cp314-win_amd64.whl", hash = "sha256:5a103c3eb905fcea0ab98be99c3a9a5ab2de60228aa5aceedc614c0281cf6153", size = 41257 }, + { url = "https://files.pythonhosted.org/packages/34/5e/63bd5896c3fec12edcbd6f12508d4890d23c265df28c74b175e1ef9f4f3b/propcache-0.4.1-cp314-cp314-win_arm64.whl", hash = "sha256:74c1fb26515153e482e00177a1ad654721bf9207da8a494a0c05e797ad27b992", size = 38097 }, + { url = "https://files.pythonhosted.org/packages/99/85/9ff785d787ccf9bbb3f3106f79884a130951436f58392000231b4c737c80/propcache-0.4.1-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:824e908bce90fb2743bd6b59db36eb4f45cd350a39637c9f73b1c1ea66f5b75f", size = 81455 }, + { url = "https://files.pythonhosted.org/packages/90/85/2431c10c8e7ddb1445c1f7c4b54d886e8ad20e3c6307e7218f05922cad67/propcache-0.4.1-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:c2b5e7db5328427c57c8e8831abda175421b709672f6cfc3d630c3b7e2146393", size = 46372 }, + { url = "https://files.pythonhosted.org/packages/01/20/b0972d902472da9bcb683fa595099911f4d2e86e5683bcc45de60dd05dc3/propcache-0.4.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:6f6ff873ed40292cd4969ef5310179afd5db59fdf055897e282485043fc80ad0", size = 48411 }, + { url = "https://files.pythonhosted.org/packages/e2/e3/7dc89f4f21e8f99bad3d5ddb3a3389afcf9da4ac69e3deb2dcdc96e74169/propcache-0.4.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:49a2dc67c154db2c1463013594c458881a069fcf98940e61a0569016a583020a", size = 275712 }, + { url = "https://files.pythonhosted.org/packages/20/67/89800c8352489b21a8047c773067644e3897f02ecbbd610f4d46b7f08612/propcache-0.4.1-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:005f08e6a0529984491e37d8dbc3dd86f84bd78a8ceb5fa9a021f4c48d4984be", size = 273557 }, + { url = "https://files.pythonhosted.org/packages/e2/a1/b52b055c766a54ce6d9c16d9aca0cad8059acd9637cdf8aa0222f4a026ef/propcache-0.4.1-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5c3310452e0d31390da9035c348633b43d7e7feb2e37be252be6da45abd1abcc", size = 280015 }, + { url = "https://files.pythonhosted.org/packages/48/c8/33cee30bd890672c63743049f3c9e4be087e6780906bfc3ec58528be59c1/propcache-0.4.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4c3c70630930447f9ef1caac7728c8ad1c56bc5015338b20fed0d08ea2480b3a", size = 262880 }, + { url = "https://files.pythonhosted.org/packages/0c/b1/8f08a143b204b418285c88b83d00edbd61afbc2c6415ffafc8905da7038b/propcache-0.4.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:8e57061305815dfc910a3634dcf584f08168a8836e6999983569f51a8544cd89", size = 260938 }, + { url = "https://files.pythonhosted.org/packages/cf/12/96e4664c82ca2f31e1c8dff86afb867348979eb78d3cb8546a680287a1e9/propcache-0.4.1-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:521a463429ef54143092c11a77e04056dd00636f72e8c45b70aaa3140d639726", size = 247641 }, + { url = "https://files.pythonhosted.org/packages/18/ed/e7a9cfca28133386ba52278136d42209d3125db08d0a6395f0cba0c0285c/propcache-0.4.1-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:120c964da3fdc75e3731aa392527136d4ad35868cc556fd09bb6d09172d9a367", size = 262510 }, + { url = "https://files.pythonhosted.org/packages/f5/76/16d8bf65e8845dd62b4e2b57444ab81f07f40caa5652b8969b87ddcf2ef6/propcache-0.4.1-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:d8f353eb14ee3441ee844ade4277d560cdd68288838673273b978e3d6d2c8f36", size = 263161 }, + { url = "https://files.pythonhosted.org/packages/e7/70/c99e9edb5d91d5ad8a49fa3c1e8285ba64f1476782fed10ab251ff413ba1/propcache-0.4.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:ab2943be7c652f09638800905ee1bab2c544e537edb57d527997a24c13dc1455", size = 257393 }, + { url = "https://files.pythonhosted.org/packages/08/02/87b25304249a35c0915d236575bc3574a323f60b47939a2262b77632a3ee/propcache-0.4.1-cp314-cp314t-win32.whl", hash = "sha256:05674a162469f31358c30bcaa8883cb7829fa3110bf9c0991fe27d7896c42d85", size = 42546 }, + { url = "https://files.pythonhosted.org/packages/cb/ef/3c6ecf8b317aa982f309835e8f96987466123c6e596646d4e6a1dfcd080f/propcache-0.4.1-cp314-cp314t-win_amd64.whl", hash = "sha256:990f6b3e2a27d683cb7602ed6c86f15ee6b43b1194736f9baaeb93d0016633b1", size = 46259 }, + { url = "https://files.pythonhosted.org/packages/c4/2d/346e946d4951f37eca1e4f55be0f0174c52cd70720f84029b02f296f4a38/propcache-0.4.1-cp314-cp314t-win_arm64.whl", hash = "sha256:ecef2343af4cc68e05131e45024ba34f6095821988a9d0a02aa7c73fcc448aa9", size = 40428 }, + { url = "https://files.pythonhosted.org/packages/5b/5a/bc7b4a4ef808fa59a816c17b20c4bef6884daebbdf627ff2a161da67da19/propcache-0.4.1-py3-none-any.whl", hash = "sha256:af2a6052aeb6cf17d3e46ee169099044fd8224cbaf75c76a2ef596e8163e2237", size = 13305 }, +] + +[[package]] +name = "pydantic" +version = "2.12.5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "annotated-types" }, + { name = "pydantic-core" }, + { name = "typing-extensions" }, + { name = "typing-inspection" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/69/44/36f1a6e523abc58ae5f928898e4aca2e0ea509b5aa6f6f392a5d882be928/pydantic-2.12.5.tar.gz", hash = "sha256:4d351024c75c0f085a9febbb665ce8c0c6ec5d30e903bdb6394b7ede26aebb49", size = 821591 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5a/87/b70ad306ebb6f9b585f114d0ac2137d792b48be34d732d60e597c2f8465a/pydantic-2.12.5-py3-none-any.whl", hash = "sha256:e561593fccf61e8a20fc46dfc2dfe075b8be7d0188df33f221ad1f0139180f9d", size = 463580 }, +] + +[[package]] +name = "pydantic-core" +version = "2.41.5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/71/70/23b021c950c2addd24ec408e9ab05d59b035b39d97cdc1130e1bce647bb6/pydantic_core-2.41.5.tar.gz", hash = "sha256:08daa51ea16ad373ffd5e7606252cc32f07bc72b28284b6bc9c6df804816476e", size = 460952 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e8/72/74a989dd9f2084b3d9530b0915fdda64ac48831c30dbf7c72a41a5232db8/pydantic_core-2.41.5-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:a3a52f6156e73e7ccb0f8cced536adccb7042be67cb45f9562e12b319c119da6", size = 2105873 }, + { url = "https://files.pythonhosted.org/packages/12/44/37e403fd9455708b3b942949e1d7febc02167662bf1a7da5b78ee1ea2842/pydantic_core-2.41.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7f3bf998340c6d4b0c9a2f02d6a400e51f123b59565d74dc60d252ce888c260b", size = 1899826 }, + { url = "https://files.pythonhosted.org/packages/33/7f/1d5cab3ccf44c1935a359d51a8a2a9e1a654b744b5e7f80d41b88d501eec/pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:378bec5c66998815d224c9ca994f1e14c0c21cb95d2f52b6021cc0b2a58f2a5a", size = 1917869 }, + { url = "https://files.pythonhosted.org/packages/6e/6a/30d94a9674a7fe4f4744052ed6c5e083424510be1e93da5bc47569d11810/pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e7b576130c69225432866fe2f4a469a85a54ade141d96fd396dffcf607b558f8", size = 2063890 }, + { url = "https://files.pythonhosted.org/packages/50/be/76e5d46203fcb2750e542f32e6c371ffa9b8ad17364cf94bb0818dbfb50c/pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6cb58b9c66f7e4179a2d5e0f849c48eff5c1fca560994d6eb6543abf955a149e", size = 2229740 }, + { url = "https://files.pythonhosted.org/packages/d3/ee/fed784df0144793489f87db310a6bbf8118d7b630ed07aa180d6067e653a/pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:88942d3a3dff3afc8288c21e565e476fc278902ae4d6d134f1eeda118cc830b1", size = 2350021 }, + { url = "https://files.pythonhosted.org/packages/c8/be/8fed28dd0a180dca19e72c233cbf58efa36df055e5b9d90d64fd1740b828/pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f31d95a179f8d64d90f6831d71fa93290893a33148d890ba15de25642c5d075b", size = 2066378 }, + { url = "https://files.pythonhosted.org/packages/b0/3b/698cf8ae1d536a010e05121b4958b1257f0b5522085e335360e53a6b1c8b/pydantic_core-2.41.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c1df3d34aced70add6f867a8cf413e299177e0c22660cc767218373d0779487b", size = 2175761 }, + { url = "https://files.pythonhosted.org/packages/b8/ba/15d537423939553116dea94ce02f9c31be0fa9d0b806d427e0308ec17145/pydantic_core-2.41.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:4009935984bd36bd2c774e13f9a09563ce8de4abaa7226f5108262fa3e637284", size = 2146303 }, + { url = "https://files.pythonhosted.org/packages/58/7f/0de669bf37d206723795f9c90c82966726a2ab06c336deba4735b55af431/pydantic_core-2.41.5-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:34a64bc3441dc1213096a20fe27e8e128bd3ff89921706e83c0b1ac971276594", size = 2340355 }, + { url = "https://files.pythonhosted.org/packages/e5/de/e7482c435b83d7e3c3ee5ee4451f6e8973cff0eb6007d2872ce6383f6398/pydantic_core-2.41.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:c9e19dd6e28fdcaa5a1de679aec4141f691023916427ef9bae8584f9c2fb3b0e", size = 2319875 }, + { url = "https://files.pythonhosted.org/packages/fe/e6/8c9e81bb6dd7560e33b9053351c29f30c8194b72f2d6932888581f503482/pydantic_core-2.41.5-cp311-cp311-win32.whl", hash = "sha256:2c010c6ded393148374c0f6f0bf89d206bf3217f201faa0635dcd56bd1520f6b", size = 1987549 }, + { url = "https://files.pythonhosted.org/packages/11/66/f14d1d978ea94d1bc21fc98fcf570f9542fe55bfcc40269d4e1a21c19bf7/pydantic_core-2.41.5-cp311-cp311-win_amd64.whl", hash = "sha256:76ee27c6e9c7f16f47db7a94157112a2f3a00e958bc626e2f4ee8bec5c328fbe", size = 2011305 }, + { url = "https://files.pythonhosted.org/packages/56/d8/0e271434e8efd03186c5386671328154ee349ff0354d83c74f5caaf096ed/pydantic_core-2.41.5-cp311-cp311-win_arm64.whl", hash = "sha256:4bc36bbc0b7584de96561184ad7f012478987882ebf9f9c389b23f432ea3d90f", size = 1972902 }, + { url = "https://files.pythonhosted.org/packages/5f/5d/5f6c63eebb5afee93bcaae4ce9a898f3373ca23df3ccaef086d0233a35a7/pydantic_core-2.41.5-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:f41a7489d32336dbf2199c8c0a215390a751c5b014c2c1c5366e817202e9cdf7", size = 2110990 }, + { url = "https://files.pythonhosted.org/packages/aa/32/9c2e8ccb57c01111e0fd091f236c7b371c1bccea0fa85247ac55b1e2b6b6/pydantic_core-2.41.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:070259a8818988b9a84a449a2a7337c7f430a22acc0859c6b110aa7212a6d9c0", size = 1896003 }, + { url = "https://files.pythonhosted.org/packages/68/b8/a01b53cb0e59139fbc9e4fda3e9724ede8de279097179be4ff31f1abb65a/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e96cea19e34778f8d59fe40775a7a574d95816eb150850a85a7a4c8f4b94ac69", size = 1919200 }, + { url = "https://files.pythonhosted.org/packages/38/de/8c36b5198a29bdaade07b5985e80a233a5ac27137846f3bc2d3b40a47360/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ed2e99c456e3fadd05c991f8f437ef902e00eedf34320ba2b0842bd1c3ca3a75", size = 2052578 }, + { url = "https://files.pythonhosted.org/packages/00/b5/0e8e4b5b081eac6cb3dbb7e60a65907549a1ce035a724368c330112adfdd/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:65840751b72fbfd82c3c640cff9284545342a4f1eb1586ad0636955b261b0b05", size = 2208504 }, + { url = "https://files.pythonhosted.org/packages/77/56/87a61aad59c7c5b9dc8caad5a41a5545cba3810c3e828708b3d7404f6cef/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e536c98a7626a98feb2d3eaf75944ef6f3dbee447e1f841eae16f2f0a72d8ddc", size = 2335816 }, + { url = "https://files.pythonhosted.org/packages/0d/76/941cc9f73529988688a665a5c0ecff1112b3d95ab48f81db5f7606f522d3/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eceb81a8d74f9267ef4081e246ffd6d129da5d87e37a77c9bde550cb04870c1c", size = 2075366 }, + { url = "https://files.pythonhosted.org/packages/d3/43/ebef01f69baa07a482844faaa0a591bad1ef129253ffd0cdaa9d8a7f72d3/pydantic_core-2.41.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d38548150c39b74aeeb0ce8ee1d8e82696f4a4e16ddc6de7b1d8823f7de4b9b5", size = 2171698 }, + { url = "https://files.pythonhosted.org/packages/b1/87/41f3202e4193e3bacfc2c065fab7706ebe81af46a83d3e27605029c1f5a6/pydantic_core-2.41.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:c23e27686783f60290e36827f9c626e63154b82b116d7fe9adba1fda36da706c", size = 2132603 }, + { url = "https://files.pythonhosted.org/packages/49/7d/4c00df99cb12070b6bccdef4a195255e6020a550d572768d92cc54dba91a/pydantic_core-2.41.5-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:482c982f814460eabe1d3bb0adfdc583387bd4691ef00b90575ca0d2b6fe2294", size = 2329591 }, + { url = "https://files.pythonhosted.org/packages/cc/6a/ebf4b1d65d458f3cda6a7335d141305dfa19bdc61140a884d165a8a1bbc7/pydantic_core-2.41.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:bfea2a5f0b4d8d43adf9d7b8bf019fb46fdd10a2e5cde477fbcb9d1fa08c68e1", size = 2319068 }, + { url = "https://files.pythonhosted.org/packages/49/3b/774f2b5cd4192d5ab75870ce4381fd89cf218af999515baf07e7206753f0/pydantic_core-2.41.5-cp312-cp312-win32.whl", hash = "sha256:b74557b16e390ec12dca509bce9264c3bbd128f8a2c376eaa68003d7f327276d", size = 1985908 }, + { url = "https://files.pythonhosted.org/packages/86/45/00173a033c801cacf67c190fef088789394feaf88a98a7035b0e40d53dc9/pydantic_core-2.41.5-cp312-cp312-win_amd64.whl", hash = "sha256:1962293292865bca8e54702b08a4f26da73adc83dd1fcf26fbc875b35d81c815", size = 2020145 }, + { url = "https://files.pythonhosted.org/packages/f9/22/91fbc821fa6d261b376a3f73809f907cec5ca6025642c463d3488aad22fb/pydantic_core-2.41.5-cp312-cp312-win_arm64.whl", hash = "sha256:1746d4a3d9a794cacae06a5eaaccb4b8643a131d45fbc9af23e353dc0a5ba5c3", size = 1976179 }, + { url = "https://files.pythonhosted.org/packages/87/06/8806241ff1f70d9939f9af039c6c35f2360cf16e93c2ca76f184e76b1564/pydantic_core-2.41.5-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:941103c9be18ac8daf7b7adca8228f8ed6bb7a1849020f643b3a14d15b1924d9", size = 2120403 }, + { url = "https://files.pythonhosted.org/packages/94/02/abfa0e0bda67faa65fef1c84971c7e45928e108fe24333c81f3bfe35d5f5/pydantic_core-2.41.5-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:112e305c3314f40c93998e567879e887a3160bb8689ef3d2c04b6cc62c33ac34", size = 1896206 }, + { url = "https://files.pythonhosted.org/packages/15/df/a4c740c0943e93e6500f9eb23f4ca7ec9bf71b19e608ae5b579678c8d02f/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0cbaad15cb0c90aa221d43c00e77bb33c93e8d36e0bf74760cd00e732d10a6a0", size = 1919307 }, + { url = "https://files.pythonhosted.org/packages/9a/e3/6324802931ae1d123528988e0e86587c2072ac2e5394b4bc2bc34b61ff6e/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:03ca43e12fab6023fc79d28ca6b39b05f794ad08ec2feccc59a339b02f2b3d33", size = 2063258 }, + { url = "https://files.pythonhosted.org/packages/c9/d4/2230d7151d4957dd79c3044ea26346c148c98fbf0ee6ebd41056f2d62ab5/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dc799088c08fa04e43144b164feb0c13f9a0bc40503f8df3e9fde58a3c0c101e", size = 2214917 }, + { url = "https://files.pythonhosted.org/packages/e6/9f/eaac5df17a3672fef0081b6c1bb0b82b33ee89aa5cec0d7b05f52fd4a1fa/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:97aeba56665b4c3235a0e52b2c2f5ae9cd071b8a8310ad27bddb3f7fb30e9aa2", size = 2332186 }, + { url = "https://files.pythonhosted.org/packages/cf/4e/35a80cae583a37cf15604b44240e45c05e04e86f9cfd766623149297e971/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:406bf18d345822d6c21366031003612b9c77b3e29ffdb0f612367352aab7d586", size = 2073164 }, + { url = "https://files.pythonhosted.org/packages/bf/e3/f6e262673c6140dd3305d144d032f7bd5f7497d3871c1428521f19f9efa2/pydantic_core-2.41.5-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b93590ae81f7010dbe380cdeab6f515902ebcbefe0b9327cc4804d74e93ae69d", size = 2179146 }, + { url = "https://files.pythonhosted.org/packages/75/c7/20bd7fc05f0c6ea2056a4565c6f36f8968c0924f19b7d97bbfea55780e73/pydantic_core-2.41.5-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:01a3d0ab748ee531f4ea6c3e48ad9dac84ddba4b0d82291f87248f2f9de8d740", size = 2137788 }, + { url = "https://files.pythonhosted.org/packages/3a/8d/34318ef985c45196e004bc46c6eab2eda437e744c124ef0dbe1ff2c9d06b/pydantic_core-2.41.5-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:6561e94ba9dacc9c61bce40e2d6bdc3bfaa0259d3ff36ace3b1e6901936d2e3e", size = 2340133 }, + { url = "https://files.pythonhosted.org/packages/9c/59/013626bf8c78a5a5d9350d12e7697d3d4de951a75565496abd40ccd46bee/pydantic_core-2.41.5-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:915c3d10f81bec3a74fbd4faebe8391013ba61e5a1a8d48c4455b923bdda7858", size = 2324852 }, + { url = "https://files.pythonhosted.org/packages/1a/d9/c248c103856f807ef70c18a4f986693a46a8ffe1602e5d361485da502d20/pydantic_core-2.41.5-cp313-cp313-win32.whl", hash = "sha256:650ae77860b45cfa6e2cdafc42618ceafab3a2d9a3811fcfbd3bbf8ac3c40d36", size = 1994679 }, + { url = "https://files.pythonhosted.org/packages/9e/8b/341991b158ddab181cff136acd2552c9f35bd30380422a639c0671e99a91/pydantic_core-2.41.5-cp313-cp313-win_amd64.whl", hash = "sha256:79ec52ec461e99e13791ec6508c722742ad745571f234ea6255bed38c6480f11", size = 2019766 }, + { url = "https://files.pythonhosted.org/packages/73/7d/f2f9db34af103bea3e09735bb40b021788a5e834c81eedb541991badf8f5/pydantic_core-2.41.5-cp313-cp313-win_arm64.whl", hash = "sha256:3f84d5c1b4ab906093bdc1ff10484838aca54ef08de4afa9de0f5f14d69639cd", size = 1981005 }, + { url = "https://files.pythonhosted.org/packages/ea/28/46b7c5c9635ae96ea0fbb779e271a38129df2550f763937659ee6c5dbc65/pydantic_core-2.41.5-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:3f37a19d7ebcdd20b96485056ba9e8b304e27d9904d233d7b1015db320e51f0a", size = 2119622 }, + { url = "https://files.pythonhosted.org/packages/74/1a/145646e5687e8d9a1e8d09acb278c8535ebe9e972e1f162ed338a622f193/pydantic_core-2.41.5-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:1d1d9764366c73f996edd17abb6d9d7649a7eb690006ab6adbda117717099b14", size = 1891725 }, + { url = "https://files.pythonhosted.org/packages/23/04/e89c29e267b8060b40dca97bfc64a19b2a3cf99018167ea1677d96368273/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:25e1c2af0fce638d5f1988b686f3b3ea8cd7de5f244ca147c777769e798a9cd1", size = 1915040 }, + { url = "https://files.pythonhosted.org/packages/84/a3/15a82ac7bd97992a82257f777b3583d3e84bdb06ba6858f745daa2ec8a85/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:506d766a8727beef16b7adaeb8ee6217c64fc813646b424d0804d67c16eddb66", size = 2063691 }, + { url = "https://files.pythonhosted.org/packages/74/9b/0046701313c6ef08c0c1cf0e028c67c770a4e1275ca73131563c5f2a310a/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4819fa52133c9aa3c387b3328f25c1facc356491e6135b459f1de698ff64d869", size = 2213897 }, + { url = "https://files.pythonhosted.org/packages/8a/cd/6bac76ecd1b27e75a95ca3a9a559c643b3afcd2dd62086d4b7a32a18b169/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2b761d210c9ea91feda40d25b4efe82a1707da2ef62901466a42492c028553a2", size = 2333302 }, + { url = "https://files.pythonhosted.org/packages/4c/d2/ef2074dc020dd6e109611a8be4449b98cd25e1b9b8a303c2f0fca2f2bcf7/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:22f0fb8c1c583a3b6f24df2470833b40207e907b90c928cc8d3594b76f874375", size = 2064877 }, + { url = "https://files.pythonhosted.org/packages/18/66/e9db17a9a763d72f03de903883c057b2592c09509ccfe468187f2a2eef29/pydantic_core-2.41.5-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2782c870e99878c634505236d81e5443092fba820f0373997ff75f90f68cd553", size = 2180680 }, + { url = "https://files.pythonhosted.org/packages/d3/9e/3ce66cebb929f3ced22be85d4c2399b8e85b622db77dad36b73c5387f8f8/pydantic_core-2.41.5-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:0177272f88ab8312479336e1d777f6b124537d47f2123f89cb37e0accea97f90", size = 2138960 }, + { url = "https://files.pythonhosted.org/packages/a6/62/205a998f4327d2079326b01abee48e502ea739d174f0a89295c481a2272e/pydantic_core-2.41.5-cp314-cp314-musllinux_1_1_armv7l.whl", hash = "sha256:63510af5e38f8955b8ee5687740d6ebf7c2a0886d15a6d65c32814613681bc07", size = 2339102 }, + { url = "https://files.pythonhosted.org/packages/3c/0d/f05e79471e889d74d3d88f5bd20d0ed189ad94c2423d81ff8d0000aab4ff/pydantic_core-2.41.5-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:e56ba91f47764cc14f1daacd723e3e82d1a89d783f0f5afe9c364b8bb491ccdb", size = 2326039 }, + { url = "https://files.pythonhosted.org/packages/ec/e1/e08a6208bb100da7e0c4b288eed624a703f4d129bde2da475721a80cab32/pydantic_core-2.41.5-cp314-cp314-win32.whl", hash = "sha256:aec5cf2fd867b4ff45b9959f8b20ea3993fc93e63c7363fe6851424c8a7e7c23", size = 1995126 }, + { url = "https://files.pythonhosted.org/packages/48/5d/56ba7b24e9557f99c9237e29f5c09913c81eeb2f3217e40e922353668092/pydantic_core-2.41.5-cp314-cp314-win_amd64.whl", hash = "sha256:8e7c86f27c585ef37c35e56a96363ab8de4e549a95512445b85c96d3e2f7c1bf", size = 2015489 }, + { url = "https://files.pythonhosted.org/packages/4e/bb/f7a190991ec9e3e0ba22e4993d8755bbc4a32925c0b5b42775c03e8148f9/pydantic_core-2.41.5-cp314-cp314-win_arm64.whl", hash = "sha256:e672ba74fbc2dc8eea59fb6d4aed6845e6905fc2a8afe93175d94a83ba2a01a0", size = 1977288 }, + { url = "https://files.pythonhosted.org/packages/92/ed/77542d0c51538e32e15afe7899d79efce4b81eee631d99850edc2f5e9349/pydantic_core-2.41.5-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:8566def80554c3faa0e65ac30ab0932b9e3a5cd7f8323764303d468e5c37595a", size = 2120255 }, + { url = "https://files.pythonhosted.org/packages/bb/3d/6913dde84d5be21e284439676168b28d8bbba5600d838b9dca99de0fad71/pydantic_core-2.41.5-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:b80aa5095cd3109962a298ce14110ae16b8c1aece8b72f9dafe81cf597ad80b3", size = 1863760 }, + { url = "https://files.pythonhosted.org/packages/5a/f0/e5e6b99d4191da102f2b0eb9687aaa7f5bea5d9964071a84effc3e40f997/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3006c3dd9ba34b0c094c544c6006cc79e87d8612999f1a5d43b769b89181f23c", size = 1878092 }, + { url = "https://files.pythonhosted.org/packages/71/48/36fb760642d568925953bcc8116455513d6e34c4beaa37544118c36aba6d/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:72f6c8b11857a856bcfa48c86f5368439f74453563f951e473514579d44aa612", size = 2053385 }, + { url = "https://files.pythonhosted.org/packages/20/25/92dc684dd8eb75a234bc1c764b4210cf2646479d54b47bf46061657292a8/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5cb1b2f9742240e4bb26b652a5aeb840aa4b417c7748b6f8387927bc6e45e40d", size = 2218832 }, + { url = "https://files.pythonhosted.org/packages/e2/09/f53e0b05023d3e30357d82eb35835d0f6340ca344720a4599cd663dca599/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bd3d54f38609ff308209bd43acea66061494157703364ae40c951f83ba99a1a9", size = 2327585 }, + { url = "https://files.pythonhosted.org/packages/aa/4e/2ae1aa85d6af35a39b236b1b1641de73f5a6ac4d5a7509f77b814885760c/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2ff4321e56e879ee8d2a879501c8e469414d948f4aba74a2d4593184eb326660", size = 2041078 }, + { url = "https://files.pythonhosted.org/packages/cd/13/2e215f17f0ef326fc72afe94776edb77525142c693767fc347ed6288728d/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d0d2568a8c11bf8225044aa94409e21da0cb09dcdafe9ecd10250b2baad531a9", size = 2173914 }, + { url = "https://files.pythonhosted.org/packages/02/7a/f999a6dcbcd0e5660bc348a3991c8915ce6599f4f2c6ac22f01d7a10816c/pydantic_core-2.41.5-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:a39455728aabd58ceabb03c90e12f71fd30fa69615760a075b9fec596456ccc3", size = 2129560 }, + { url = "https://files.pythonhosted.org/packages/3a/b1/6c990ac65e3b4c079a4fb9f5b05f5b013afa0f4ed6780a3dd236d2cbdc64/pydantic_core-2.41.5-cp314-cp314t-musllinux_1_1_armv7l.whl", hash = "sha256:239edca560d05757817c13dc17c50766136d21f7cd0fac50295499ae24f90fdf", size = 2329244 }, + { url = "https://files.pythonhosted.org/packages/d9/02/3c562f3a51afd4d88fff8dffb1771b30cfdfd79befd9883ee094f5b6c0d8/pydantic_core-2.41.5-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:2a5e06546e19f24c6a96a129142a75cee553cc018ffee48a460059b1185f4470", size = 2331955 }, + { url = "https://files.pythonhosted.org/packages/5c/96/5fb7d8c3c17bc8c62fdb031c47d77a1af698f1d7a406b0f79aaa1338f9ad/pydantic_core-2.41.5-cp314-cp314t-win32.whl", hash = "sha256:b4ececa40ac28afa90871c2cc2b9ffd2ff0bf749380fbdf57d165fd23da353aa", size = 1988906 }, + { url = "https://files.pythonhosted.org/packages/22/ed/182129d83032702912c2e2d8bbe33c036f342cc735737064668585dac28f/pydantic_core-2.41.5-cp314-cp314t-win_amd64.whl", hash = "sha256:80aa89cad80b32a912a65332f64a4450ed00966111b6615ca6816153d3585a8c", size = 1981607 }, + { url = "https://files.pythonhosted.org/packages/9f/ed/068e41660b832bb0b1aa5b58011dea2a3fe0ba7861ff38c4d4904c1c1a99/pydantic_core-2.41.5-cp314-cp314t-win_arm64.whl", hash = "sha256:35b44f37a3199f771c3eaa53051bc8a70cd7b54f333531c59e29fd4db5d15008", size = 1974769 }, + { url = "https://files.pythonhosted.org/packages/5f/9b/1b3f0e9f9305839d7e84912f9e8bfbd191ed1b1ef48083609f0dabde978c/pydantic_core-2.41.5-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:b2379fa7ed44ddecb5bfe4e48577d752db9fc10be00a6b7446e9663ba143de26", size = 2101980 }, + { url = "https://files.pythonhosted.org/packages/a4/ed/d71fefcb4263df0da6a85b5d8a7508360f2f2e9b3bf5814be9c8bccdccc1/pydantic_core-2.41.5-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:266fb4cbf5e3cbd0b53669a6d1b039c45e3ce651fd5442eff4d07c2cc8d66808", size = 1923865 }, + { url = "https://files.pythonhosted.org/packages/ce/3a/626b38db460d675f873e4444b4bb030453bbe7b4ba55df821d026a0493c4/pydantic_core-2.41.5-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:58133647260ea01e4d0500089a8c4f07bd7aa6ce109682b1426394988d8aaacc", size = 2134256 }, + { url = "https://files.pythonhosted.org/packages/83/d9/8412d7f06f616bbc053d30cb4e5f76786af3221462ad5eee1f202021eb4e/pydantic_core-2.41.5-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:287dad91cfb551c363dc62899a80e9e14da1f0e2b6ebde82c806612ca2a13ef1", size = 2174762 }, + { url = "https://files.pythonhosted.org/packages/55/4c/162d906b8e3ba3a99354e20faa1b49a85206c47de97a639510a0e673f5da/pydantic_core-2.41.5-pp311-pypy311_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:03b77d184b9eb40240ae9fd676ca364ce1085f203e1b1256f8ab9984dca80a84", size = 2143141 }, + { url = "https://files.pythonhosted.org/packages/1f/f2/f11dd73284122713f5f89fc940f370d035fa8e1e078d446b3313955157fe/pydantic_core-2.41.5-pp311-pypy311_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:a668ce24de96165bb239160b3d854943128f4334822900534f2fe947930e5770", size = 2330317 }, + { url = "https://files.pythonhosted.org/packages/88/9d/b06ca6acfe4abb296110fb1273a4d848a0bfb2ff65f3ee92127b3244e16b/pydantic_core-2.41.5-pp311-pypy311_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:f14f8f046c14563f8eb3f45f499cc658ab8d10072961e07225e507adb700e93f", size = 2316992 }, + { url = "https://files.pythonhosted.org/packages/36/c7/cfc8e811f061c841d7990b0201912c3556bfeb99cdcb7ed24adc8d6f8704/pydantic_core-2.41.5-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:56121965f7a4dc965bff783d70b907ddf3d57f6eba29b6d2e5dabfaf07799c51", size = 2145302 }, +] + +[[package]] +name = "pygments" +version = "2.19.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b0/77/a5b8c569bf593b0140bde72ea885a803b82086995367bf2037de0159d924/pygments-2.19.2.tar.gz", hash = "sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887", size = 4968631 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c7/21/705964c7812476f378728bdf590ca4b771ec72385c533964653c68e86bdc/pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b", size = 1225217 }, +] + +[[package]] +name = "pytest" +version = "9.0.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, + { name = "iniconfig" }, + { name = "packaging" }, + { name = "pluggy" }, + { name = "pygments" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/d1/db/7ef3487e0fb0049ddb5ce41d3a49c235bf9ad299b6a25d5780a89f19230f/pytest-9.0.2.tar.gz", hash = "sha256:75186651a92bd89611d1d9fc20f0b4345fd827c41ccd5c299a868a05d70edf11", size = 1568901 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3b/ab/b3226f0bd7cdcf710fbede2b3548584366da3b19b5021e74f5bde2a8fa3f/pytest-9.0.2-py3-none-any.whl", hash = "sha256:711ffd45bf766d5264d487b917733b453d917afd2b0ad65223959f59089f875b", size = 374801 }, +] + +[[package]] +name = "python-dotenv" +version = "1.2.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f0/26/19cadc79a718c5edbec86fd4919a6b6d3f681039a2f6d66d14be94e75fb9/python_dotenv-1.2.1.tar.gz", hash = "sha256:42667e897e16ab0d66954af0e60a9caa94f0fd4ecf3aaf6d2d260eec1aa36ad6", size = 44221 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/14/1b/a298b06749107c305e1fe0f814c6c74aea7b2f1e10989cb30f544a1b3253/python_dotenv-1.2.1-py3-none-any.whl", hash = "sha256:b81ee9561e9ca4004139c6cbba3a238c32b03e4894671e181b671e8cb8425d61", size = 21230 }, +] + +[[package]] +name = "pyyaml" +version = "6.0.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/05/8e/961c0007c59b8dd7729d542c61a4d537767a59645b82a0b521206e1e25c2/pyyaml-6.0.3.tar.gz", hash = "sha256:d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f", size = 130960 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6d/16/a95b6757765b7b031c9374925bb718d55e0a9ba8a1b6a12d25962ea44347/pyyaml-6.0.3-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:44edc647873928551a01e7a563d7452ccdebee747728c1080d881d68af7b997e", size = 185826 }, + { url = "https://files.pythonhosted.org/packages/16/19/13de8e4377ed53079ee996e1ab0a9c33ec2faf808a4647b7b4c0d46dd239/pyyaml-6.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:652cb6edd41e718550aad172851962662ff2681490a8a711af6a4d288dd96824", size = 175577 }, + { url = "https://files.pythonhosted.org/packages/0c/62/d2eb46264d4b157dae1275b573017abec435397aa59cbcdab6fc978a8af4/pyyaml-6.0.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:10892704fc220243f5305762e276552a0395f7beb4dbf9b14ec8fd43b57f126c", size = 775556 }, + { url = "https://files.pythonhosted.org/packages/10/cb/16c3f2cf3266edd25aaa00d6c4350381c8b012ed6f5276675b9eba8d9ff4/pyyaml-6.0.3-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:850774a7879607d3a6f50d36d04f00ee69e7fc816450e5f7e58d7f17f1ae5c00", size = 882114 }, + { url = "https://files.pythonhosted.org/packages/71/60/917329f640924b18ff085ab889a11c763e0b573da888e8404ff486657602/pyyaml-6.0.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b8bb0864c5a28024fac8a632c443c87c5aa6f215c0b126c449ae1a150412f31d", size = 806638 }, + { url = "https://files.pythonhosted.org/packages/dd/6f/529b0f316a9fd167281a6c3826b5583e6192dba792dd55e3203d3f8e655a/pyyaml-6.0.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1d37d57ad971609cf3c53ba6a7e365e40660e3be0e5175fa9f2365a379d6095a", size = 767463 }, + { url = "https://files.pythonhosted.org/packages/f2/6a/b627b4e0c1dd03718543519ffb2f1deea4a1e6d42fbab8021936a4d22589/pyyaml-6.0.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:37503bfbfc9d2c40b344d06b2199cf0e96e97957ab1c1b546fd4f87e53e5d3e4", size = 794986 }, + { url = "https://files.pythonhosted.org/packages/45/91/47a6e1c42d9ee337c4839208f30d9f09caa9f720ec7582917b264defc875/pyyaml-6.0.3-cp311-cp311-win32.whl", hash = "sha256:8098f252adfa6c80ab48096053f512f2321f0b998f98150cea9bd23d83e1467b", size = 142543 }, + { url = "https://files.pythonhosted.org/packages/da/e3/ea007450a105ae919a72393cb06f122f288ef60bba2dc64b26e2646fa315/pyyaml-6.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:9f3bfb4965eb874431221a3ff3fdcddc7e74e3b07799e0e84ca4a0f867d449bf", size = 158763 }, + { url = "https://files.pythonhosted.org/packages/d1/33/422b98d2195232ca1826284a76852ad5a86fe23e31b009c9886b2d0fb8b2/pyyaml-6.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7f047e29dcae44602496db43be01ad42fc6f1cc0d8cd6c83d342306c32270196", size = 182063 }, + { url = "https://files.pythonhosted.org/packages/89/a0/6cf41a19a1f2f3feab0e9c0b74134aa2ce6849093d5517a0c550fe37a648/pyyaml-6.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fc09d0aa354569bc501d4e787133afc08552722d3ab34836a80547331bb5d4a0", size = 173973 }, + { url = "https://files.pythonhosted.org/packages/ed/23/7a778b6bd0b9a8039df8b1b1d80e2e2ad78aa04171592c8a5c43a56a6af4/pyyaml-6.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9149cad251584d5fb4981be1ecde53a1ca46c891a79788c0df828d2f166bda28", size = 775116 }, + { url = "https://files.pythonhosted.org/packages/65/30/d7353c338e12baef4ecc1b09e877c1970bd3382789c159b4f89d6a70dc09/pyyaml-6.0.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5fdec68f91a0c6739b380c83b951e2c72ac0197ace422360e6d5a959d8d97b2c", size = 844011 }, + { url = "https://files.pythonhosted.org/packages/8b/9d/b3589d3877982d4f2329302ef98a8026e7f4443c765c46cfecc8858c6b4b/pyyaml-6.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ba1cc08a7ccde2d2ec775841541641e4548226580ab850948cbfda66a1befcdc", size = 807870 }, + { url = "https://files.pythonhosted.org/packages/05/c0/b3be26a015601b822b97d9149ff8cb5ead58c66f981e04fedf4e762f4bd4/pyyaml-6.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8dc52c23056b9ddd46818a57b78404882310fb473d63f17b07d5c40421e47f8e", size = 761089 }, + { url = "https://files.pythonhosted.org/packages/be/8e/98435a21d1d4b46590d5459a22d88128103f8da4c2d4cb8f14f2a96504e1/pyyaml-6.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:41715c910c881bc081f1e8872880d3c650acf13dfa8214bad49ed4cede7c34ea", size = 790181 }, + { url = "https://files.pythonhosted.org/packages/74/93/7baea19427dcfbe1e5a372d81473250b379f04b1bd3c4c5ff825e2327202/pyyaml-6.0.3-cp312-cp312-win32.whl", hash = "sha256:96b533f0e99f6579b3d4d4995707cf36df9100d67e0c8303a0c55b27b5f99bc5", size = 137658 }, + { url = "https://files.pythonhosted.org/packages/86/bf/899e81e4cce32febab4fb42bb97dcdf66bc135272882d1987881a4b519e9/pyyaml-6.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:5fcd34e47f6e0b794d17de1b4ff496c00986e1c83f7ab2fb8fcfe9616ff7477b", size = 154003 }, + { url = "https://files.pythonhosted.org/packages/1a/08/67bd04656199bbb51dbed1439b7f27601dfb576fb864099c7ef0c3e55531/pyyaml-6.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:64386e5e707d03a7e172c0701abfb7e10f0fb753ee1d773128192742712a98fd", size = 140344 }, + { url = "https://files.pythonhosted.org/packages/d1/11/0fd08f8192109f7169db964b5707a2f1e8b745d4e239b784a5a1dd80d1db/pyyaml-6.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8da9669d359f02c0b91ccc01cac4a67f16afec0dac22c2ad09f46bee0697eba8", size = 181669 }, + { url = "https://files.pythonhosted.org/packages/b1/16/95309993f1d3748cd644e02e38b75d50cbc0d9561d21f390a76242ce073f/pyyaml-6.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2283a07e2c21a2aa78d9c4442724ec1eb15f5e42a723b99cb3d822d48f5f7ad1", size = 173252 }, + { url = "https://files.pythonhosted.org/packages/50/31/b20f376d3f810b9b2371e72ef5adb33879b25edb7a6d072cb7ca0c486398/pyyaml-6.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ee2922902c45ae8ccada2c5b501ab86c36525b883eff4255313a253a3160861c", size = 767081 }, + { url = "https://files.pythonhosted.org/packages/49/1e/a55ca81e949270d5d4432fbbd19dfea5321eda7c41a849d443dc92fd1ff7/pyyaml-6.0.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a33284e20b78bd4a18c8c2282d549d10bc8408a2a7ff57653c0cf0b9be0afce5", size = 841159 }, + { url = "https://files.pythonhosted.org/packages/74/27/e5b8f34d02d9995b80abcef563ea1f8b56d20134d8f4e5e81733b1feceb2/pyyaml-6.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0f29edc409a6392443abf94b9cf89ce99889a1dd5376d94316ae5145dfedd5d6", size = 801626 }, + { url = "https://files.pythonhosted.org/packages/f9/11/ba845c23988798f40e52ba45f34849aa8a1f2d4af4b798588010792ebad6/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f7057c9a337546edc7973c0d3ba84ddcdf0daa14533c2065749c9075001090e6", size = 753613 }, + { url = "https://files.pythonhosted.org/packages/3d/e0/7966e1a7bfc0a45bf0a7fb6b98ea03fc9b8d84fa7f2229e9659680b69ee3/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:eda16858a3cab07b80edaf74336ece1f986ba330fdb8ee0d6c0d68fe82bc96be", size = 794115 }, + { url = "https://files.pythonhosted.org/packages/de/94/980b50a6531b3019e45ddeada0626d45fa85cbe22300844a7983285bed3b/pyyaml-6.0.3-cp313-cp313-win32.whl", hash = "sha256:d0eae10f8159e8fdad514efdc92d74fd8d682c933a6dd088030f3834bc8e6b26", size = 137427 }, + { url = "https://files.pythonhosted.org/packages/97/c9/39d5b874e8b28845e4ec2202b5da735d0199dbe5b8fb85f91398814a9a46/pyyaml-6.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:79005a0d97d5ddabfeeea4cf676af11e647e41d81c9a7722a193022accdb6b7c", size = 154090 }, + { url = "https://files.pythonhosted.org/packages/73/e8/2bdf3ca2090f68bb3d75b44da7bbc71843b19c9f2b9cb9b0f4ab7a5a4329/pyyaml-6.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:5498cd1645aa724a7c71c8f378eb29ebe23da2fc0d7a08071d89469bf1d2defb", size = 140246 }, + { url = "https://files.pythonhosted.org/packages/9d/8c/f4bd7f6465179953d3ac9bc44ac1a8a3e6122cf8ada906b4f96c60172d43/pyyaml-6.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:8d1fab6bb153a416f9aeb4b8763bc0f22a5586065f86f7664fc23339fc1c1fac", size = 181814 }, + { url = "https://files.pythonhosted.org/packages/bd/9c/4d95bb87eb2063d20db7b60faa3840c1b18025517ae857371c4dd55a6b3a/pyyaml-6.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:34d5fcd24b8445fadc33f9cf348c1047101756fd760b4dacb5c3e99755703310", size = 173809 }, + { url = "https://files.pythonhosted.org/packages/92/b5/47e807c2623074914e29dabd16cbbdd4bf5e9b2db9f8090fa64411fc5382/pyyaml-6.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:501a031947e3a9025ed4405a168e6ef5ae3126c59f90ce0cd6f2bfc477be31b7", size = 766454 }, + { url = "https://files.pythonhosted.org/packages/02/9e/e5e9b168be58564121efb3de6859c452fccde0ab093d8438905899a3a483/pyyaml-6.0.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b3bc83488de33889877a0f2543ade9f70c67d66d9ebb4ac959502e12de895788", size = 836355 }, + { url = "https://files.pythonhosted.org/packages/88/f9/16491d7ed2a919954993e48aa941b200f38040928474c9e85ea9e64222c3/pyyaml-6.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c458b6d084f9b935061bc36216e8a69a7e293a2f1e68bf956dcd9e6cbcd143f5", size = 794175 }, + { url = "https://files.pythonhosted.org/packages/dd/3f/5989debef34dc6397317802b527dbbafb2b4760878a53d4166579111411e/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7c6610def4f163542a622a73fb39f534f8c101d690126992300bf3207eab9764", size = 755228 }, + { url = "https://files.pythonhosted.org/packages/d7/ce/af88a49043cd2e265be63d083fc75b27b6ed062f5f9fd6cdc223ad62f03e/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5190d403f121660ce8d1d2c1bb2ef1bd05b5f68533fc5c2ea899bd15f4399b35", size = 789194 }, + { url = "https://files.pythonhosted.org/packages/23/20/bb6982b26a40bb43951265ba29d4c246ef0ff59c9fdcdf0ed04e0687de4d/pyyaml-6.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:4a2e8cebe2ff6ab7d1050ecd59c25d4c8bd7e6f400f5f82b96557ac0abafd0ac", size = 156429 }, + { url = "https://files.pythonhosted.org/packages/f4/f4/a4541072bb9422c8a883ab55255f918fa378ecf083f5b85e87fc2b4eda1b/pyyaml-6.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:93dda82c9c22deb0a405ea4dc5f2d0cda384168e466364dec6255b293923b2f3", size = 143912 }, + { url = "https://files.pythonhosted.org/packages/7c/f9/07dd09ae774e4616edf6cda684ee78f97777bdd15847253637a6f052a62f/pyyaml-6.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:02893d100e99e03eda1c8fd5c441d8c60103fd175728e23e431db1b589cf5ab3", size = 189108 }, + { url = "https://files.pythonhosted.org/packages/4e/78/8d08c9fb7ce09ad8c38ad533c1191cf27f7ae1effe5bb9400a46d9437fcf/pyyaml-6.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c1ff362665ae507275af2853520967820d9124984e0f7466736aea23d8611fba", size = 183641 }, + { url = "https://files.pythonhosted.org/packages/7b/5b/3babb19104a46945cf816d047db2788bcaf8c94527a805610b0289a01c6b/pyyaml-6.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6adc77889b628398debc7b65c073bcb99c4a0237b248cacaf3fe8a557563ef6c", size = 831901 }, + { url = "https://files.pythonhosted.org/packages/8b/cc/dff0684d8dc44da4d22a13f35f073d558c268780ce3c6ba1b87055bb0b87/pyyaml-6.0.3-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a80cb027f6b349846a3bf6d73b5e95e782175e52f22108cfa17876aaeff93702", size = 861132 }, + { url = "https://files.pythonhosted.org/packages/b1/5e/f77dc6b9036943e285ba76b49e118d9ea929885becb0a29ba8a7c75e29fe/pyyaml-6.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:00c4bdeba853cc34e7dd471f16b4114f4162dc03e6b7afcc2128711f0eca823c", size = 839261 }, + { url = "https://files.pythonhosted.org/packages/ce/88/a9db1376aa2a228197c58b37302f284b5617f56a5d959fd1763fb1675ce6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:66e1674c3ef6f541c35191caae2d429b967b99e02040f5ba928632d9a7f0f065", size = 805272 }, + { url = "https://files.pythonhosted.org/packages/da/92/1446574745d74df0c92e6aa4a7b0b3130706a4142b2d1a5869f2eaa423c6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:16249ee61e95f858e83976573de0f5b2893b3677ba71c9dd36b9cf8be9ac6d65", size = 829923 }, + { url = "https://files.pythonhosted.org/packages/f0/7a/1c7270340330e575b92f397352af856a8c06f230aa3e76f86b39d01b416a/pyyaml-6.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4ad1906908f2f5ae4e5a8ddfce73c320c2a1429ec52eafd27138b7f1cbe341c9", size = 174062 }, + { url = "https://files.pythonhosted.org/packages/f1/12/de94a39c2ef588c7e6455cfbe7343d3b2dc9d6b6b2f40c4c6565744c873d/pyyaml-6.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:ebc55a14a21cb14062aa4162f906cd962b28e2e9ea38f9b4391244cd8de4ae0b", size = 149341 }, +] + +[[package]] +name = "referencing" +version = "0.37.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "attrs" }, + { name = "rpds-py" }, + { name = "typing-extensions", marker = "python_full_version < '3.13'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/22/f5/df4e9027acead3ecc63e50fe1e36aca1523e1719559c499951bb4b53188f/referencing-0.37.0.tar.gz", hash = "sha256:44aefc3142c5b842538163acb373e24cce6632bd54bdb01b21ad5863489f50d8", size = 78036 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2c/58/ca301544e1fa93ed4f80d724bf5b194f6e4b945841c5bfd555878eea9fcb/referencing-0.37.0-py3-none-any.whl", hash = "sha256:381329a9f99628c9069361716891d34ad94af76e461dcb0335825aecc7692231", size = 26766 }, +] + +[[package]] +name = "regex" +version = "2026.1.15" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/0b/86/07d5056945f9ec4590b518171c4254a5925832eb727b56d3c38a7476f316/regex-2026.1.15.tar.gz", hash = "sha256:164759aa25575cbc0651bef59a0b18353e54300d79ace8084c818ad8ac72b7d5", size = 414811 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d0/c9/0c80c96eab96948363d270143138d671d5731c3a692b417629bf3492a9d6/regex-2026.1.15-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:1ae6020fb311f68d753b7efa9d4b9a5d47a5d6466ea0d5e3b5a471a960ea6e4a", size = 488168 }, + { url = "https://files.pythonhosted.org/packages/17/f0/271c92f5389a552494c429e5cc38d76d1322eb142fb5db3c8ccc47751468/regex-2026.1.15-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:eddf73f41225942c1f994914742afa53dc0d01a6e20fe14b878a1b1edc74151f", size = 290636 }, + { url = "https://files.pythonhosted.org/packages/a0/f9/5f1fd077d106ca5655a0f9ff8f25a1ab55b92128b5713a91ed7134ff688e/regex-2026.1.15-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1e8cd52557603f5c66a548f69421310886b28b7066853089e1a71ee710e1cdc1", size = 288496 }, + { url = "https://files.pythonhosted.org/packages/b5/e1/8f43b03a4968c748858ec77f746c286d81f896c2e437ccf050ebc5d3128c/regex-2026.1.15-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5170907244b14303edc5978f522f16c974f32d3aa92109fabc2af52411c9433b", size = 793503 }, + { url = "https://files.pythonhosted.org/packages/8d/4e/a39a5e8edc5377a46a7c875c2f9a626ed3338cb3bb06931be461c3e1a34a/regex-2026.1.15-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:2748c1ec0663580b4510bd89941a31560b4b439a0b428b49472a3d9944d11cd8", size = 860535 }, + { url = "https://files.pythonhosted.org/packages/dc/1c/9dce667a32a9477f7a2869c1c767dc00727284a9fa3ff5c09a5c6c03575e/regex-2026.1.15-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:2f2775843ca49360508d080eaa87f94fa248e2c946bbcd963bb3aae14f333413", size = 907225 }, + { url = "https://files.pythonhosted.org/packages/a4/3c/87ca0a02736d16b6262921425e84b48984e77d8e4e572c9072ce96e66c30/regex-2026.1.15-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d9ea2604370efc9a174c1b5dcc81784fb040044232150f7f33756049edfc9026", size = 800526 }, + { url = "https://files.pythonhosted.org/packages/4b/ff/647d5715aeea7c87bdcbd2f578f47b415f55c24e361e639fe8c0cc88878f/regex-2026.1.15-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:0dcd31594264029b57bf16f37fd7248a70b3b764ed9e0839a8f271b2d22c0785", size = 773446 }, + { url = "https://files.pythonhosted.org/packages/af/89/bf22cac25cb4ba0fe6bff52ebedbb65b77a179052a9d6037136ae93f42f4/regex-2026.1.15-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c08c1f3e34338256732bd6938747daa3c0d5b251e04b6e43b5813e94d503076e", size = 783051 }, + { url = "https://files.pythonhosted.org/packages/1e/f4/6ed03e71dca6348a5188363a34f5e26ffd5db1404780288ff0d79513bce4/regex-2026.1.15-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:e43a55f378df1e7a4fa3547c88d9a5a9b7113f653a66821bcea4718fe6c58763", size = 854485 }, + { url = "https://files.pythonhosted.org/packages/d9/9a/8e8560bd78caded8eb137e3e47612430a05b9a772caf60876435192d670a/regex-2026.1.15-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:f82110ab962a541737bd0ce87978d4c658f06e7591ba899192e2712a517badbb", size = 762195 }, + { url = "https://files.pythonhosted.org/packages/38/6b/61fc710f9aa8dfcd764fe27d37edfaa023b1a23305a0d84fccd5adb346ea/regex-2026.1.15-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:27618391db7bdaf87ac6c92b31e8f0dfb83a9de0075855152b720140bda177a2", size = 845986 }, + { url = "https://files.pythonhosted.org/packages/fd/2e/fbee4cb93f9d686901a7ca8d94285b80405e8c34fe4107f63ffcbfb56379/regex-2026.1.15-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:bfb0d6be01fbae8d6655c8ca21b3b72458606c4aec9bbc932db758d47aba6db1", size = 788992 }, + { url = "https://files.pythonhosted.org/packages/ed/14/3076348f3f586de64b1ab75a3fbabdaab7684af7f308ad43be7ef1849e55/regex-2026.1.15-cp311-cp311-win32.whl", hash = "sha256:b10e42a6de0e32559a92f2f8dc908478cc0fa02838d7dbe764c44dca3fa13569", size = 265893 }, + { url = "https://files.pythonhosted.org/packages/0f/19/772cf8b5fc803f5c89ba85d8b1870a1ca580dc482aa030383a9289c82e44/regex-2026.1.15-cp311-cp311-win_amd64.whl", hash = "sha256:e9bf3f0bbdb56633c07d7116ae60a576f846efdd86a8848f8d62b749e1209ca7", size = 277840 }, + { url = "https://files.pythonhosted.org/packages/78/84/d05f61142709474da3c0853222d91086d3e1372bcdab516c6fd8d80f3297/regex-2026.1.15-cp311-cp311-win_arm64.whl", hash = "sha256:41aef6f953283291c4e4e6850607bd71502be67779586a61472beacb315c97ec", size = 270374 }, + { url = "https://files.pythonhosted.org/packages/92/81/10d8cf43c807d0326efe874c1b79f22bfb0fb226027b0b19ebc26d301408/regex-2026.1.15-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:4c8fcc5793dde01641a35905d6731ee1548f02b956815f8f1cab89e515a5bdf1", size = 489398 }, + { url = "https://files.pythonhosted.org/packages/90/b0/7c2a74e74ef2a7c32de724658a69a862880e3e4155cba992ba04d1c70400/regex-2026.1.15-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:bfd876041a956e6a90ad7cdb3f6a630c07d491280bfeed4544053cd434901681", size = 291339 }, + { url = "https://files.pythonhosted.org/packages/19/4d/16d0773d0c818417f4cc20aa0da90064b966d22cd62a8c46765b5bd2d643/regex-2026.1.15-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:9250d087bc92b7d4899ccd5539a1b2334e44eee85d848c4c1aef8e221d3f8c8f", size = 289003 }, + { url = "https://files.pythonhosted.org/packages/c6/e4/1fc4599450c9f0863d9406e944592d968b8d6dfd0d552a7d569e43bceada/regex-2026.1.15-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c8a154cf6537ebbc110e24dabe53095e714245c272da9c1be05734bdad4a61aa", size = 798656 }, + { url = "https://files.pythonhosted.org/packages/b2/e6/59650d73a73fa8a60b3a590545bfcf1172b4384a7df2e7fe7b9aab4e2da9/regex-2026.1.15-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:8050ba2e3ea1d8731a549e83c18d2f0999fbc99a5f6bd06b4c91449f55291804", size = 864252 }, + { url = "https://files.pythonhosted.org/packages/6e/ab/1d0f4d50a1638849a97d731364c9a80fa304fec46325e48330c170ee8e80/regex-2026.1.15-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:0bf065240704cb8951cc04972cf107063917022511273e0969bdb34fc173456c", size = 912268 }, + { url = "https://files.pythonhosted.org/packages/dd/df/0d722c030c82faa1d331d1921ee268a4e8fb55ca8b9042c9341c352f17fa/regex-2026.1.15-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c32bef3e7aeee75746748643667668ef941d28b003bfc89994ecf09a10f7a1b5", size = 803589 }, + { url = "https://files.pythonhosted.org/packages/66/23/33289beba7ccb8b805c6610a8913d0131f834928afc555b241caabd422a9/regex-2026.1.15-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:d5eaa4a4c5b1906bd0d2508d68927f15b81821f85092e06f1a34a4254b0e1af3", size = 775700 }, + { url = "https://files.pythonhosted.org/packages/e7/65/bf3a42fa6897a0d3afa81acb25c42f4b71c274f698ceabd75523259f6688/regex-2026.1.15-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:86c1077a3cc60d453d4084d5b9649065f3bf1184e22992bd322e1f081d3117fb", size = 787928 }, + { url = "https://files.pythonhosted.org/packages/f4/f5/13bf65864fc314f68cdd6d8ca94adcab064d4d39dbd0b10fef29a9da48fc/regex-2026.1.15-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:2b091aefc05c78d286657cd4db95f2e6313375ff65dcf085e42e4c04d9c8d410", size = 858607 }, + { url = "https://files.pythonhosted.org/packages/a3/31/040e589834d7a439ee43fb0e1e902bc81bd58a5ba81acffe586bb3321d35/regex-2026.1.15-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:57e7d17f59f9ebfa9667e6e5a1c0127b96b87cb9cede8335482451ed00788ba4", size = 763729 }, + { url = "https://files.pythonhosted.org/packages/9b/84/6921e8129687a427edf25a34a5594b588b6d88f491320b9de5b6339a4fcb/regex-2026.1.15-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:c6c4dcdfff2c08509faa15d36ba7e5ef5fcfab25f1e8f85a0c8f45bc3a30725d", size = 850697 }, + { url = "https://files.pythonhosted.org/packages/8a/87/3d06143d4b128f4229158f2de5de6c8f2485170c7221e61bf381313314b2/regex-2026.1.15-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:cf8ff04c642716a7f2048713ddc6278c5fd41faa3b9cab12607c7abecd012c22", size = 789849 }, + { url = "https://files.pythonhosted.org/packages/77/69/c50a63842b6bd48850ebc7ab22d46e7a2a32d824ad6c605b218441814639/regex-2026.1.15-cp312-cp312-win32.whl", hash = "sha256:82345326b1d8d56afbe41d881fdf62f1926d7264b2fc1537f99ae5da9aad7913", size = 266279 }, + { url = "https://files.pythonhosted.org/packages/f2/36/39d0b29d087e2b11fd8191e15e81cce1b635fcc845297c67f11d0d19274d/regex-2026.1.15-cp312-cp312-win_amd64.whl", hash = "sha256:4def140aa6156bc64ee9912383d4038f3fdd18fee03a6f222abd4de6357ce42a", size = 277166 }, + { url = "https://files.pythonhosted.org/packages/28/32/5b8e476a12262748851fa8ab1b0be540360692325975b094e594dfebbb52/regex-2026.1.15-cp312-cp312-win_arm64.whl", hash = "sha256:c6c565d9a6e1a8d783c1948937ffc377dd5771e83bd56de8317c450a954d2056", size = 270415 }, + { url = "https://files.pythonhosted.org/packages/f8/2e/6870bb16e982669b674cce3ee9ff2d1d46ab80528ee6bcc20fb2292efb60/regex-2026.1.15-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:e69d0deeb977ffe7ed3d2e4439360089f9c3f217ada608f0f88ebd67afb6385e", size = 489164 }, + { url = "https://files.pythonhosted.org/packages/dc/67/9774542e203849b0286badf67199970a44ebdb0cc5fb739f06e47ada72f8/regex-2026.1.15-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:3601ffb5375de85a16f407854d11cca8fe3f5febbe3ac78fb2866bb220c74d10", size = 291218 }, + { url = "https://files.pythonhosted.org/packages/b2/87/b0cda79f22b8dee05f774922a214da109f9a4c0eca5da2c9d72d77ea062c/regex-2026.1.15-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:4c5ef43b5c2d4114eb8ea424bb8c9cec01d5d17f242af88b2448f5ee81caadbc", size = 288895 }, + { url = "https://files.pythonhosted.org/packages/3b/6a/0041f0a2170d32be01ab981d6346c83a8934277d82c780d60b127331f264/regex-2026.1.15-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:968c14d4f03e10b2fd960f1d5168c1f0ac969381d3c1fcc973bc45fb06346599", size = 798680 }, + { url = "https://files.pythonhosted.org/packages/58/de/30e1cfcdbe3e891324aa7568b7c968771f82190df5524fabc1138cb2d45a/regex-2026.1.15-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:56a5595d0f892f214609c9f76b41b7428bed439d98dc961efafdd1354d42baae", size = 864210 }, + { url = "https://files.pythonhosted.org/packages/64/44/4db2f5c5ca0ccd40ff052ae7b1e9731352fcdad946c2b812285a7505ca75/regex-2026.1.15-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:0bf650f26087363434c4e560011f8e4e738f6f3e029b85d4904c50135b86cfa5", size = 912358 }, + { url = "https://files.pythonhosted.org/packages/79/b6/e6a5665d43a7c42467138c8a2549be432bad22cbd206f5ec87162de74bd7/regex-2026.1.15-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:18388a62989c72ac24de75f1449d0fb0b04dfccd0a1a7c1c43af5eb503d890f6", size = 803583 }, + { url = "https://files.pythonhosted.org/packages/e7/53/7cd478222169d85d74d7437e74750005e993f52f335f7c04ff7adfda3310/regex-2026.1.15-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:6d220a2517f5893f55daac983bfa9fe998a7dbcaee4f5d27a88500f8b7873788", size = 775782 }, + { url = "https://files.pythonhosted.org/packages/ca/b5/75f9a9ee4b03a7c009fe60500fe550b45df94f0955ca29af16333ef557c5/regex-2026.1.15-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:c9c08c2fbc6120e70abff5d7f28ffb4d969e14294fb2143b4b5c7d20e46d1714", size = 787978 }, + { url = "https://files.pythonhosted.org/packages/72/b3/79821c826245bbe9ccbb54f6eadb7879c722fd3e0248c17bfc90bf54e123/regex-2026.1.15-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:7ef7d5d4bd49ec7364315167a4134a015f61e8266c6d446fc116a9ac4456e10d", size = 858550 }, + { url = "https://files.pythonhosted.org/packages/4a/85/2ab5f77a1c465745bfbfcb3ad63178a58337ae8d5274315e2cc623a822fa/regex-2026.1.15-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:6e42844ad64194fa08d5ccb75fe6a459b9b08e6d7296bd704460168d58a388f3", size = 763747 }, + { url = "https://files.pythonhosted.org/packages/6d/84/c27df502d4bfe2873a3e3a7cf1bdb2b9cc10284d1a44797cf38bed790470/regex-2026.1.15-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:cfecdaa4b19f9ca534746eb3b55a5195d5c95b88cac32a205e981ec0a22b7d31", size = 850615 }, + { url = "https://files.pythonhosted.org/packages/7d/b7/658a9782fb253680aa8ecb5ccbb51f69e088ed48142c46d9f0c99b46c575/regex-2026.1.15-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:08df9722d9b87834a3d701f3fca570b2be115654dbfd30179f30ab2f39d606d3", size = 789951 }, + { url = "https://files.pythonhosted.org/packages/fc/2a/5928af114441e059f15b2f63e188bd00c6529b3051c974ade7444b85fcda/regex-2026.1.15-cp313-cp313-win32.whl", hash = "sha256:d426616dae0967ca225ab12c22274eb816558f2f99ccb4a1d52ca92e8baf180f", size = 266275 }, + { url = "https://files.pythonhosted.org/packages/4f/16/5bfbb89e435897bff28cf0352a992ca719d9e55ebf8b629203c96b6ce4f7/regex-2026.1.15-cp313-cp313-win_amd64.whl", hash = "sha256:febd38857b09867d3ed3f4f1af7d241c5c50362e25ef43034995b77a50df494e", size = 277145 }, + { url = "https://files.pythonhosted.org/packages/56/c1/a09ff7392ef4233296e821aec5f78c51be5e91ffde0d163059e50fd75835/regex-2026.1.15-cp313-cp313-win_arm64.whl", hash = "sha256:8e32f7896f83774f91499d239e24cebfadbc07639c1494bb7213983842348337", size = 270411 }, + { url = "https://files.pythonhosted.org/packages/3c/38/0cfd5a78e5c6db00e6782fdae70458f89850ce95baa5e8694ab91d89744f/regex-2026.1.15-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:ec94c04149b6a7b8120f9f44565722c7ae31b7a6d2275569d2eefa76b83da3be", size = 492068 }, + { url = "https://files.pythonhosted.org/packages/50/72/6c86acff16cb7c959c4355826bbf06aad670682d07c8f3998d9ef4fee7cd/regex-2026.1.15-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:40c86d8046915bb9aeb15d3f3f15b6fd500b8ea4485b30e1bbc799dab3fe29f8", size = 292756 }, + { url = "https://files.pythonhosted.org/packages/4e/58/df7fb69eadfe76526ddfce28abdc0af09ffe65f20c2c90932e89d705153f/regex-2026.1.15-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:726ea4e727aba21643205edad8f2187ec682d3305d790f73b7a51c7587b64bdd", size = 291114 }, + { url = "https://files.pythonhosted.org/packages/ed/6c/a4011cd1cf96b90d2cdc7e156f91efbd26531e822a7fbb82a43c1016678e/regex-2026.1.15-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1cb740d044aff31898804e7bf1181cc72c03d11dfd19932b9911ffc19a79070a", size = 807524 }, + { url = "https://files.pythonhosted.org/packages/1d/25/a53ffb73183f69c3e9f4355c4922b76d2840aee160af6af5fac229b6201d/regex-2026.1.15-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:05d75a668e9ea16f832390d22131fe1e8acc8389a694c8febc3e340b0f810b93", size = 873455 }, + { url = "https://files.pythonhosted.org/packages/66/0b/8b47fc2e8f97d9b4a851736f3890a5f786443aa8901061c55f24c955f45b/regex-2026.1.15-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d991483606f3dbec93287b9f35596f41aa2e92b7c2ebbb935b63f409e243c9af", size = 915007 }, + { url = "https://files.pythonhosted.org/packages/c2/fa/97de0d681e6d26fabe71968dbee06dd52819e9a22fdce5dac7256c31ed84/regex-2026.1.15-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:194312a14819d3e44628a44ed6fea6898fdbecb0550089d84c403475138d0a09", size = 812794 }, + { url = "https://files.pythonhosted.org/packages/22/38/e752f94e860d429654aa2b1c51880bff8dfe8f084268258adf9151cf1f53/regex-2026.1.15-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:fe2fda4110a3d0bc163c2e0664be44657431440722c5c5315c65155cab92f9e5", size = 781159 }, + { url = "https://files.pythonhosted.org/packages/e9/a7/d739ffaef33c378fc888302a018d7f81080393d96c476b058b8c64fd2b0d/regex-2026.1.15-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:124dc36c85d34ef2d9164da41a53c1c8c122cfb1f6e1ec377a1f27ee81deb794", size = 795558 }, + { url = "https://files.pythonhosted.org/packages/3e/c4/542876f9a0ac576100fc73e9c75b779f5c31e3527576cfc9cb3009dcc58a/regex-2026.1.15-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:a1774cd1981cd212506a23a14dba7fdeaee259f5deba2df6229966d9911e767a", size = 868427 }, + { url = "https://files.pythonhosted.org/packages/fc/0f/d5655bea5b22069e32ae85a947aa564912f23758e112cdb74212848a1a1b/regex-2026.1.15-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:b5f7d8d2867152cdb625e72a530d2ccb48a3d199159144cbdd63870882fb6f80", size = 769939 }, + { url = "https://files.pythonhosted.org/packages/20/06/7e18a4fa9d326daeda46d471a44ef94201c46eaa26dbbb780b5d92cbfdda/regex-2026.1.15-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:492534a0ab925d1db998defc3c302dae3616a2fc3fe2e08db1472348f096ddf2", size = 854753 }, + { url = "https://files.pythonhosted.org/packages/3b/67/dc8946ef3965e166f558ef3b47f492bc364e96a265eb4a2bb3ca765c8e46/regex-2026.1.15-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:c661fc820cfb33e166bf2450d3dadbda47c8d8981898adb9b6fe24e5e582ba60", size = 799559 }, + { url = "https://files.pythonhosted.org/packages/a5/61/1bba81ff6d50c86c65d9fd84ce9699dd106438ee4cdb105bf60374ee8412/regex-2026.1.15-cp313-cp313t-win32.whl", hash = "sha256:99ad739c3686085e614bf77a508e26954ff1b8f14da0e3765ff7abbf7799f952", size = 268879 }, + { url = "https://files.pythonhosted.org/packages/e9/5e/cef7d4c5fb0ea3ac5c775fd37db5747f7378b29526cc83f572198924ff47/regex-2026.1.15-cp313-cp313t-win_amd64.whl", hash = "sha256:32655d17905e7ff8ba5c764c43cb124e34a9245e45b83c22e81041e1071aee10", size = 280317 }, + { url = "https://files.pythonhosted.org/packages/b4/52/4317f7a5988544e34ab57b4bde0f04944c4786128c933fb09825924d3e82/regex-2026.1.15-cp313-cp313t-win_arm64.whl", hash = "sha256:b2a13dd6a95e95a489ca242319d18fc02e07ceb28fa9ad146385194d95b3c829", size = 271551 }, + { url = "https://files.pythonhosted.org/packages/52/0a/47fa888ec7cbbc7d62c5f2a6a888878e76169170ead271a35239edd8f0e8/regex-2026.1.15-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:d920392a6b1f353f4aa54328c867fec3320fa50657e25f64abf17af054fc97ac", size = 489170 }, + { url = "https://files.pythonhosted.org/packages/ac/c4/d000e9b7296c15737c9301708e9e7fbdea009f8e93541b6b43bdb8219646/regex-2026.1.15-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:b5a28980a926fa810dbbed059547b02783952e2efd9c636412345232ddb87ff6", size = 291146 }, + { url = "https://files.pythonhosted.org/packages/f9/b6/921cc61982e538682bdf3bdf5b2c6ab6b34368da1f8e98a6c1ddc503c9cf/regex-2026.1.15-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:621f73a07595d83f28952d7bd1e91e9d1ed7625fb7af0064d3516674ec93a2a2", size = 288986 }, + { url = "https://files.pythonhosted.org/packages/ca/33/eb7383dde0bbc93f4fb9d03453aab97e18ad4024ac7e26cef8d1f0a2cff0/regex-2026.1.15-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3d7d92495f47567a9b1669c51fc8d6d809821849063d168121ef801bbc213846", size = 799098 }, + { url = "https://files.pythonhosted.org/packages/27/56/b664dccae898fc8d8b4c23accd853f723bde0f026c747b6f6262b688029c/regex-2026.1.15-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:8dd16fba2758db7a3780a051f245539c4451ca20910f5a5e6ea1c08d06d4a76b", size = 864980 }, + { url = "https://files.pythonhosted.org/packages/16/40/0999e064a170eddd237bae9ccfcd8f28b3aa98a38bf727a086425542a4fc/regex-2026.1.15-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:1e1808471fbe44c1a63e5f577a1d5f02fe5d66031dcbdf12f093ffc1305a858e", size = 911607 }, + { url = "https://files.pythonhosted.org/packages/07/78/c77f644b68ab054e5a674fb4da40ff7bffb2c88df58afa82dbf86573092d/regex-2026.1.15-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0751a26ad39d4f2ade8fe16c59b2bf5cb19eb3d2cd543e709e583d559bd9efde", size = 803358 }, + { url = "https://files.pythonhosted.org/packages/27/31/d4292ea8566eaa551fafc07797961c5963cf5235c797cc2ae19b85dfd04d/regex-2026.1.15-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:0f0c7684c7f9ca241344ff95a1de964f257a5251968484270e91c25a755532c5", size = 775833 }, + { url = "https://files.pythonhosted.org/packages/ce/b2/cff3bf2fea4133aa6fb0d1e370b37544d18c8350a2fa118c7e11d1db0e14/regex-2026.1.15-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:74f45d170a21df41508cb67165456538425185baaf686281fa210d7e729abc34", size = 788045 }, + { url = "https://files.pythonhosted.org/packages/8d/99/2cb9b69045372ec877b6f5124bda4eb4253bc58b8fe5848c973f752bc52c/regex-2026.1.15-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:f1862739a1ffb50615c0fde6bae6569b5efbe08d98e59ce009f68a336f64da75", size = 859374 }, + { url = "https://files.pythonhosted.org/packages/09/16/710b0a5abe8e077b1729a562d2f297224ad079f3a66dce46844c193416c8/regex-2026.1.15-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:453078802f1b9e2b7303fb79222c054cb18e76f7bdc220f7530fdc85d319f99e", size = 763940 }, + { url = "https://files.pythonhosted.org/packages/dd/d1/7585c8e744e40eb3d32f119191969b91de04c073fca98ec14299041f6e7e/regex-2026.1.15-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:a30a68e89e5a218b8b23a52292924c1f4b245cb0c68d1cce9aec9bbda6e2c160", size = 850112 }, + { url = "https://files.pythonhosted.org/packages/af/d6/43e1dd85df86c49a347aa57c1f69d12c652c7b60e37ec162e3096194a278/regex-2026.1.15-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:9479cae874c81bf610d72b85bb681a94c95722c127b55445285fb0e2c82db8e1", size = 789586 }, + { url = "https://files.pythonhosted.org/packages/93/38/77142422f631e013f316aaae83234c629555729a9fbc952b8a63ac91462a/regex-2026.1.15-cp314-cp314-win32.whl", hash = "sha256:d639a750223132afbfb8f429c60d9d318aeba03281a5f1ab49f877456448dcf1", size = 271691 }, + { url = "https://files.pythonhosted.org/packages/4a/a9/ab16b4649524ca9e05213c1cdbb7faa85cc2aa90a0230d2f796cbaf22736/regex-2026.1.15-cp314-cp314-win_amd64.whl", hash = "sha256:4161d87f85fa831e31469bfd82c186923070fc970b9de75339b68f0c75b51903", size = 280422 }, + { url = "https://files.pythonhosted.org/packages/be/2a/20fd057bf3521cb4791f69f869635f73e0aaf2b9ad2d260f728144f9047c/regex-2026.1.15-cp314-cp314-win_arm64.whl", hash = "sha256:91c5036ebb62663a6b3999bdd2e559fd8456d17e2b485bf509784cd31a8b1705", size = 273467 }, + { url = "https://files.pythonhosted.org/packages/ad/77/0b1e81857060b92b9cad239104c46507dd481b3ff1fa79f8e7f865aae38a/regex-2026.1.15-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:ee6854c9000a10938c79238de2379bea30c82e4925a371711af45387df35cab8", size = 492073 }, + { url = "https://files.pythonhosted.org/packages/70/f3/f8302b0c208b22c1e4f423147e1913fd475ddd6230565b299925353de644/regex-2026.1.15-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:2c2b80399a422348ce5de4fe40c418d6299a0fa2803dd61dc0b1a2f28e280fcf", size = 292757 }, + { url = "https://files.pythonhosted.org/packages/bf/f0/ef55de2460f3b4a6da9d9e7daacd0cb79d4ef75c64a2af316e68447f0df0/regex-2026.1.15-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:dca3582bca82596609959ac39e12b7dad98385b4fefccb1151b937383cec547d", size = 291122 }, + { url = "https://files.pythonhosted.org/packages/cf/55/bb8ccbacabbc3a11d863ee62a9f18b160a83084ea95cdfc5d207bfc3dd75/regex-2026.1.15-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ef71d476caa6692eea743ae5ea23cde3260677f70122c4d258ca952e5c2d4e84", size = 807761 }, + { url = "https://files.pythonhosted.org/packages/8f/84/f75d937f17f81e55679a0509e86176e29caa7298c38bd1db7ce9c0bf6075/regex-2026.1.15-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c243da3436354f4af6c3058a3f81a97d47ea52c9bd874b52fd30274853a1d5df", size = 873538 }, + { url = "https://files.pythonhosted.org/packages/b8/d9/0da86327df70349aa8d86390da91171bd3ca4f0e7c1d1d453a9c10344da3/regex-2026.1.15-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:8355ad842a7c7e9e5e55653eade3b7d1885ba86f124dd8ab1f722f9be6627434", size = 915066 }, + { url = "https://files.pythonhosted.org/packages/2a/5e/f660fb23fc77baa2a61aa1f1fe3a4eea2bbb8a286ddec148030672e18834/regex-2026.1.15-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f192a831d9575271a22d804ff1a5355355723f94f31d9eef25f0d45a152fdc1a", size = 812938 }, + { url = "https://files.pythonhosted.org/packages/69/33/a47a29bfecebbbfd1e5cd3f26b28020a97e4820f1c5148e66e3b7d4b4992/regex-2026.1.15-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:166551807ec20d47ceaeec380081f843e88c8949780cd42c40f18d16168bed10", size = 781314 }, + { url = "https://files.pythonhosted.org/packages/65/ec/7ec2bbfd4c3f4e494a24dec4c6943a668e2030426b1b8b949a6462d2c17b/regex-2026.1.15-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:f9ca1cbdc0fbfe5e6e6f8221ef2309988db5bcede52443aeaee9a4ad555e0dac", size = 795652 }, + { url = "https://files.pythonhosted.org/packages/46/79/a5d8651ae131fe27d7c521ad300aa7f1c7be1dbeee4d446498af5411b8a9/regex-2026.1.15-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:b30bcbd1e1221783c721483953d9e4f3ab9c5d165aa709693d3f3946747b1aea", size = 868550 }, + { url = "https://files.pythonhosted.org/packages/06/b7/25635d2809664b79f183070786a5552dd4e627e5aedb0065f4e3cf8ee37d/regex-2026.1.15-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:2a8d7b50c34578d0d3bf7ad58cde9652b7d683691876f83aedc002862a35dc5e", size = 769981 }, + { url = "https://files.pythonhosted.org/packages/16/8b/fc3fcbb2393dcfa4a6c5ffad92dc498e842df4581ea9d14309fcd3c55fb9/regex-2026.1.15-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:9d787e3310c6a6425eb346be4ff2ccf6eece63017916fd77fe8328c57be83521", size = 854780 }, + { url = "https://files.pythonhosted.org/packages/d0/38/dde117c76c624713c8a2842530be9c93ca8b606c0f6102d86e8cd1ce8bea/regex-2026.1.15-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:619843841e220adca114118533a574a9cd183ed8a28b85627d2844c500a2b0db", size = 799778 }, + { url = "https://files.pythonhosted.org/packages/e3/0d/3a6cfa9ae99606afb612d8fb7a66b245a9d5ff0f29bb347c8a30b6ad561b/regex-2026.1.15-cp314-cp314t-win32.whl", hash = "sha256:e90b8db97f6f2c97eb045b51a6b2c5ed69cedd8392459e0642d4199b94fabd7e", size = 274667 }, + { url = "https://files.pythonhosted.org/packages/5b/b2/297293bb0742fd06b8d8e2572db41a855cdf1cae0bf009b1cb74fe07e196/regex-2026.1.15-cp314-cp314t-win_amd64.whl", hash = "sha256:5ef19071f4ac9f0834793af85bd04a920b4407715624e40cb7a0631a11137cdf", size = 284386 }, + { url = "https://files.pythonhosted.org/packages/95/e4/a3b9480c78cf8ee86626cb06f8d931d74d775897d44201ccb813097ae697/regex-2026.1.15-cp314-cp314t-win_arm64.whl", hash = "sha256:ca89c5e596fc05b015f27561b3793dc2fa0917ea0d7507eebb448efd35274a70", size = 274837 }, +] + +[[package]] +name = "requests" +version = "2.32.5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "certifi" }, + { name = "charset-normalizer" }, + { name = "idna" }, + { name = "urllib3" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c9/74/b3ff8e6c8446842c3f5c837e9c3dfcfe2018ea6ecef224c710c85ef728f4/requests-2.32.5.tar.gz", hash = "sha256:dbba0bac56e100853db0ea71b82b4dfd5fe2bf6d3754a8893c3af500cec7d7cf", size = 134517 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1e/db/4254e3eabe8020b458f1a747140d32277ec7a271daf1d235b70dc0b4e6e3/requests-2.32.5-py3-none-any.whl", hash = "sha256:2462f94637a34fd532264295e186976db0f5d453d1cdd31473c85a6a161affb6", size = 64738 }, +] + +[[package]] +name = "rpds-py" +version = "0.30.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/20/af/3f2f423103f1113b36230496629986e0ef7e199d2aa8392452b484b38ced/rpds_py-0.30.0.tar.gz", hash = "sha256:dd8ff7cf90014af0c0f787eea34794ebf6415242ee1d6fa91eaba725cc441e84", size = 69469 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4d/6e/f964e88b3d2abee2a82c1ac8366da848fce1c6d834dc2132c3fda3970290/rpds_py-0.30.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:a2bffea6a4ca9f01b3f8e548302470306689684e61602aa3d141e34da06cf425", size = 370157 }, + { url = "https://files.pythonhosted.org/packages/94/ba/24e5ebb7c1c82e74c4e4f33b2112a5573ddc703915b13a073737b59b86e0/rpds_py-0.30.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:dc4f992dfe1e2bc3ebc7444f6c7051b4bc13cd8e33e43511e8ffd13bf407010d", size = 359676 }, + { url = "https://files.pythonhosted.org/packages/84/86/04dbba1b087227747d64d80c3b74df946b986c57af0a9f0c98726d4d7a3b/rpds_py-0.30.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:422c3cb9856d80b09d30d2eb255d0754b23e090034e1deb4083f8004bd0761e4", size = 389938 }, + { url = "https://files.pythonhosted.org/packages/42/bb/1463f0b1722b7f45431bdd468301991d1328b16cffe0b1c2918eba2c4eee/rpds_py-0.30.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:07ae8a593e1c3c6b82ca3292efbe73c30b61332fd612e05abee07c79359f292f", size = 402932 }, + { url = "https://files.pythonhosted.org/packages/99/ee/2520700a5c1f2d76631f948b0736cdf9b0acb25abd0ca8e889b5c62ac2e3/rpds_py-0.30.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:12f90dd7557b6bd57f40abe7747e81e0c0b119bef015ea7726e69fe550e394a4", size = 525830 }, + { url = "https://files.pythonhosted.org/packages/e0/ad/bd0331f740f5705cc555a5e17fdf334671262160270962e69a2bdef3bf76/rpds_py-0.30.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:99b47d6ad9a6da00bec6aabe5a6279ecd3c06a329d4aa4771034a21e335c3a97", size = 412033 }, + { url = "https://files.pythonhosted.org/packages/f8/1e/372195d326549bb51f0ba0f2ecb9874579906b97e08880e7a65c3bef1a99/rpds_py-0.30.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:33f559f3104504506a44bb666b93a33f5d33133765b0c216a5bf2f1e1503af89", size = 390828 }, + { url = "https://files.pythonhosted.org/packages/ab/2b/d88bb33294e3e0c76bc8f351a3721212713629ffca1700fa94979cb3eae8/rpds_py-0.30.0-cp311-cp311-manylinux_2_31_riscv64.whl", hash = "sha256:946fe926af6e44f3697abbc305ea168c2c31d3e3ef1058cf68f379bf0335a78d", size = 404683 }, + { url = "https://files.pythonhosted.org/packages/50/32/c759a8d42bcb5289c1fac697cd92f6fe01a018dd937e62ae77e0e7f15702/rpds_py-0.30.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:495aeca4b93d465efde585977365187149e75383ad2684f81519f504f5c13038", size = 421583 }, + { url = "https://files.pythonhosted.org/packages/2b/81/e729761dbd55ddf5d84ec4ff1f47857f4374b0f19bdabfcf929164da3e24/rpds_py-0.30.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d9a0ca5da0386dee0655b4ccdf46119df60e0f10da268d04fe7cc87886872ba7", size = 572496 }, + { url = "https://files.pythonhosted.org/packages/14/f6/69066a924c3557c9c30baa6ec3a0aa07526305684c6f86c696b08860726c/rpds_py-0.30.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:8d6d1cc13664ec13c1b84241204ff3b12f9bb82464b8ad6e7a5d3486975c2eed", size = 598669 }, + { url = "https://files.pythonhosted.org/packages/5f/48/905896b1eb8a05630d20333d1d8ffd162394127b74ce0b0784ae04498d32/rpds_py-0.30.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:3896fa1be39912cf0757753826bc8bdc8ca331a28a7c4ae46b7a21280b06bb85", size = 561011 }, + { url = "https://files.pythonhosted.org/packages/22/16/cd3027c7e279d22e5eb431dd3c0fbc677bed58797fe7581e148f3f68818b/rpds_py-0.30.0-cp311-cp311-win32.whl", hash = "sha256:55f66022632205940f1827effeff17c4fa7ae1953d2b74a8581baaefb7d16f8c", size = 221406 }, + { url = "https://files.pythonhosted.org/packages/fa/5b/e7b7aa136f28462b344e652ee010d4de26ee9fd16f1bfd5811f5153ccf89/rpds_py-0.30.0-cp311-cp311-win_amd64.whl", hash = "sha256:a51033ff701fca756439d641c0ad09a41d9242fa69121c7d8769604a0a629825", size = 236024 }, + { url = "https://files.pythonhosted.org/packages/14/a6/364bba985e4c13658edb156640608f2c9e1d3ea3c81b27aa9d889fff0e31/rpds_py-0.30.0-cp311-cp311-win_arm64.whl", hash = "sha256:47b0ef6231c58f506ef0b74d44e330405caa8428e770fec25329ed2cb971a229", size = 229069 }, + { url = "https://files.pythonhosted.org/packages/03/e7/98a2f4ac921d82f33e03f3835f5bf3a4a40aa1bfdc57975e74a97b2b4bdd/rpds_py-0.30.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:a161f20d9a43006833cd7068375a94d035714d73a172b681d8881820600abfad", size = 375086 }, + { url = "https://files.pythonhosted.org/packages/4d/a1/bca7fd3d452b272e13335db8d6b0b3ecde0f90ad6f16f3328c6fb150c889/rpds_py-0.30.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6abc8880d9d036ecaafe709079969f56e876fcf107f7a8e9920ba6d5a3878d05", size = 359053 }, + { url = "https://files.pythonhosted.org/packages/65/1c/ae157e83a6357eceff62ba7e52113e3ec4834a84cfe07fa4b0757a7d105f/rpds_py-0.30.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ca28829ae5f5d569bb62a79512c842a03a12576375d5ece7d2cadf8abe96ec28", size = 390763 }, + { url = "https://files.pythonhosted.org/packages/d4/36/eb2eb8515e2ad24c0bd43c3ee9cd74c33f7ca6430755ccdb240fd3144c44/rpds_py-0.30.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a1010ed9524c73b94d15919ca4d41d8780980e1765babf85f9a2f90d247153dd", size = 408951 }, + { url = "https://files.pythonhosted.org/packages/d6/65/ad8dc1784a331fabbd740ef6f71ce2198c7ed0890dab595adb9ea2d775a1/rpds_py-0.30.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f8d1736cfb49381ba528cd5baa46f82fdc65c06e843dab24dd70b63d09121b3f", size = 514622 }, + { url = "https://files.pythonhosted.org/packages/63/8e/0cfa7ae158e15e143fe03993b5bcd743a59f541f5952e1546b1ac1b5fd45/rpds_py-0.30.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d948b135c4693daff7bc2dcfc4ec57237a29bd37e60c2fabf5aff2bbacf3e2f1", size = 414492 }, + { url = "https://files.pythonhosted.org/packages/60/1b/6f8f29f3f995c7ffdde46a626ddccd7c63aefc0efae881dc13b6e5d5bb16/rpds_py-0.30.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47f236970bccb2233267d89173d3ad2703cd36a0e2a6e92d0560d333871a3d23", size = 394080 }, + { url = "https://files.pythonhosted.org/packages/6d/d5/a266341051a7a3ca2f4b750a3aa4abc986378431fc2da508c5034d081b70/rpds_py-0.30.0-cp312-cp312-manylinux_2_31_riscv64.whl", hash = "sha256:2e6ecb5a5bcacf59c3f912155044479af1d0b6681280048b338b28e364aca1f6", size = 408680 }, + { url = "https://files.pythonhosted.org/packages/10/3b/71b725851df9ab7a7a4e33cf36d241933da66040d195a84781f49c50490c/rpds_py-0.30.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a8fa71a2e078c527c3e9dc9fc5a98c9db40bcc8a92b4e8858e36d329f8684b51", size = 423589 }, + { url = "https://files.pythonhosted.org/packages/00/2b/e59e58c544dc9bd8bd8384ecdb8ea91f6727f0e37a7131baeff8d6f51661/rpds_py-0.30.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:73c67f2db7bc334e518d097c6d1e6fed021bbc9b7d678d6cc433478365d1d5f5", size = 573289 }, + { url = "https://files.pythonhosted.org/packages/da/3e/a18e6f5b460893172a7d6a680e86d3b6bc87a54c1f0b03446a3c8c7b588f/rpds_py-0.30.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:5ba103fb455be00f3b1c2076c9d4264bfcb037c976167a6047ed82f23153f02e", size = 599737 }, + { url = "https://files.pythonhosted.org/packages/5c/e2/714694e4b87b85a18e2c243614974413c60aa107fd815b8cbc42b873d1d7/rpds_py-0.30.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:7cee9c752c0364588353e627da8a7e808a66873672bcb5f52890c33fd965b394", size = 563120 }, + { url = "https://files.pythonhosted.org/packages/6f/ab/d5d5e3bcedb0a77f4f613706b750e50a5a3ba1c15ccd3665ecc636c968fd/rpds_py-0.30.0-cp312-cp312-win32.whl", hash = "sha256:1ab5b83dbcf55acc8b08fc62b796ef672c457b17dbd7820a11d6c52c06839bdf", size = 223782 }, + { url = "https://files.pythonhosted.org/packages/39/3b/f786af9957306fdc38a74cef405b7b93180f481fb48453a114bb6465744a/rpds_py-0.30.0-cp312-cp312-win_amd64.whl", hash = "sha256:a090322ca841abd453d43456ac34db46e8b05fd9b3b4ac0c78bcde8b089f959b", size = 240463 }, + { url = "https://files.pythonhosted.org/packages/f3/d2/b91dc748126c1559042cfe41990deb92c4ee3e2b415f6b5234969ffaf0cc/rpds_py-0.30.0-cp312-cp312-win_arm64.whl", hash = "sha256:669b1805bd639dd2989b281be2cfd951c6121b65e729d9b843e9639ef1fd555e", size = 230868 }, + { url = "https://files.pythonhosted.org/packages/ed/dc/d61221eb88ff410de3c49143407f6f3147acf2538c86f2ab7ce65ae7d5f9/rpds_py-0.30.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:f83424d738204d9770830d35290ff3273fbb02b41f919870479fab14b9d303b2", size = 374887 }, + { url = "https://files.pythonhosted.org/packages/fd/32/55fb50ae104061dbc564ef15cc43c013dc4a9f4527a1f4d99baddf56fe5f/rpds_py-0.30.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:e7536cd91353c5273434b4e003cbda89034d67e7710eab8761fd918ec6c69cf8", size = 358904 }, + { url = "https://files.pythonhosted.org/packages/58/70/faed8186300e3b9bdd138d0273109784eea2396c68458ed580f885dfe7ad/rpds_py-0.30.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2771c6c15973347f50fece41fc447c054b7ac2ae0502388ce3b6738cd366e3d4", size = 389945 }, + { url = "https://files.pythonhosted.org/packages/bd/a8/073cac3ed2c6387df38f71296d002ab43496a96b92c823e76f46b8af0543/rpds_py-0.30.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:0a59119fc6e3f460315fe9d08149f8102aa322299deaa5cab5b40092345c2136", size = 407783 }, + { url = "https://files.pythonhosted.org/packages/77/57/5999eb8c58671f1c11eba084115e77a8899d6e694d2a18f69f0ba471ec8b/rpds_py-0.30.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:76fec018282b4ead0364022e3c54b60bf368b9d926877957a8624b58419169b7", size = 515021 }, + { url = "https://files.pythonhosted.org/packages/e0/af/5ab4833eadc36c0a8ed2bc5c0de0493c04f6c06de223170bd0798ff98ced/rpds_py-0.30.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:692bef75a5525db97318e8cd061542b5a79812d711ea03dbc1f6f8dbb0c5f0d2", size = 414589 }, + { url = "https://files.pythonhosted.org/packages/b7/de/f7192e12b21b9e9a68a6d0f249b4af3fdcdff8418be0767a627564afa1f1/rpds_py-0.30.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9027da1ce107104c50c81383cae773ef5c24d296dd11c99e2629dbd7967a20c6", size = 394025 }, + { url = "https://files.pythonhosted.org/packages/91/c4/fc70cd0249496493500e7cc2de87504f5aa6509de1e88623431fec76d4b6/rpds_py-0.30.0-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:9cf69cdda1f5968a30a359aba2f7f9aa648a9ce4b580d6826437f2b291cfc86e", size = 408895 }, + { url = "https://files.pythonhosted.org/packages/58/95/d9275b05ab96556fefff73a385813eb66032e4c99f411d0795372d9abcea/rpds_py-0.30.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a4796a717bf12b9da9d3ad002519a86063dcac8988b030e405704ef7d74d2d9d", size = 422799 }, + { url = "https://files.pythonhosted.org/packages/06/c1/3088fc04b6624eb12a57eb814f0d4997a44b0d208d6cace713033ff1a6ba/rpds_py-0.30.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:5d4c2aa7c50ad4728a094ebd5eb46c452e9cb7edbfdb18f9e1221f597a73e1e7", size = 572731 }, + { url = "https://files.pythonhosted.org/packages/d8/42/c612a833183b39774e8ac8fecae81263a68b9583ee343db33ab571a7ce55/rpds_py-0.30.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ba81a9203d07805435eb06f536d95a266c21e5b2dfbf6517748ca40c98d19e31", size = 599027 }, + { url = "https://files.pythonhosted.org/packages/5f/60/525a50f45b01d70005403ae0e25f43c0384369ad24ffe46e8d9068b50086/rpds_py-0.30.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:945dccface01af02675628334f7cf49c2af4c1c904748efc5cf7bbdf0b579f95", size = 563020 }, + { url = "https://files.pythonhosted.org/packages/0b/5d/47c4655e9bcd5ca907148535c10e7d489044243cc9941c16ed7cd53be91d/rpds_py-0.30.0-cp313-cp313-win32.whl", hash = "sha256:b40fb160a2db369a194cb27943582b38f79fc4887291417685f3ad693c5a1d5d", size = 223139 }, + { url = "https://files.pythonhosted.org/packages/f2/e1/485132437d20aa4d3e1d8b3fb5a5e65aa8139f1e097080c2a8443201742c/rpds_py-0.30.0-cp313-cp313-win_amd64.whl", hash = "sha256:806f36b1b605e2d6a72716f321f20036b9489d29c51c91f4dd29a3e3afb73b15", size = 240224 }, + { url = "https://files.pythonhosted.org/packages/24/95/ffd128ed1146a153d928617b0ef673960130be0009c77d8fbf0abe306713/rpds_py-0.30.0-cp313-cp313-win_arm64.whl", hash = "sha256:d96c2086587c7c30d44f31f42eae4eac89b60dabbac18c7669be3700f13c3ce1", size = 230645 }, + { url = "https://files.pythonhosted.org/packages/ff/1b/b10de890a0def2a319a2626334a7f0ae388215eb60914dbac8a3bae54435/rpds_py-0.30.0-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:eb0b93f2e5c2189ee831ee43f156ed34e2a89a78a66b98cadad955972548be5a", size = 364443 }, + { url = "https://files.pythonhosted.org/packages/0d/bf/27e39f5971dc4f305a4fb9c672ca06f290f7c4e261c568f3dea16a410d47/rpds_py-0.30.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:922e10f31f303c7c920da8981051ff6d8c1a56207dbdf330d9047f6d30b70e5e", size = 353375 }, + { url = "https://files.pythonhosted.org/packages/40/58/442ada3bba6e8e6615fc00483135c14a7538d2ffac30e2d933ccf6852232/rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cdc62c8286ba9bf7f47befdcea13ea0e26bf294bda99758fd90535cbaf408000", size = 383850 }, + { url = "https://files.pythonhosted.org/packages/14/14/f59b0127409a33c6ef6f5c1ebd5ad8e32d7861c9c7adfa9a624fc3889f6c/rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:47f9a91efc418b54fb8190a6b4aa7813a23fb79c51f4bb84e418f5476c38b8db", size = 392812 }, + { url = "https://files.pythonhosted.org/packages/b3/66/e0be3e162ac299b3a22527e8913767d869e6cc75c46bd844aa43fb81ab62/rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1f3587eb9b17f3789ad50824084fa6f81921bbf9a795826570bda82cb3ed91f2", size = 517841 }, + { url = "https://files.pythonhosted.org/packages/3d/55/fa3b9cf31d0c963ecf1ba777f7cf4b2a2c976795ac430d24a1f43d25a6ba/rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:39c02563fc592411c2c61d26b6c5fe1e51eaa44a75aa2c8735ca88b0d9599daa", size = 408149 }, + { url = "https://files.pythonhosted.org/packages/60/ca/780cf3b1a32b18c0f05c441958d3758f02544f1d613abf9488cd78876378/rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:51a1234d8febafdfd33a42d97da7a43f5dcb120c1060e352a3fbc0c6d36e2083", size = 383843 }, + { url = "https://files.pythonhosted.org/packages/82/86/d5f2e04f2aa6247c613da0c1dd87fcd08fa17107e858193566048a1e2f0a/rpds_py-0.30.0-cp313-cp313t-manylinux_2_31_riscv64.whl", hash = "sha256:eb2c4071ab598733724c08221091e8d80e89064cd472819285a9ab0f24bcedb9", size = 396507 }, + { url = "https://files.pythonhosted.org/packages/4b/9a/453255d2f769fe44e07ea9785c8347edaf867f7026872e76c1ad9f7bed92/rpds_py-0.30.0-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6bdfdb946967d816e6adf9a3d8201bfad269c67efe6cefd7093ef959683c8de0", size = 414949 }, + { url = "https://files.pythonhosted.org/packages/a3/31/622a86cdc0c45d6df0e9ccb6becdba5074735e7033c20e401a6d9d0e2ca0/rpds_py-0.30.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:c77afbd5f5250bf27bf516c7c4a016813eb2d3e116139aed0096940c5982da94", size = 565790 }, + { url = "https://files.pythonhosted.org/packages/1c/5d/15bbf0fb4a3f58a3b1c67855ec1efcc4ceaef4e86644665fff03e1b66d8d/rpds_py-0.30.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:61046904275472a76c8c90c9ccee9013d70a6d0f73eecefd38c1ae7c39045a08", size = 590217 }, + { url = "https://files.pythonhosted.org/packages/6d/61/21b8c41f68e60c8cc3b2e25644f0e3681926020f11d06ab0b78e3c6bbff1/rpds_py-0.30.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:4c5f36a861bc4b7da6516dbdf302c55313afa09b81931e8280361a4f6c9a2d27", size = 555806 }, + { url = "https://files.pythonhosted.org/packages/f9/39/7e067bb06c31de48de3eb200f9fc7c58982a4d3db44b07e73963e10d3be9/rpds_py-0.30.0-cp313-cp313t-win32.whl", hash = "sha256:3d4a69de7a3e50ffc214ae16d79d8fbb0922972da0356dcf4d0fdca2878559c6", size = 211341 }, + { url = "https://files.pythonhosted.org/packages/0a/4d/222ef0b46443cf4cf46764d9c630f3fe4abaa7245be9417e56e9f52b8f65/rpds_py-0.30.0-cp313-cp313t-win_amd64.whl", hash = "sha256:f14fc5df50a716f7ece6a80b6c78bb35ea2ca47c499e422aa4463455dd96d56d", size = 225768 }, + { url = "https://files.pythonhosted.org/packages/86/81/dad16382ebbd3d0e0328776d8fd7ca94220e4fa0798d1dc5e7da48cb3201/rpds_py-0.30.0-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:68f19c879420aa08f61203801423f6cd5ac5f0ac4ac82a2368a9fcd6a9a075e0", size = 362099 }, + { url = "https://files.pythonhosted.org/packages/2b/60/19f7884db5d5603edf3c6bce35408f45ad3e97e10007df0e17dd57af18f8/rpds_py-0.30.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:ec7c4490c672c1a0389d319b3a9cfcd098dcdc4783991553c332a15acf7249be", size = 353192 }, + { url = "https://files.pythonhosted.org/packages/bf/c4/76eb0e1e72d1a9c4703c69607cec123c29028bff28ce41588792417098ac/rpds_py-0.30.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f251c812357a3fed308d684a5079ddfb9d933860fc6de89f2b7ab00da481e65f", size = 384080 }, + { url = "https://files.pythonhosted.org/packages/72/87/87ea665e92f3298d1b26d78814721dc39ed8d2c74b86e83348d6b48a6f31/rpds_py-0.30.0-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ac98b175585ecf4c0348fd7b29c3864bda53b805c773cbf7bfdaffc8070c976f", size = 394841 }, + { url = "https://files.pythonhosted.org/packages/77/ad/7783a89ca0587c15dcbf139b4a8364a872a25f861bdb88ed99f9b0dec985/rpds_py-0.30.0-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3e62880792319dbeb7eb866547f2e35973289e7d5696c6e295476448f5b63c87", size = 516670 }, + { url = "https://files.pythonhosted.org/packages/5b/3c/2882bdac942bd2172f3da574eab16f309ae10a3925644e969536553cb4ee/rpds_py-0.30.0-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4e7fc54e0900ab35d041b0601431b0a0eb495f0851a0639b6ef90f7741b39a18", size = 408005 }, + { url = "https://files.pythonhosted.org/packages/ce/81/9a91c0111ce1758c92516a3e44776920b579d9a7c09b2b06b642d4de3f0f/rpds_py-0.30.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47e77dc9822d3ad616c3d5759ea5631a75e5809d5a28707744ef79d7a1bcfcad", size = 382112 }, + { url = "https://files.pythonhosted.org/packages/cf/8e/1da49d4a107027e5fbc64daeab96a0706361a2918da10cb41769244b805d/rpds_py-0.30.0-cp314-cp314-manylinux_2_31_riscv64.whl", hash = "sha256:b4dc1a6ff022ff85ecafef7979a2c6eb423430e05f1165d6688234e62ba99a07", size = 399049 }, + { url = "https://files.pythonhosted.org/packages/df/5a/7ee239b1aa48a127570ec03becbb29c9d5a9eb092febbd1699d567cae859/rpds_py-0.30.0-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4559c972db3a360808309e06a74628b95eaccbf961c335c8fe0d590cf587456f", size = 415661 }, + { url = "https://files.pythonhosted.org/packages/70/ea/caa143cf6b772f823bc7929a45da1fa83569ee49b11d18d0ada7f5ee6fd6/rpds_py-0.30.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:0ed177ed9bded28f8deb6ab40c183cd1192aa0de40c12f38be4d59cd33cb5c65", size = 565606 }, + { url = "https://files.pythonhosted.org/packages/64/91/ac20ba2d69303f961ad8cf55bf7dbdb4763f627291ba3d0d7d67333cced9/rpds_py-0.30.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:ad1fa8db769b76ea911cb4e10f049d80bf518c104f15b3edb2371cc65375c46f", size = 591126 }, + { url = "https://files.pythonhosted.org/packages/21/20/7ff5f3c8b00c8a95f75985128c26ba44503fb35b8e0259d812766ea966c7/rpds_py-0.30.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:46e83c697b1f1c72b50e5ee5adb4353eef7406fb3f2043d64c33f20ad1c2fc53", size = 553371 }, + { url = "https://files.pythonhosted.org/packages/72/c7/81dadd7b27c8ee391c132a6b192111ca58d866577ce2d9b0ca157552cce0/rpds_py-0.30.0-cp314-cp314-win32.whl", hash = "sha256:ee454b2a007d57363c2dfd5b6ca4a5d7e2c518938f8ed3b706e37e5d470801ed", size = 215298 }, + { url = "https://files.pythonhosted.org/packages/3e/d2/1aaac33287e8cfb07aab2e6b8ac1deca62f6f65411344f1433c55e6f3eb8/rpds_py-0.30.0-cp314-cp314-win_amd64.whl", hash = "sha256:95f0802447ac2d10bcc69f6dc28fe95fdf17940367b21d34e34c737870758950", size = 228604 }, + { url = "https://files.pythonhosted.org/packages/e8/95/ab005315818cc519ad074cb7784dae60d939163108bd2b394e60dc7b5461/rpds_py-0.30.0-cp314-cp314-win_arm64.whl", hash = "sha256:613aa4771c99f03346e54c3f038e4cc574ac09a3ddfb0e8878487335e96dead6", size = 222391 }, + { url = "https://files.pythonhosted.org/packages/9e/68/154fe0194d83b973cdedcdcc88947a2752411165930182ae41d983dcefa6/rpds_py-0.30.0-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:7e6ecfcb62edfd632e56983964e6884851786443739dbfe3582947e87274f7cb", size = 364868 }, + { url = "https://files.pythonhosted.org/packages/83/69/8bbc8b07ec854d92a8b75668c24d2abcb1719ebf890f5604c61c9369a16f/rpds_py-0.30.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:a1d0bc22a7cdc173fedebb73ef81e07faef93692b8c1ad3733b67e31e1b6e1b8", size = 353747 }, + { url = "https://files.pythonhosted.org/packages/ab/00/ba2e50183dbd9abcce9497fa5149c62b4ff3e22d338a30d690f9af970561/rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0d08f00679177226c4cb8c5265012eea897c8ca3b93f429e546600c971bcbae7", size = 383795 }, + { url = "https://files.pythonhosted.org/packages/05/6f/86f0272b84926bcb0e4c972262f54223e8ecc556b3224d281e6598fc9268/rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5965af57d5848192c13534f90f9dd16464f3c37aaf166cc1da1cae1fd5a34898", size = 393330 }, + { url = "https://files.pythonhosted.org/packages/cb/e9/0e02bb2e6dc63d212641da45df2b0bf29699d01715913e0d0f017ee29438/rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9a4e86e34e9ab6b667c27f3211ca48f73dba7cd3d90f8d5b11be56e5dbc3fb4e", size = 518194 }, + { url = "https://files.pythonhosted.org/packages/ee/ca/be7bca14cf21513bdf9c0606aba17d1f389ea2b6987035eb4f62bd923f25/rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e5d3e6b26f2c785d65cc25ef1e5267ccbe1b069c5c21b8cc724efee290554419", size = 408340 }, + { url = "https://files.pythonhosted.org/packages/c2/c7/736e00ebf39ed81d75544c0da6ef7b0998f8201b369acf842f9a90dc8fce/rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:626a7433c34566535b6e56a1b39a7b17ba961e97ce3b80ec62e6f1312c025551", size = 383765 }, + { url = "https://files.pythonhosted.org/packages/4a/3f/da50dfde9956aaf365c4adc9533b100008ed31aea635f2b8d7b627e25b49/rpds_py-0.30.0-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:acd7eb3f4471577b9b5a41baf02a978e8bdeb08b4b355273994f8b87032000a8", size = 396834 }, + { url = "https://files.pythonhosted.org/packages/4e/00/34bcc2565b6020eab2623349efbdec810676ad571995911f1abdae62a3a0/rpds_py-0.30.0-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:fe5fa731a1fa8a0a56b0977413f8cacac1768dad38d16b3a296712709476fbd5", size = 415470 }, + { url = "https://files.pythonhosted.org/packages/8c/28/882e72b5b3e6f718d5453bd4d0d9cf8df36fddeb4ddbbab17869d5868616/rpds_py-0.30.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:74a3243a411126362712ee1524dfc90c650a503502f135d54d1b352bd01f2404", size = 565630 }, + { url = "https://files.pythonhosted.org/packages/3b/97/04a65539c17692de5b85c6e293520fd01317fd878ea1995f0367d4532fb1/rpds_py-0.30.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:3e8eeb0544f2eb0d2581774be4c3410356eba189529a6b3e36bbbf9696175856", size = 591148 }, + { url = "https://files.pythonhosted.org/packages/85/70/92482ccffb96f5441aab93e26c4d66489eb599efdcf96fad90c14bbfb976/rpds_py-0.30.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:dbd936cde57abfee19ab3213cf9c26be06d60750e60a8e4dd85d1ab12c8b1f40", size = 556030 }, + { url = "https://files.pythonhosted.org/packages/20/53/7c7e784abfa500a2b6b583b147ee4bb5a2b3747a9166bab52fec4b5b5e7d/rpds_py-0.30.0-cp314-cp314t-win32.whl", hash = "sha256:dc824125c72246d924f7f796b4f63c1e9dc810c7d9e2355864b3c3a73d59ade0", size = 211570 }, + { url = "https://files.pythonhosted.org/packages/d0/02/fa464cdfbe6b26e0600b62c528b72d8608f5cc49f96b8d6e38c95d60c676/rpds_py-0.30.0-cp314-cp314t-win_amd64.whl", hash = "sha256:27f4b0e92de5bfbc6f86e43959e6edd1425c33b5e69aab0984a72047f2bcf1e3", size = 226532 }, + { url = "https://files.pythonhosted.org/packages/69/71/3f34339ee70521864411f8b6992e7ab13ac30d8e4e3309e07c7361767d91/rpds_py-0.30.0-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:c2262bdba0ad4fc6fb5545660673925c2d2a5d9e2e0fb603aad545427be0fc58", size = 372292 }, + { url = "https://files.pythonhosted.org/packages/57/09/f183df9b8f2d66720d2ef71075c59f7e1b336bec7ee4c48f0a2b06857653/rpds_py-0.30.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:ee6af14263f25eedc3bb918a3c04245106a42dfd4f5c2285ea6f997b1fc3f89a", size = 362128 }, + { url = "https://files.pythonhosted.org/packages/7a/68/5c2594e937253457342e078f0cc1ded3dd7b2ad59afdbf2d354869110a02/rpds_py-0.30.0-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3adbb8179ce342d235c31ab8ec511e66c73faa27a47e076ccc92421add53e2bb", size = 391542 }, + { url = "https://files.pythonhosted.org/packages/49/5c/31ef1afd70b4b4fbdb2800249f34c57c64beb687495b10aec0365f53dfc4/rpds_py-0.30.0-pp311-pypy311_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:250fa00e9543ac9b97ac258bd37367ff5256666122c2d0f2bc97577c60a1818c", size = 404004 }, + { url = "https://files.pythonhosted.org/packages/e3/63/0cfbea38d05756f3440ce6534d51a491d26176ac045e2707adc99bb6e60a/rpds_py-0.30.0-pp311-pypy311_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9854cf4f488b3d57b9aaeb105f06d78e5529d3145b1e4a41750167e8c213c6d3", size = 527063 }, + { url = "https://files.pythonhosted.org/packages/42/e6/01e1f72a2456678b0f618fc9a1a13f882061690893c192fcad9f2926553a/rpds_py-0.30.0-pp311-pypy311_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:993914b8e560023bc0a8bf742c5f303551992dcb85e247b1e5c7f4a7d145bda5", size = 413099 }, + { url = "https://files.pythonhosted.org/packages/b8/25/8df56677f209003dcbb180765520c544525e3ef21ea72279c98b9aa7c7fb/rpds_py-0.30.0-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:58edca431fb9b29950807e301826586e5bbf24163677732429770a697ffe6738", size = 392177 }, + { url = "https://files.pythonhosted.org/packages/4a/b4/0a771378c5f16f8115f796d1f437950158679bcd2a7c68cf251cfb00ed5b/rpds_py-0.30.0-pp311-pypy311_pp73-manylinux_2_31_riscv64.whl", hash = "sha256:dea5b552272a944763b34394d04577cf0f9bd013207bc32323b5a89a53cf9c2f", size = 406015 }, + { url = "https://files.pythonhosted.org/packages/36/d8/456dbba0af75049dc6f63ff295a2f92766b9d521fa00de67a2bd6427d57a/rpds_py-0.30.0-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ba3af48635eb83d03f6c9735dfb21785303e73d22ad03d489e88adae6eab8877", size = 423736 }, + { url = "https://files.pythonhosted.org/packages/13/64/b4d76f227d5c45a7e0b796c674fd81b0a6c4fbd48dc29271857d8219571c/rpds_py-0.30.0-pp311-pypy311_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:dff13836529b921e22f15cb099751209a60009731a68519630a24d61f0b1b30a", size = 573981 }, + { url = "https://files.pythonhosted.org/packages/20/91/092bacadeda3edf92bf743cc96a7be133e13a39cdbfd7b5082e7ab638406/rpds_py-0.30.0-pp311-pypy311_pp73-musllinux_1_2_i686.whl", hash = "sha256:1b151685b23929ab7beec71080a8889d4d6d9fa9a983d213f07121205d48e2c4", size = 599782 }, + { url = "https://files.pythonhosted.org/packages/d1/b7/b95708304cd49b7b6f82fdd039f1748b66ec2b21d6a45180910802f1abf1/rpds_py-0.30.0-pp311-pypy311_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:ac37f9f516c51e5753f27dfdef11a88330f04de2d564be3991384b2f3535d02e", size = 562191 }, +] + +[[package]] +name = "shellingham" +version = "1.5.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/58/15/8b3609fd3830ef7b27b655beb4b4e9c62313a4e8da8c676e142cc210d58e/shellingham-1.5.4.tar.gz", hash = "sha256:8dbca0739d487e5bd35ab3ca4b36e11c4078f3a234bfce294b0a0291363404de", size = 10310 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e0/f9/0595336914c5619e5f28a1fb793285925a8cd4b432c9da0a987836c7f822/shellingham-1.5.4-py2.py3-none-any.whl", hash = "sha256:7ecfff8f2fd72616f7481040475a65b2bf8af90a56c89140852d1120324e8686", size = 9755 }, +] + +[[package]] +name = "sniffio" +version = "1.3.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a2/87/a6771e1546d97e7e041b6ae58d80074f81b7d5121207425c964ddf5cfdbd/sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc", size = 20372 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2", size = 10235 }, +] + +[[package]] +name = "tiktoken" +version = "0.12.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "regex" }, + { name = "requests" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/7d/ab/4d017d0f76ec3171d469d80fc03dfbb4e48a4bcaddaa831b31d526f05edc/tiktoken-0.12.0.tar.gz", hash = "sha256:b18ba7ee2b093863978fcb14f74b3707cdc8d4d4d3836853ce7ec60772139931", size = 37806 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/de/46/21ea696b21f1d6d1efec8639c204bdf20fde8bafb351e1355c72c5d7de52/tiktoken-0.12.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:6e227c7f96925003487c33b1b32265fad2fbcec2b7cf4817afb76d416f40f6bb", size = 1051565 }, + { url = "https://files.pythonhosted.org/packages/c9/d9/35c5d2d9e22bb2a5f74ba48266fb56c63d76ae6f66e02feb628671c0283e/tiktoken-0.12.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c06cf0fcc24c2cb2adb5e185c7082a82cba29c17575e828518c2f11a01f445aa", size = 995284 }, + { url = "https://files.pythonhosted.org/packages/01/84/961106c37b8e49b9fdcf33fe007bb3a8fdcc380c528b20cc7fbba80578b8/tiktoken-0.12.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:f18f249b041851954217e9fd8e5c00b024ab2315ffda5ed77665a05fa91f42dc", size = 1129201 }, + { url = "https://files.pythonhosted.org/packages/6a/d0/3d9275198e067f8b65076a68894bb52fd253875f3644f0a321a720277b8a/tiktoken-0.12.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:47a5bc270b8c3db00bb46ece01ef34ad050e364b51d406b6f9730b64ac28eded", size = 1152444 }, + { url = "https://files.pythonhosted.org/packages/78/db/a58e09687c1698a7c592e1038e01c206569b86a0377828d51635561f8ebf/tiktoken-0.12.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:508fa71810c0efdcd1b898fda574889ee62852989f7c1667414736bcb2b9a4bd", size = 1195080 }, + { url = "https://files.pythonhosted.org/packages/9e/1b/a9e4d2bf91d515c0f74afc526fd773a812232dd6cda33ebea7f531202325/tiktoken-0.12.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:a1af81a6c44f008cba48494089dd98cccb8b313f55e961a52f5b222d1e507967", size = 1255240 }, + { url = "https://files.pythonhosted.org/packages/9d/15/963819345f1b1fb0809070a79e9dd96938d4ca41297367d471733e79c76c/tiktoken-0.12.0-cp311-cp311-win_amd64.whl", hash = "sha256:3e68e3e593637b53e56f7237be560f7a394451cb8c11079755e80ae64b9e6def", size = 879422 }, + { url = "https://files.pythonhosted.org/packages/a4/85/be65d39d6b647c79800fd9d29241d081d4eeb06271f383bb87200d74cf76/tiktoken-0.12.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b97f74aca0d78a1ff21b8cd9e9925714c15a9236d6ceacf5c7327c117e6e21e8", size = 1050728 }, + { url = "https://files.pythonhosted.org/packages/4a/42/6573e9129bc55c9bf7300b3a35bef2c6b9117018acca0dc760ac2d93dffe/tiktoken-0.12.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2b90f5ad190a4bb7c3eb30c5fa32e1e182ca1ca79f05e49b448438c3e225a49b", size = 994049 }, + { url = "https://files.pythonhosted.org/packages/66/c5/ed88504d2f4a5fd6856990b230b56d85a777feab84e6129af0822f5d0f70/tiktoken-0.12.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:65b26c7a780e2139e73acc193e5c63ac754021f160df919add909c1492c0fb37", size = 1129008 }, + { url = "https://files.pythonhosted.org/packages/f4/90/3dae6cc5436137ebd38944d396b5849e167896fc2073da643a49f372dc4f/tiktoken-0.12.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:edde1ec917dfd21c1f2f8046b86348b0f54a2c0547f68149d8600859598769ad", size = 1152665 }, + { url = "https://files.pythonhosted.org/packages/a3/fe/26df24ce53ffde419a42f5f53d755b995c9318908288c17ec3f3448313a3/tiktoken-0.12.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:35a2f8ddd3824608b3d650a000c1ef71f730d0c56486845705a8248da00f9fe5", size = 1194230 }, + { url = "https://files.pythonhosted.org/packages/20/cc/b064cae1a0e9fac84b0d2c46b89f4e57051a5f41324e385d10225a984c24/tiktoken-0.12.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:83d16643edb7fa2c99eff2ab7733508aae1eebb03d5dfc46f5565862810f24e3", size = 1254688 }, + { url = "https://files.pythonhosted.org/packages/81/10/b8523105c590c5b8349f2587e2fdfe51a69544bd5a76295fc20f2374f470/tiktoken-0.12.0-cp312-cp312-win_amd64.whl", hash = "sha256:ffc5288f34a8bc02e1ea7047b8d041104791d2ddbf42d1e5fa07822cbffe16bd", size = 878694 }, + { url = "https://files.pythonhosted.org/packages/00/61/441588ee21e6b5cdf59d6870f86beb9789e532ee9718c251b391b70c68d6/tiktoken-0.12.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:775c2c55de2310cc1bc9a3ad8826761cbdc87770e586fd7b6da7d4589e13dab3", size = 1050802 }, + { url = "https://files.pythonhosted.org/packages/1f/05/dcf94486d5c5c8d34496abe271ac76c5b785507c8eae71b3708f1ad9b45a/tiktoken-0.12.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a01b12f69052fbe4b080a2cfb867c4de12c704b56178edf1d1d7b273561db160", size = 993995 }, + { url = "https://files.pythonhosted.org/packages/a0/70/5163fe5359b943f8db9946b62f19be2305de8c3d78a16f629d4165e2f40e/tiktoken-0.12.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:01d99484dc93b129cd0964f9d34eee953f2737301f18b3c7257bf368d7615baa", size = 1128948 }, + { url = "https://files.pythonhosted.org/packages/0c/da/c028aa0babf77315e1cef357d4d768800c5f8a6de04d0eac0f377cb619fa/tiktoken-0.12.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:4a1a4fcd021f022bfc81904a911d3df0f6543b9e7627b51411da75ff2fe7a1be", size = 1151986 }, + { url = "https://files.pythonhosted.org/packages/a0/5a/886b108b766aa53e295f7216b509be95eb7d60b166049ce2c58416b25f2a/tiktoken-0.12.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:981a81e39812d57031efdc9ec59fa32b2a5a5524d20d4776574c4b4bd2e9014a", size = 1194222 }, + { url = "https://files.pythonhosted.org/packages/f4/f8/4db272048397636ac7a078d22773dd2795b1becee7bc4922fe6207288d57/tiktoken-0.12.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9baf52f84a3f42eef3ff4e754a0db79a13a27921b457ca9832cf944c6be4f8f3", size = 1255097 }, + { url = "https://files.pythonhosted.org/packages/8e/32/45d02e2e0ea2be3a9ed22afc47d93741247e75018aac967b713b2941f8ea/tiktoken-0.12.0-cp313-cp313-win_amd64.whl", hash = "sha256:b8a0cd0c789a61f31bf44851defbd609e8dd1e2c8589c614cc1060940ef1f697", size = 879117 }, + { url = "https://files.pythonhosted.org/packages/ce/76/994fc868f88e016e6d05b0da5ac24582a14c47893f4474c3e9744283f1d5/tiktoken-0.12.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:d5f89ea5680066b68bcb797ae85219c72916c922ef0fcdd3480c7d2315ffff16", size = 1050309 }, + { url = "https://files.pythonhosted.org/packages/f6/b8/57ef1456504c43a849821920d582a738a461b76a047f352f18c0b26c6516/tiktoken-0.12.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:b4e7ed1c6a7a8a60a3230965bdedba8cc58f68926b835e519341413370e0399a", size = 993712 }, + { url = "https://files.pythonhosted.org/packages/72/90/13da56f664286ffbae9dbcfadcc625439142675845baa62715e49b87b68b/tiktoken-0.12.0-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:fc530a28591a2d74bce821d10b418b26a094bf33839e69042a6e86ddb7a7fb27", size = 1128725 }, + { url = "https://files.pythonhosted.org/packages/05/df/4f80030d44682235bdaecd7346c90f67ae87ec8f3df4a3442cb53834f7e4/tiktoken-0.12.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:06a9f4f49884139013b138920a4c393aa6556b2f8f536345f11819389c703ebb", size = 1151875 }, + { url = "https://files.pythonhosted.org/packages/22/1f/ae535223a8c4ef4c0c1192e3f9b82da660be9eb66b9279e95c99288e9dab/tiktoken-0.12.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:04f0e6a985d95913cabc96a741c5ffec525a2c72e9df086ff17ebe35985c800e", size = 1194451 }, + { url = "https://files.pythonhosted.org/packages/78/a7/f8ead382fce0243cb625c4f266e66c27f65ae65ee9e77f59ea1653b6d730/tiktoken-0.12.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:0ee8f9ae00c41770b5f9b0bb1235474768884ae157de3beb5439ca0fd70f3e25", size = 1253794 }, + { url = "https://files.pythonhosted.org/packages/93/e0/6cc82a562bc6365785a3ff0af27a2a092d57c47d7a81d9e2295d8c36f011/tiktoken-0.12.0-cp313-cp313t-win_amd64.whl", hash = "sha256:dc2dd125a62cb2b3d858484d6c614d136b5b848976794edfb63688d539b8b93f", size = 878777 }, + { url = "https://files.pythonhosted.org/packages/72/05/3abc1db5d2c9aadc4d2c76fa5640134e475e58d9fbb82b5c535dc0de9b01/tiktoken-0.12.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:a90388128df3b3abeb2bfd1895b0681412a8d7dc644142519e6f0a97c2111646", size = 1050188 }, + { url = "https://files.pythonhosted.org/packages/e3/7b/50c2f060412202d6c95f32b20755c7a6273543b125c0985d6fa9465105af/tiktoken-0.12.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:da900aa0ad52247d8794e307d6446bd3cdea8e192769b56276695d34d2c9aa88", size = 993978 }, + { url = "https://files.pythonhosted.org/packages/14/27/bf795595a2b897e271771cd31cb847d479073497344c637966bdf2853da1/tiktoken-0.12.0-cp314-cp314-manylinux_2_28_aarch64.whl", hash = "sha256:285ba9d73ea0d6171e7f9407039a290ca77efcdb026be7769dccc01d2c8d7fff", size = 1129271 }, + { url = "https://files.pythonhosted.org/packages/f5/de/9341a6d7a8f1b448573bbf3425fa57669ac58258a667eb48a25dfe916d70/tiktoken-0.12.0-cp314-cp314-manylinux_2_28_x86_64.whl", hash = "sha256:d186a5c60c6a0213f04a7a802264083dea1bbde92a2d4c7069e1a56630aef830", size = 1151216 }, + { url = "https://files.pythonhosted.org/packages/75/0d/881866647b8d1be4d67cb24e50d0c26f9f807f994aa1510cb9ba2fe5f612/tiktoken-0.12.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:604831189bd05480f2b885ecd2d1986dc7686f609de48208ebbbddeea071fc0b", size = 1194860 }, + { url = "https://files.pythonhosted.org/packages/b3/1e/b651ec3059474dab649b8d5b69f5c65cd8fcd8918568c1935bd4136c9392/tiktoken-0.12.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:8f317e8530bb3a222547b85a58583238c8f74fd7a7408305f9f63246d1a0958b", size = 1254567 }, + { url = "https://files.pythonhosted.org/packages/80/57/ce64fd16ac390fafde001268c364d559447ba09b509181b2808622420eec/tiktoken-0.12.0-cp314-cp314-win_amd64.whl", hash = "sha256:399c3dd672a6406719d84442299a490420b458c44d3ae65516302a99675888f3", size = 921067 }, + { url = "https://files.pythonhosted.org/packages/ac/a4/72eed53e8976a099539cdd5eb36f241987212c29629d0a52c305173e0a68/tiktoken-0.12.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:c2c714c72bc00a38ca969dae79e8266ddec999c7ceccd603cc4f0d04ccd76365", size = 1050473 }, + { url = "https://files.pythonhosted.org/packages/e6/d7/0110b8f54c008466b19672c615f2168896b83706a6611ba6e47313dbc6e9/tiktoken-0.12.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:cbb9a3ba275165a2cb0f9a83f5d7025afe6b9d0ab01a22b50f0e74fee2ad253e", size = 993855 }, + { url = "https://files.pythonhosted.org/packages/5f/77/4f268c41a3957c418b084dd576ea2fad2e95da0d8e1ab705372892c2ca22/tiktoken-0.12.0-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:dfdfaa5ffff8993a3af94d1125870b1d27aed7cb97aa7eb8c1cefdbc87dbee63", size = 1129022 }, + { url = "https://files.pythonhosted.org/packages/4e/2b/fc46c90fe5028bd094cd6ee25a7db321cb91d45dc87531e2bdbb26b4867a/tiktoken-0.12.0-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:584c3ad3d0c74f5269906eb8a659c8bfc6144a52895d9261cdaf90a0ae5f4de0", size = 1150736 }, + { url = "https://files.pythonhosted.org/packages/28/c0/3c7a39ff68022ddfd7d93f3337ad90389a342f761c4d71de99a3ccc57857/tiktoken-0.12.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:54c891b416a0e36b8e2045b12b33dd66fb34a4fe7965565f1b482da50da3e86a", size = 1194908 }, + { url = "https://files.pythonhosted.org/packages/ab/0d/c1ad6f4016a3968c048545f5d9b8ffebf577774b2ede3e2e352553b685fe/tiktoken-0.12.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:5edb8743b88d5be814b1a8a8854494719080c28faaa1ccbef02e87354fe71ef0", size = 1253706 }, + { url = "https://files.pythonhosted.org/packages/af/df/c7891ef9d2712ad774777271d39fdef63941ffba0a9d59b7ad1fd2765e57/tiktoken-0.12.0-cp314-cp314t-win_amd64.whl", hash = "sha256:f61c0aea5565ac82e2ec50a05e02a6c44734e91b51c10510b084ea1b8e633a71", size = 920667 }, +] + +[[package]] +name = "tokenizers" +version = "0.22.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "huggingface-hub" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/73/6f/f80cfef4a312e1fb34baf7d85c72d4411afde10978d4657f8cdd811d3ccc/tokenizers-0.22.2.tar.gz", hash = "sha256:473b83b915e547aa366d1eee11806deaf419e17be16310ac0a14077f1e28f917", size = 372115 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/92/97/5dbfabf04c7e348e655e907ed27913e03db0923abb5dfdd120d7b25630e1/tokenizers-0.22.2-cp39-abi3-macosx_10_12_x86_64.whl", hash = "sha256:544dd704ae7238755d790de45ba8da072e9af3eea688f698b137915ae959281c", size = 3100275 }, + { url = "https://files.pythonhosted.org/packages/2e/47/174dca0502ef88b28f1c9e06b73ce33500eedfac7a7692108aec220464e7/tokenizers-0.22.2-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:1e418a55456beedca4621dbab65a318981467a2b188e982a23e117f115ce5001", size = 2981472 }, + { url = "https://files.pythonhosted.org/packages/d6/84/7990e799f1309a8b87af6b948f31edaa12a3ed22d11b352eaf4f4b2e5753/tokenizers-0.22.2-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2249487018adec45d6e3554c71d46eb39fa8ea67156c640f7513eb26f318cec7", size = 3290736 }, + { url = "https://files.pythonhosted.org/packages/78/59/09d0d9ba94dcd5f4f1368d4858d24546b4bdc0231c2354aa31d6199f0399/tokenizers-0.22.2-cp39-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:25b85325d0815e86e0bac263506dd114578953b7b53d7de09a6485e4a160a7dd", size = 3168835 }, + { url = "https://files.pythonhosted.org/packages/47/50/b3ebb4243e7160bda8d34b731e54dd8ab8b133e50775872e7a434e524c28/tokenizers-0.22.2-cp39-abi3-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bfb88f22a209ff7b40a576d5324bf8286b519d7358663db21d6246fb17eea2d5", size = 3521673 }, + { url = "https://files.pythonhosted.org/packages/e0/fa/89f4cb9e08df770b57adb96f8cbb7e22695a4cb6c2bd5f0c4f0ebcf33b66/tokenizers-0.22.2-cp39-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1c774b1276f71e1ef716e5486f21e76333464f47bece56bbd554485982a9e03e", size = 3724818 }, + { url = "https://files.pythonhosted.org/packages/64/04/ca2363f0bfbe3b3d36e95bf67e56a4c88c8e3362b658e616d1ac185d47f2/tokenizers-0.22.2-cp39-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:df6c4265b289083bf710dff49bc51ef252f9d5be33a45ee2bed151114a56207b", size = 3379195 }, + { url = "https://files.pythonhosted.org/packages/2e/76/932be4b50ef6ccedf9d3c6639b056a967a86258c6d9200643f01269211ca/tokenizers-0.22.2-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:369cc9fc8cc10cb24143873a0d95438bb8ee257bb80c71989e3ee290e8d72c67", size = 3274982 }, + { url = "https://files.pythonhosted.org/packages/1d/28/5f9f5a4cc211b69e89420980e483831bcc29dade307955cc9dc858a40f01/tokenizers-0.22.2-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:29c30b83d8dcd061078b05ae0cb94d3c710555fbb44861139f9f83dcca3dc3e4", size = 9478245 }, + { url = "https://files.pythonhosted.org/packages/6c/fb/66e2da4704d6aadebf8cb39f1d6d1957df667ab24cff2326b77cda0dcb85/tokenizers-0.22.2-cp39-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:37ae80a28c1d3265bb1f22464c856bd23c02a05bb211e56d0c5301a435be6c1a", size = 9560069 }, + { url = "https://files.pythonhosted.org/packages/16/04/fed398b05caa87ce9b1a1bb5166645e38196081b225059a6edaff6440fac/tokenizers-0.22.2-cp39-abi3-musllinux_1_2_i686.whl", hash = "sha256:791135ee325f2336f498590eb2f11dc5c295232f288e75c99a36c5dbce63088a", size = 9899263 }, + { url = "https://files.pythonhosted.org/packages/05/a1/d62dfe7376beaaf1394917e0f8e93ee5f67fea8fcf4107501db35996586b/tokenizers-0.22.2-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:38337540fbbddff8e999d59970f3c6f35a82de10053206a7562f1ea02d046fa5", size = 10033429 }, + { url = "https://files.pythonhosted.org/packages/fd/18/a545c4ea42af3df6effd7d13d250ba77a0a86fb20393143bbb9a92e434d4/tokenizers-0.22.2-cp39-abi3-win32.whl", hash = "sha256:a6bf3f88c554a2b653af81f3204491c818ae2ac6fbc09e76ef4773351292bc92", size = 2502363 }, + { url = "https://files.pythonhosted.org/packages/65/71/0670843133a43d43070abeb1949abfdef12a86d490bea9cd9e18e37c5ff7/tokenizers-0.22.2-cp39-abi3-win_amd64.whl", hash = "sha256:c9ea31edff2968b44a88f97d784c2f16dc0729b8b143ed004699ebca91f05c48", size = 2747786 }, + { url = "https://files.pythonhosted.org/packages/72/f4/0de46cfa12cdcbcd464cc59fde36912af405696f687e53a091fb432f694c/tokenizers-0.22.2-cp39-abi3-win_arm64.whl", hash = "sha256:9ce725d22864a1e965217204946f830c37876eee3b2ba6fc6255e8e903d5fcbc", size = 2612133 }, +] + +[[package]] +name = "tqdm" +version = "4.67.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a8/4b/29b4ef32e036bb34e4ab51796dd745cdba7ed47ad142a9f4a1eb8e0c744d/tqdm-4.67.1.tar.gz", hash = "sha256:f8aef9c52c08c13a65f30ea34f4e5aac3fd1a34959879d7e59e63027286627f2", size = 169737 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d0/30/dc54f88dd4a2b5dc8a0279bdd7270e735851848b762aeb1c1184ed1f6b14/tqdm-4.67.1-py3-none-any.whl", hash = "sha256:26445eca388f82e72884e0d580d5464cd801a3ea01e63e5601bdff9ba6a48de2", size = 78540 }, +] + +[[package]] +name = "typer-slim" +version = "0.21.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "click" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/17/d4/064570dec6358aa9049d4708e4a10407d74c99258f8b2136bb8702303f1a/typer_slim-0.21.1.tar.gz", hash = "sha256:73495dd08c2d0940d611c5a8c04e91c2a0a98600cbd4ee19192255a233b6dbfd", size = 110478 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c8/0a/4aca634faf693e33004796b6cee0ae2e1dba375a800c16ab8d3eff4bb800/typer_slim-0.21.1-py3-none-any.whl", hash = "sha256:6e6c31047f171ac93cc5a973c9e617dbc5ab2bddc4d0a3135dc161b4e2020e0d", size = 47444 }, +] + +[[package]] +name = "typing-extensions" +version = "4.15.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/72/94/1a15dd82efb362ac84269196e94cf00f187f7ed21c242792a923cdb1c61f/typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466", size = 109391 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/18/67/36e9267722cc04a6b9f15c7f3441c2363321a3ea07da7ae0c0707beb2a9c/typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548", size = 44614 }, +] + +[[package]] +name = "typing-inspection" +version = "0.4.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/55/e3/70399cb7dd41c10ac53367ae42139cf4b1ca5f36bb3dc6c9d33acdb43655/typing_inspection-0.4.2.tar.gz", hash = "sha256:ba561c48a67c5958007083d386c3295464928b01faa735ab8547c5692e87f464", size = 75949 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/dc/9b/47798a6c91d8bdb567fe2698fe81e0c6b7cb7ef4d13da4114b41d239f65d/typing_inspection-0.4.2-py3-none-any.whl", hash = "sha256:4ed1cacbdc298c220f1bd249ed5287caa16f34d44ef4e9c3d0cbad5b521545e7", size = 14611 }, +] + +[[package]] +name = "urllib3" +version = "2.6.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/c7/24/5f1b3bdffd70275f6661c76461e25f024d5a38a46f04aaca912426a2b1d3/urllib3-2.6.3.tar.gz", hash = "sha256:1b62b6884944a57dbe321509ab94fd4d3b307075e0c2eae991ac71ee15ad38ed", size = 435556 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/39/08/aaaad47bc4e9dc8c725e68f9d04865dbcb2052843ff09c97b08904852d84/urllib3-2.6.3-py3-none-any.whl", hash = "sha256:bf272323e553dfb2e87d9bfd225ca7b0f467b919d7bbd355436d3fd37cb0acd4", size = 131584 }, +] + +[[package]] +name = "worldview-evals" +version = "0.1.0" +source = { editable = "." } +dependencies = [ + { name = "anthropic" }, + { name = "litellm" }, + { name = "openai" }, +] + +[package.dev-dependencies] +dev = [ + { name = "pytest" }, +] + +[package.metadata] +requires-dist = [ + { name = "anthropic", specifier = ">=0.25.0" }, + { name = "litellm", specifier = ">=1.30.0" }, + { name = "openai", specifier = ">=1.12.0" }, +] + +[package.metadata.requires-dev] +dev = [{ name = "pytest", specifier = ">=8.0" }] + +[[package]] +name = "yarl" +version = "1.22.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "idna" }, + { name = "multidict" }, + { name = "propcache" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/57/63/0c6ebca57330cd313f6102b16dd57ffaf3ec4c83403dcb45dbd15c6f3ea1/yarl-1.22.0.tar.gz", hash = "sha256:bebf8557577d4401ba8bd9ff33906f1376c877aa78d1fe216ad01b4d6745af71", size = 187169 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4d/27/5ab13fc84c76a0250afd3d26d5936349a35be56ce5785447d6c423b26d92/yarl-1.22.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:1ab72135b1f2db3fed3997d7e7dc1b80573c67138023852b6efb336a5eae6511", size = 141607 }, + { url = "https://files.pythonhosted.org/packages/6a/a1/d065d51d02dc02ce81501d476b9ed2229d9a990818332242a882d5d60340/yarl-1.22.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:669930400e375570189492dc8d8341301578e8493aec04aebc20d4717f899dd6", size = 94027 }, + { url = "https://files.pythonhosted.org/packages/c1/da/8da9f6a53f67b5106ffe902c6fa0164e10398d4e150d85838b82f424072a/yarl-1.22.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:792a2af6d58177ef7c19cbf0097aba92ca1b9cb3ffdd9c7470e156c8f9b5e028", size = 94963 }, + { url = "https://files.pythonhosted.org/packages/68/fe/2c1f674960c376e29cb0bec1249b117d11738db92a6ccc4a530b972648db/yarl-1.22.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3ea66b1c11c9150f1372f69afb6b8116f2dd7286f38e14ea71a44eee9ec51b9d", size = 368406 }, + { url = "https://files.pythonhosted.org/packages/95/26/812a540e1c3c6418fec60e9bbd38e871eaba9545e94fa5eff8f4a8e28e1e/yarl-1.22.0-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3e2daa88dc91870215961e96a039ec73e4937da13cf77ce17f9cad0c18df3503", size = 336581 }, + { url = "https://files.pythonhosted.org/packages/0b/f5/5777b19e26fdf98563985e481f8be3d8a39f8734147a6ebf459d0dab5a6b/yarl-1.22.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ba440ae430c00eee41509353628600212112cd5018d5def7e9b05ea7ac34eb65", size = 388924 }, + { url = "https://files.pythonhosted.org/packages/86/08/24bd2477bd59c0bbd994fe1d93b126e0472e4e3df5a96a277b0a55309e89/yarl-1.22.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:e6438cc8f23a9c1478633d216b16104a586b9761db62bfacb6425bac0a36679e", size = 392890 }, + { url = "https://files.pythonhosted.org/packages/46/00/71b90ed48e895667ecfb1eaab27c1523ee2fa217433ed77a73b13205ca4b/yarl-1.22.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4c52a6e78aef5cf47a98ef8e934755abf53953379b7d53e68b15ff4420e6683d", size = 365819 }, + { url = "https://files.pythonhosted.org/packages/30/2d/f715501cae832651d3282387c6a9236cd26bd00d0ff1e404b3dc52447884/yarl-1.22.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:3b06bcadaac49c70f4c88af4ffcfbe3dc155aab3163e75777818092478bcbbe7", size = 363601 }, + { url = "https://files.pythonhosted.org/packages/f8/f9/a678c992d78e394e7126ee0b0e4e71bd2775e4334d00a9278c06a6cce96a/yarl-1.22.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:6944b2dc72c4d7f7052683487e3677456050ff77fcf5e6204e98caf785ad1967", size = 358072 }, + { url = "https://files.pythonhosted.org/packages/2c/d1/b49454411a60edb6fefdcad4f8e6dbba7d8019e3a508a1c5836cba6d0781/yarl-1.22.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:d5372ca1df0f91a86b047d1277c2aaf1edb32d78bbcefffc81b40ffd18f027ed", size = 385311 }, + { url = "https://files.pythonhosted.org/packages/87/e5/40d7a94debb8448c7771a916d1861d6609dddf7958dc381117e7ba36d9e8/yarl-1.22.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:51af598701f5299012b8416486b40fceef8c26fc87dc6d7d1f6fc30609ea0aa6", size = 381094 }, + { url = "https://files.pythonhosted.org/packages/35/d8/611cc282502381ad855448643e1ad0538957fc82ae83dfe7762c14069e14/yarl-1.22.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b266bd01fedeffeeac01a79ae181719ff848a5a13ce10075adbefc8f1daee70e", size = 370944 }, + { url = "https://files.pythonhosted.org/packages/2d/df/fadd00fb1c90e1a5a8bd731fa3d3de2e165e5a3666a095b04e31b04d9cb6/yarl-1.22.0-cp311-cp311-win32.whl", hash = "sha256:a9b1ba5610a4e20f655258d5a1fdc7ebe3d837bb0e45b581398b99eb98b1f5ca", size = 81804 }, + { url = "https://files.pythonhosted.org/packages/b5/f7/149bb6f45f267cb5c074ac40c01c6b3ea6d8a620d34b337f6321928a1b4d/yarl-1.22.0-cp311-cp311-win_amd64.whl", hash = "sha256:078278b9b0b11568937d9509b589ee83ef98ed6d561dfe2020e24a9fd08eaa2b", size = 86858 }, + { url = "https://files.pythonhosted.org/packages/2b/13/88b78b93ad3f2f0b78e13bfaaa24d11cbc746e93fe76d8c06bf139615646/yarl-1.22.0-cp311-cp311-win_arm64.whl", hash = "sha256:b6a6f620cfe13ccec221fa312139135166e47ae169f8253f72a0abc0dae94376", size = 81637 }, + { url = "https://files.pythonhosted.org/packages/75/ff/46736024fee3429b80a165a732e38e5d5a238721e634ab41b040d49f8738/yarl-1.22.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:e340382d1afa5d32b892b3ff062436d592ec3d692aeea3bef3a5cfe11bbf8c6f", size = 142000 }, + { url = "https://files.pythonhosted.org/packages/5a/9a/b312ed670df903145598914770eb12de1bac44599549b3360acc96878df8/yarl-1.22.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:f1e09112a2c31ffe8d80be1b0988fa6a18c5d5cad92a9ffbb1c04c91bfe52ad2", size = 94338 }, + { url = "https://files.pythonhosted.org/packages/ba/f5/0601483296f09c3c65e303d60c070a5c19fcdbc72daa061e96170785bc7d/yarl-1.22.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:939fe60db294c786f6b7c2d2e121576628468f65453d86b0fe36cb52f987bd74", size = 94909 }, + { url = "https://files.pythonhosted.org/packages/60/41/9a1fe0b73dbcefce72e46cf149b0e0a67612d60bfc90fb59c2b2efdfbd86/yarl-1.22.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e1651bf8e0398574646744c1885a41198eba53dc8a9312b954073f845c90a8df", size = 372940 }, + { url = "https://files.pythonhosted.org/packages/17/7a/795cb6dfee561961c30b800f0ed616b923a2ec6258b5def2a00bf8231334/yarl-1.22.0-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:b8a0588521a26bf92a57a1705b77b8b59044cdceccac7151bd8d229e66b8dedb", size = 345825 }, + { url = "https://files.pythonhosted.org/packages/d7/93/a58f4d596d2be2ae7bab1a5846c4d270b894958845753b2c606d666744d3/yarl-1.22.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:42188e6a615c1a75bcaa6e150c3fe8f3e8680471a6b10150c5f7e83f47cc34d2", size = 386705 }, + { url = "https://files.pythonhosted.org/packages/61/92/682279d0e099d0e14d7fd2e176bd04f48de1484f56546a3e1313cd6c8e7c/yarl-1.22.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f6d2cb59377d99718913ad9a151030d6f83ef420a2b8f521d94609ecc106ee82", size = 396518 }, + { url = "https://files.pythonhosted.org/packages/db/0f/0d52c98b8a885aeda831224b78f3be7ec2e1aa4a62091f9f9188c3c65b56/yarl-1.22.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:50678a3b71c751d58d7908edc96d332af328839eea883bb554a43f539101277a", size = 377267 }, + { url = "https://files.pythonhosted.org/packages/22/42/d2685e35908cbeaa6532c1fc73e89e7f2efb5d8a7df3959ea8e37177c5a3/yarl-1.22.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1e8fbaa7cec507aa24ea27a01456e8dd4b6fab829059b69844bd348f2d467124", size = 365797 }, + { url = "https://files.pythonhosted.org/packages/a2/83/cf8c7bcc6355631762f7d8bdab920ad09b82efa6b722999dfb05afa6cfac/yarl-1.22.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:433885ab5431bc3d3d4f2f9bd15bfa1614c522b0f1405d62c4f926ccd69d04fa", size = 365535 }, + { url = "https://files.pythonhosted.org/packages/25/e1/5302ff9b28f0c59cac913b91fe3f16c59a033887e57ce9ca5d41a3a94737/yarl-1.22.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:b790b39c7e9a4192dc2e201a282109ed2985a1ddbd5ac08dc56d0e121400a8f7", size = 382324 }, + { url = "https://files.pythonhosted.org/packages/bf/cd/4617eb60f032f19ae3a688dc990d8f0d89ee0ea378b61cac81ede3e52fae/yarl-1.22.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:31f0b53913220599446872d757257be5898019c85e7971599065bc55065dc99d", size = 383803 }, + { url = "https://files.pythonhosted.org/packages/59/65/afc6e62bb506a319ea67b694551dab4a7e6fb7bf604e9bd9f3e11d575fec/yarl-1.22.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a49370e8f711daec68d09b821a34e1167792ee2d24d405cbc2387be4f158b520", size = 374220 }, + { url = "https://files.pythonhosted.org/packages/e7/3d/68bf18d50dc674b942daec86a9ba922d3113d8399b0e52b9897530442da2/yarl-1.22.0-cp312-cp312-win32.whl", hash = "sha256:70dfd4f241c04bd9239d53b17f11e6ab672b9f1420364af63e8531198e3f5fe8", size = 81589 }, + { url = "https://files.pythonhosted.org/packages/c8/9a/6ad1a9b37c2f72874f93e691b2e7ecb6137fb2b899983125db4204e47575/yarl-1.22.0-cp312-cp312-win_amd64.whl", hash = "sha256:8884d8b332a5e9b88e23f60bb166890009429391864c685e17bd73a9eda9105c", size = 87213 }, + { url = "https://files.pythonhosted.org/packages/44/c5/c21b562d1680a77634d748e30c653c3ca918beb35555cff24986fff54598/yarl-1.22.0-cp312-cp312-win_arm64.whl", hash = "sha256:ea70f61a47f3cc93bdf8b2f368ed359ef02a01ca6393916bc8ff877427181e74", size = 81330 }, + { url = "https://files.pythonhosted.org/packages/ea/f3/d67de7260456ee105dc1d162d43a019ecad6b91e2f51809d6cddaa56690e/yarl-1.22.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:8dee9c25c74997f6a750cd317b8ca63545169c098faee42c84aa5e506c819b53", size = 139980 }, + { url = "https://files.pythonhosted.org/packages/01/88/04d98af0b47e0ef42597b9b28863b9060bb515524da0a65d5f4db160b2d5/yarl-1.22.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:01e73b85a5434f89fc4fe27dcda2aff08ddf35e4d47bbbea3bdcd25321af538a", size = 93424 }, + { url = "https://files.pythonhosted.org/packages/18/91/3274b215fd8442a03975ce6bee5fe6aa57a8326b29b9d3d56234a1dca244/yarl-1.22.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:22965c2af250d20c873cdbee8ff958fb809940aeb2e74ba5f20aaf6b7ac8c70c", size = 93821 }, + { url = "https://files.pythonhosted.org/packages/61/3a/caf4e25036db0f2da4ca22a353dfeb3c9d3c95d2761ebe9b14df8fc16eb0/yarl-1.22.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b4f15793aa49793ec8d1c708ab7f9eded1aa72edc5174cae703651555ed1b601", size = 373243 }, + { url = "https://files.pythonhosted.org/packages/6e/9e/51a77ac7516e8e7803b06e01f74e78649c24ee1021eca3d6a739cb6ea49c/yarl-1.22.0-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:e5542339dcf2747135c5c85f68680353d5cb9ffd741c0f2e8d832d054d41f35a", size = 342361 }, + { url = "https://files.pythonhosted.org/packages/d4/f8/33b92454789dde8407f156c00303e9a891f1f51a0330b0fad7c909f87692/yarl-1.22.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:5c401e05ad47a75869c3ab3e35137f8468b846770587e70d71e11de797d113df", size = 387036 }, + { url = "https://files.pythonhosted.org/packages/d9/9a/c5db84ea024f76838220280f732970aa4ee154015d7f5c1bfb60a267af6f/yarl-1.22.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:243dda95d901c733f5b59214d28b0120893d91777cb8aa043e6ef059d3cddfe2", size = 397671 }, + { url = "https://files.pythonhosted.org/packages/11/c9/cd8538dc2e7727095e0c1d867bad1e40c98f37763e6d995c1939f5fdc7b1/yarl-1.22.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bec03d0d388060058f5d291a813f21c011041938a441c593374da6077fe21b1b", size = 377059 }, + { url = "https://files.pythonhosted.org/packages/a1/b9/ab437b261702ced75122ed78a876a6dec0a1b0f5e17a4ac7a9a2482d8abe/yarl-1.22.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:b0748275abb8c1e1e09301ee3cf90c8a99678a4e92e4373705f2a2570d581273", size = 365356 }, + { url = "https://files.pythonhosted.org/packages/b2/9d/8e1ae6d1d008a9567877b08f0ce4077a29974c04c062dabdb923ed98e6fe/yarl-1.22.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:47fdb18187e2a4e18fda2c25c05d8251a9e4a521edaed757fef033e7d8498d9a", size = 361331 }, + { url = "https://files.pythonhosted.org/packages/ca/5a/09b7be3905962f145b73beb468cdd53db8aa171cf18c80400a54c5b82846/yarl-1.22.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:c7044802eec4524fde550afc28edda0dd5784c4c45f0be151a2d3ba017daca7d", size = 382590 }, + { url = "https://files.pythonhosted.org/packages/aa/7f/59ec509abf90eda5048b0bc3e2d7b5099dffdb3e6b127019895ab9d5ef44/yarl-1.22.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:139718f35149ff544caba20fce6e8a2f71f1e39b92c700d8438a0b1d2a631a02", size = 385316 }, + { url = "https://files.pythonhosted.org/packages/e5/84/891158426bc8036bfdfd862fabd0e0fa25df4176ec793e447f4b85cf1be4/yarl-1.22.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e1b51bebd221006d3d2f95fbe124b22b247136647ae5dcc8c7acafba66e5ee67", size = 374431 }, + { url = "https://files.pythonhosted.org/packages/bb/49/03da1580665baa8bef5e8ed34c6df2c2aca0a2f28bf397ed238cc1bbc6f2/yarl-1.22.0-cp313-cp313-win32.whl", hash = "sha256:d3e32536234a95f513bd374e93d717cf6b2231a791758de6c509e3653f234c95", size = 81555 }, + { url = "https://files.pythonhosted.org/packages/9a/ee/450914ae11b419eadd067c6183ae08381cfdfcb9798b90b2b713bbebddda/yarl-1.22.0-cp313-cp313-win_amd64.whl", hash = "sha256:47743b82b76d89a1d20b83e60d5c20314cbd5ba2befc9cda8f28300c4a08ed4d", size = 86965 }, + { url = "https://files.pythonhosted.org/packages/98/4d/264a01eae03b6cf629ad69bae94e3b0e5344741e929073678e84bf7a3e3b/yarl-1.22.0-cp313-cp313-win_arm64.whl", hash = "sha256:5d0fcda9608875f7d052eff120c7a5da474a6796fe4d83e152e0e4d42f6d1a9b", size = 81205 }, + { url = "https://files.pythonhosted.org/packages/88/fc/6908f062a2f77b5f9f6d69cecb1747260831ff206adcbc5b510aff88df91/yarl-1.22.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:719ae08b6972befcba4310e49edb1161a88cdd331e3a694b84466bd938a6ab10", size = 146209 }, + { url = "https://files.pythonhosted.org/packages/65/47/76594ae8eab26210b4867be6f49129861ad33da1f1ebdf7051e98492bf62/yarl-1.22.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:47d8a5c446df1c4db9d21b49619ffdba90e77c89ec6e283f453856c74b50b9e3", size = 95966 }, + { url = "https://files.pythonhosted.org/packages/ab/ce/05e9828a49271ba6b5b038b15b3934e996980dd78abdfeb52a04cfb9467e/yarl-1.22.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:cfebc0ac8333520d2d0423cbbe43ae43c8838862ddb898f5ca68565e395516e9", size = 97312 }, + { url = "https://files.pythonhosted.org/packages/d1/c5/7dffad5e4f2265b29c9d7ec869c369e4223166e4f9206fc2243ee9eea727/yarl-1.22.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4398557cbf484207df000309235979c79c4356518fd5c99158c7d38203c4da4f", size = 361967 }, + { url = "https://files.pythonhosted.org/packages/50/b2/375b933c93a54bff7fc041e1a6ad2c0f6f733ffb0c6e642ce56ee3b39970/yarl-1.22.0-cp313-cp313t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:2ca6fd72a8cd803be290d42f2dec5cdcd5299eeb93c2d929bf060ad9efaf5de0", size = 323949 }, + { url = "https://files.pythonhosted.org/packages/66/50/bfc2a29a1d78644c5a7220ce2f304f38248dc94124a326794e677634b6cf/yarl-1.22.0-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ca1f59c4e1ab6e72f0a23c13fca5430f889634166be85dbf1013683e49e3278e", size = 361818 }, + { url = "https://files.pythonhosted.org/packages/46/96/f3941a46af7d5d0f0498f86d71275696800ddcdd20426298e572b19b91ff/yarl-1.22.0-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:6c5010a52015e7c70f86eb967db0f37f3c8bd503a695a49f8d45700144667708", size = 372626 }, + { url = "https://files.pythonhosted.org/packages/c1/42/8b27c83bb875cd89448e42cd627e0fb971fa1675c9ec546393d18826cb50/yarl-1.22.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9d7672ecf7557476642c88497c2f8d8542f8e36596e928e9bcba0e42e1e7d71f", size = 341129 }, + { url = "https://files.pythonhosted.org/packages/49/36/99ca3122201b382a3cf7cc937b95235b0ac944f7e9f2d5331d50821ed352/yarl-1.22.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:3b7c88eeef021579d600e50363e0b6ee4f7f6f728cd3486b9d0f3ee7b946398d", size = 346776 }, + { url = "https://files.pythonhosted.org/packages/85/b4/47328bf996acd01a4c16ef9dcd2f59c969f495073616586f78cd5f2efb99/yarl-1.22.0-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:f4afb5c34f2c6fecdcc182dfcfc6af6cccf1aa923eed4d6a12e9d96904e1a0d8", size = 334879 }, + { url = "https://files.pythonhosted.org/packages/c2/ad/b77d7b3f14a4283bffb8e92c6026496f6de49751c2f97d4352242bba3990/yarl-1.22.0-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:59c189e3e99a59cf8d83cbb31d4db02d66cda5a1a4374e8a012b51255341abf5", size = 350996 }, + { url = "https://files.pythonhosted.org/packages/81/c8/06e1d69295792ba54d556f06686cbd6a7ce39c22307100e3fb4a2c0b0a1d/yarl-1.22.0-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:5a3bf7f62a289fa90f1990422dc8dff5a458469ea71d1624585ec3a4c8d6960f", size = 356047 }, + { url = "https://files.pythonhosted.org/packages/4b/b8/4c0e9e9f597074b208d18cef227d83aac36184bfbc6eab204ea55783dbc5/yarl-1.22.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:de6b9a04c606978fdfe72666fa216ffcf2d1a9f6a381058d4378f8d7b1e5de62", size = 342947 }, + { url = "https://files.pythonhosted.org/packages/e0/e5/11f140a58bf4c6ad7aca69a892bff0ee638c31bea4206748fc0df4ebcb3a/yarl-1.22.0-cp313-cp313t-win32.whl", hash = "sha256:1834bb90991cc2999f10f97f5f01317f99b143284766d197e43cd5b45eb18d03", size = 86943 }, + { url = "https://files.pythonhosted.org/packages/31/74/8b74bae38ed7fe6793d0c15a0c8207bbb819cf287788459e5ed230996cdd/yarl-1.22.0-cp313-cp313t-win_amd64.whl", hash = "sha256:ff86011bd159a9d2dfc89c34cfd8aff12875980e3bd6a39ff097887520e60249", size = 93715 }, + { url = "https://files.pythonhosted.org/packages/69/66/991858aa4b5892d57aef7ee1ba6b4d01ec3b7eb3060795d34090a3ca3278/yarl-1.22.0-cp313-cp313t-win_arm64.whl", hash = "sha256:7861058d0582b847bc4e3a4a4c46828a410bca738673f35a29ba3ca5db0b473b", size = 83857 }, + { url = "https://files.pythonhosted.org/packages/46/b3/e20ef504049f1a1c54a814b4b9bed96d1ac0e0610c3b4da178f87209db05/yarl-1.22.0-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:34b36c2c57124530884d89d50ed2c1478697ad7473efd59cfd479945c95650e4", size = 140520 }, + { url = "https://files.pythonhosted.org/packages/e4/04/3532d990fdbab02e5ede063676b5c4260e7f3abea2151099c2aa745acc4c/yarl-1.22.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:0dd9a702591ca2e543631c2a017e4a547e38a5c0f29eece37d9097e04a7ac683", size = 93504 }, + { url = "https://files.pythonhosted.org/packages/11/63/ff458113c5c2dac9a9719ac68ee7c947cb621432bcf28c9972b1c0e83938/yarl-1.22.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:594fcab1032e2d2cc3321bb2e51271e7cd2b516c7d9aee780ece81b07ff8244b", size = 94282 }, + { url = "https://files.pythonhosted.org/packages/a7/bc/315a56aca762d44a6aaaf7ad253f04d996cb6b27bad34410f82d76ea8038/yarl-1.22.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f3d7a87a78d46a2e3d5b72587ac14b4c16952dd0887dbb051451eceac774411e", size = 372080 }, + { url = "https://files.pythonhosted.org/packages/3f/3f/08e9b826ec2e099ea6e7c69a61272f4f6da62cb5b1b63590bb80ca2e4a40/yarl-1.22.0-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:852863707010316c973162e703bddabec35e8757e67fcb8ad58829de1ebc8590", size = 338696 }, + { url = "https://files.pythonhosted.org/packages/e3/9f/90360108e3b32bd76789088e99538febfea24a102380ae73827f62073543/yarl-1.22.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:131a085a53bfe839a477c0845acf21efc77457ba2bcf5899618136d64f3303a2", size = 387121 }, + { url = "https://files.pythonhosted.org/packages/98/92/ab8d4657bd5b46a38094cfaea498f18bb70ce6b63508fd7e909bd1f93066/yarl-1.22.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:078a8aefd263f4d4f923a9677b942b445a2be970ca24548a8102689a3a8ab8da", size = 394080 }, + { url = "https://files.pythonhosted.org/packages/f5/e7/d8c5a7752fef68205296201f8ec2bf718f5c805a7a7e9880576c67600658/yarl-1.22.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bca03b91c323036913993ff5c738d0842fc9c60c4648e5c8d98331526df89784", size = 372661 }, + { url = "https://files.pythonhosted.org/packages/b6/2e/f4d26183c8db0bb82d491b072f3127fb8c381a6206a3a56332714b79b751/yarl-1.22.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:68986a61557d37bb90d3051a45b91fa3d5c516d177dfc6dd6f2f436a07ff2b6b", size = 364645 }, + { url = "https://files.pythonhosted.org/packages/80/7c/428e5812e6b87cd00ee8e898328a62c95825bf37c7fa87f0b6bb2ad31304/yarl-1.22.0-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:4792b262d585ff0dff6bcb787f8492e40698443ec982a3568c2096433660c694", size = 355361 }, + { url = "https://files.pythonhosted.org/packages/ec/2a/249405fd26776f8b13c067378ef4d7dd49c9098d1b6457cdd152a99e96a9/yarl-1.22.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:ebd4549b108d732dba1d4ace67614b9545b21ece30937a63a65dd34efa19732d", size = 381451 }, + { url = "https://files.pythonhosted.org/packages/67/a8/fb6b1adbe98cf1e2dd9fad71003d3a63a1bc22459c6e15f5714eb9323b93/yarl-1.22.0-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:f87ac53513d22240c7d59203f25cc3beac1e574c6cd681bbfd321987b69f95fd", size = 383814 }, + { url = "https://files.pythonhosted.org/packages/d9/f9/3aa2c0e480fb73e872ae2814c43bc1e734740bb0d54e8cb2a95925f98131/yarl-1.22.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:22b029f2881599e2f1b06f8f1db2ee63bd309e2293ba2d566e008ba12778b8da", size = 370799 }, + { url = "https://files.pythonhosted.org/packages/50/3c/af9dba3b8b5eeb302f36f16f92791f3ea62e3f47763406abf6d5a4a3333b/yarl-1.22.0-cp314-cp314-win32.whl", hash = "sha256:6a635ea45ba4ea8238463b4f7d0e721bad669f80878b7bfd1f89266e2ae63da2", size = 82990 }, + { url = "https://files.pythonhosted.org/packages/ac/30/ac3a0c5bdc1d6efd1b41fa24d4897a4329b3b1e98de9449679dd327af4f0/yarl-1.22.0-cp314-cp314-win_amd64.whl", hash = "sha256:0d6e6885777af0f110b0e5d7e5dda8b704efed3894da26220b7f3d887b839a79", size = 88292 }, + { url = "https://files.pythonhosted.org/packages/df/0a/227ab4ff5b998a1b7410abc7b46c9b7a26b0ca9e86c34ba4b8d8bc7c63d5/yarl-1.22.0-cp314-cp314-win_arm64.whl", hash = "sha256:8218f4e98d3c10d683584cb40f0424f4b9fd6e95610232dd75e13743b070ee33", size = 82888 }, + { url = "https://files.pythonhosted.org/packages/06/5e/a15eb13db90abd87dfbefb9760c0f3f257ac42a5cac7e75dbc23bed97a9f/yarl-1.22.0-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:45c2842ff0e0d1b35a6bf1cd6c690939dacb617a70827f715232b2e0494d55d1", size = 146223 }, + { url = "https://files.pythonhosted.org/packages/18/82/9665c61910d4d84f41a5bf6837597c89e665fa88aa4941080704645932a9/yarl-1.22.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:d947071e6ebcf2e2bee8fce76e10faca8f7a14808ca36a910263acaacef08eca", size = 95981 }, + { url = "https://files.pythonhosted.org/packages/5d/9a/2f65743589809af4d0a6d3aa749343c4b5f4c380cc24a8e94a3c6625a808/yarl-1.22.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:334b8721303e61b00019474cc103bdac3d7b1f65e91f0bfedeec2d56dfe74b53", size = 97303 }, + { url = "https://files.pythonhosted.org/packages/b0/ab/5b13d3e157505c43c3b43b5a776cbf7b24a02bc4cccc40314771197e3508/yarl-1.22.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1e7ce67c34138a058fd092f67d07a72b8e31ff0c9236e751957465a24b28910c", size = 361820 }, + { url = "https://files.pythonhosted.org/packages/fb/76/242a5ef4677615cf95330cfc1b4610e78184400699bdda0acb897ef5e49a/yarl-1.22.0-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:d77e1b2c6d04711478cb1c4ab90db07f1609ccf06a287d5607fcd90dc9863acf", size = 323203 }, + { url = "https://files.pythonhosted.org/packages/8c/96/475509110d3f0153b43d06164cf4195c64d16999e0c7e2d8a099adcd6907/yarl-1.22.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c4647674b6150d2cae088fc07de2738a84b8bcedebef29802cf0b0a82ab6face", size = 363173 }, + { url = "https://files.pythonhosted.org/packages/c9/66/59db471aecfbd559a1fd48aedd954435558cd98c7d0da8b03cc6c140a32c/yarl-1.22.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:efb07073be061c8f79d03d04139a80ba33cbd390ca8f0297aae9cce6411e4c6b", size = 373562 }, + { url = "https://files.pythonhosted.org/packages/03/1f/c5d94abc91557384719da10ff166b916107c1b45e4d0423a88457071dd88/yarl-1.22.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e51ac5435758ba97ad69617e13233da53908beccc6cfcd6c34bbed8dcbede486", size = 339828 }, + { url = "https://files.pythonhosted.org/packages/5f/97/aa6a143d3afba17b6465733681c70cf175af89f76ec8d9286e08437a7454/yarl-1.22.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:33e32a0dd0c8205efa8e83d04fc9f19313772b78522d1bdc7d9aed706bfd6138", size = 347551 }, + { url = "https://files.pythonhosted.org/packages/43/3c/45a2b6d80195959239a7b2a8810506d4eea5487dce61c2a3393e7fc3c52e/yarl-1.22.0-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:bf4a21e58b9cde0e401e683ebd00f6ed30a06d14e93f7c8fd059f8b6e8f87b6a", size = 334512 }, + { url = "https://files.pythonhosted.org/packages/86/a0/c2ab48d74599c7c84cb104ebd799c5813de252bea0f360ffc29d270c2caa/yarl-1.22.0-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:e4b582bab49ac33c8deb97e058cd67c2c50dac0dd134874106d9c774fd272529", size = 352400 }, + { url = "https://files.pythonhosted.org/packages/32/75/f8919b2eafc929567d3d8411f72bdb1a2109c01caaab4ebfa5f8ffadc15b/yarl-1.22.0-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:0b5bcc1a9c4839e7e30b7b30dd47fe5e7e44fb7054ec29b5bb8d526aa1041093", size = 357140 }, + { url = "https://files.pythonhosted.org/packages/cf/72/6a85bba382f22cf78add705d8c3731748397d986e197e53ecc7835e76de7/yarl-1.22.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:c0232bce2170103ec23c454e54a57008a9a72b5d1c3105dc2496750da8cfa47c", size = 341473 }, + { url = "https://files.pythonhosted.org/packages/35/18/55e6011f7c044dc80b98893060773cefcfdbf60dfefb8cb2f58b9bacbd83/yarl-1.22.0-cp314-cp314t-win32.whl", hash = "sha256:8009b3173bcd637be650922ac455946197d858b3630b6d8787aa9e5c4564533e", size = 89056 }, + { url = "https://files.pythonhosted.org/packages/f9/86/0f0dccb6e59a9e7f122c5afd43568b1d31b8ab7dda5f1b01fb5c7025c9a9/yarl-1.22.0-cp314-cp314t-win_amd64.whl", hash = "sha256:9fb17ea16e972c63d25d4a97f016d235c78dd2344820eb35bc034bc32012ee27", size = 96292 }, + { url = "https://files.pythonhosted.org/packages/48/b7/503c98092fb3b344a179579f55814b613c1fbb1c23b3ec14a7b008a66a6e/yarl-1.22.0-cp314-cp314t-win_arm64.whl", hash = "sha256:9f6d73c1436b934e3f01df1e1b21ff765cd1d28c77dfb9ace207f746d4610ee1", size = 85171 }, + { url = "https://files.pythonhosted.org/packages/73/ae/b48f95715333080afb75a4504487cbe142cae1268afc482d06692d605ae6/yarl-1.22.0-py3-none-any.whl", hash = "sha256:1380560bdba02b6b6c90de54133c81c9f2a453dee9912fe58c1dcced1edb7cff", size = 46814 }, +] + +[[package]] +name = "zipp" +version = "3.23.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e3/02/0f2892c661036d50ede074e376733dca2ae7c6eb617489437771209d4180/zipp-3.23.0.tar.gz", hash = "sha256:a07157588a12518c9d4034df3fbbee09c814741a33ff63c05fa29d26a2404166", size = 25547 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2e/54/647ade08bf0db230bfea292f893923872fd20be6ac6f53b2b936ba839d75/zipp-3.23.0-py3-none-any.whl", hash = "sha256:071652d6115ed432f5ce1d34c336c0adfd6a884660d1e9712a256d3d3bd4b14e", size = 10276 }, +] From 318a21e82fd38445e495145088b4e3bc9fa9f8ab Mon Sep 17 00:00:00 2001 From: Travis Dent Date: Wed, 21 Jan 2026 15:19:49 -0800 Subject: [PATCH 4/8] Add release workflow for multi-platform binaries Builds for macOS ARM, Linux x86_64, and Linux ARM on tag push. Creates GitHub release with: - worldview-validate-{platform}.tar.gz - worldview-{platform}.tar.gz Added RELEASE.md with release process documentation. --- .codey/transcripts/000000.json | 313 ++++++++++++++++++++++++++++++++- .github/workflows/release.yml | 88 +++++++++ RELEASE.md | 42 +++++ 3 files changed, 441 insertions(+), 2 deletions(-) create mode 100644 .github/workflows/release.yml create mode 100644 RELEASE.md diff --git a/.codey/transcripts/000000.json b/.codey/transcripts/000000.json index 06f8582..93362cb 100644 --- a/.codey/transcripts/000000.json +++ b/.codey/transcripts/000000.json @@ -1422,13 +1422,322 @@ "params": { "command": "rm evals/uv.lock && git add -A && git status" }, + "status": "complete", + "text": "On branch main\nYour branch is ahead of 'origin/main' by 2 commits.\n (use \"git push\" to publish your local commits)\n\nChanges to be committed:\n (use \"git restore --staged ...\" to unstage)\n\tmodified: .codey/transcripts/000000.json\n\tmodified: README.md\n\tdeleted: evals/requirements.txt\n\tnew file: pyproject.toml\n\tnew file: uv.lock\n\n", + "background": false + }, + { + "type": "ThinkingBlock", + "text": "Good, now let me commit this.", + "status": "complete" + }, + { + "type": "ShellBlock", + "call_id": "toolu_01AHBzWMadT3qW5dejwCotyM", + "tool_name": "mcp_shell", + "params": { + "command": "git commit -m \"Migrate Python evals from requirements.txt to pyproject.toml\n\n- Added pyproject.toml at project root with uv support\n- Updated README to use uv commands\n- Removed evals/requirements.txt\n- Added uv.lock for reproducible builds\"" + }, + "status": "complete", + "text": "[main b877337] Migrate Python evals from requirements.txt to pyproject.toml\n 5 files changed, 2119 insertions(+), 14 deletions(-)\n delete mode 100644 evals/requirements.txt\n create mode 100644 pyproject.toml\n create mode 100644 uv.lock\n", + "background": false + }, + { + "type": "ThinkingBlock", + "text": "Done. Now let me summarize what we've done and ask what's next.", + "status": "complete" + }, + { + "type": "TextBlock", + "text": "Done! Three commits so far:\n\n1. **Replace hand-rolled YAML parser with serde_yaml** - build.rs went from 129 → 67 lines\n2. **Add validation for undefined references** - `&Concept.facet` now checked against actual document\n3. **Migrate Python evals to pyproject.toml** - modern uv-based tooling\n\nWhat's next? Still on my radar:\n- **Claim parsing** - `parse_claim()` is a chunky function doing several things\n- **Agent error handling** - no timeouts or retry on API failures \n- **Duplicate detection** - no check for duplicate concept/facet names\n\nOr something else catching your eye?", + "status": "complete" + } + ], + "timestamp": "2026-01-21T23:16:25.759134Z" + }, + { + "id": 15, + "role": "user", + "content": [ + { + "type": "TextBlock", + "text": "i want to be able to release binaries for a few systems of these tools. can uyou look at ../code which has some github actions and build setup", + "status": "complete" + } + ], + "timestamp": "2026-01-21T23:18:27.250816Z" + }, + { + "id": 16, + "role": "assistant", + "content": [ + { + "type": "ThinkingBlock", + "text": "The user wants to look at a sibling directory `../codey` which likely has GitHub Actions workflows for building and releasing binaries. Let me explore that.", + "status": "complete" + }, + { + "type": "ShellBlock", + "call_id": "toolu_01QQ3LXDVrSnTufYtWrXGvQn", + "tool_name": "mcp_shell", + "params": { + "command": "ls -la ../codey/" + }, + "status": "complete", + "text": "[stderr]\nls: ../codey/: No such file or directory\n\n[exit code: 1]", + "background": false + }, + { + "type": "ShellBlock", + "call_id": "toolu_01DuU25CfEwYiSoBaa3gtFTr", + "tool_name": "mcp_shell", + "params": { + "command": "ls -la ../codey/.github/workflows/" + }, + "status": "complete", + "text": "[stderr]\nls: ../codey/.github/workflows/: No such file or directory\n\n[exit code: 1]", + "background": false + }, + { + "type": "ThinkingBlock", + "text": "The user said `../code` not `../codey`. Let me try that.", + "status": "complete" + }, + { + "type": "ShellBlock", + "call_id": "toolu_019Bd2PhH7HLdUExZAshYdqS", + "tool_name": "mcp_shell", + "params": { + "command": "ls -la ../code/" + }, + "status": "complete", + "text": "total 736\ndrwxr-xr-x@ 24 tcdent staff 768 Jan 21 08:31 .\ndrwxr-xr-x 180 tcdent staff 5760 Jan 21 10:11 ..\n-rw-r--r--@ 1 tcdent staff 8196 Jan 16 09:25 .DS_Store\ndrwxr-xr-x@ 3 tcdent staff 96 Jan 14 18:46 .cargo\ndrwxr-xr-x@ 4 tcdent staff 128 Jan 17 18:26 .codey\ndrwxr-xr-x@ 17 tcdent staff 544 Jan 21 09:56 .git\ndrwxr-xr-x@ 3 tcdent staff 96 Jan 6 15:02 .github\n-rw-r--r--@ 1 tcdent staff 336 Jan 6 20:13 .gitignore\n-rw-r--r--@ 1 tcdent staff 122322 Jan 21 09:56 Cargo.lock\n-rw-r--r--@ 1 tcdent staff 2614 Jan 21 09:56 Cargo.toml\n-rw-r--r--@ 1 tcdent staff 7287 Jan 21 08:31 LIBRARY.md\n-rw-r--r--@ 1 tcdent staff 1068 Dec 14 10:31 LICENSE\n-rw-r--r--@ 1 tcdent staff 1148 Jan 6 15:02 Makefile\n-rw-r--r--@ 1 tcdent staff 7327 Jan 21 09:46 README.md\n-rw-r--r--@ 1 tcdent staff 1231 Jan 7 15:54 RELEASE.md\n-rw-r--r--@ 1 tcdent staff 3688 Jan 6 20:13 config.example.toml\ndrwxr-xr-x@ 7 tcdent staff 224 Jan 21 09:13 lib\n-rw-r--r--@ 1 tcdent staff 778 Jan 6 20:16 profile.json\n-rw-r--r--@ 1 tcdent staff 192854 Dec 15 08:19 profile.json.gz\ndrwxr-xr-x@ 11 tcdent staff 352 Jan 17 12:43 research\ndrwxr-xr-x@ 7 tcdent staff 224 Jan 9 10:15 sandbox\ndrwxr-xr-x@ 17 tcdent staff 544 Jan 21 09:48 src\ndrwxr-xr-x@ 8 tcdent staff 256 Jan 16 09:25 target\ndrwxr-xr-x@ 3 tcdent staff 96 Jan 6 20:13 tools\n", + "background": false + }, + { + "type": "ThinkingBlock", + "text": "Found it. Let me look at the GitHub workflows directory.", + "status": "complete" + }, + { + "type": "ShellBlock", + "call_id": "toolu_01749yJPwCN4nDZAhVQqrQPz", + "tool_name": "mcp_shell", + "params": { + "command": "ls -la ../code/.github/workflows/ 2>/dev/null || ls -la ../code/.github/" + }, + "status": "complete", + "text": "total 8\ndrwxr-xr-x@ 3 tcdent staff 96 Jan 6 15:02 .\ndrwxr-xr-x@ 3 tcdent staff 96 Jan 6 15:02 ..\n-rw-r--r--@ 1 tcdent staff 1772 Jan 7 15:43 release.yml\n", + "background": false + }, + { + "type": "ThinkingBlock", + "text": "There's a release.yml. Let me read that along with the Makefile and RELEASE.md to understand the build/release process.", + "status": "complete" + }, + { + "type": "ReadFileBlock", + "call_id": "toolu_01XofhVdQGiWoEqpVFNckGor", + "tool_name": "mcp_read_file", + "params": { + "path": "../code/RELEASE.md" + }, + "status": "complete", + "text": " 1│# Release Process\n 2│\n 3│## 1. Bump version\n 4│\n 5│```bash\n 6│# Edit Cargo.toml version\n 7│vim Cargo.toml\n 8│\n 9│git add Cargo.toml Cargo.lock\n 10│git commit -m \"Bump version to X.Y.Z\"\n 11│git push origin main\n 12│```\n 13│\n 14│## 2. Tag and push\n 15│\n 16│```bash\n 17│git tag vX.Y.Z\n 18│git push origin vX.Y.Z\n 19│```\n 20│\n 21│This triggers the [Release workflow](https://github.com/tcdent/codey/actions/workflows/release.yml) which builds for:\n 22│- `codey-darwin-arm64` (macOS ARM)\n 23│- `codey-linux-x86_64` (Linux x86)\n 24│- `codey-linux-arm64` (Linux ARM)\n 25│\n 26│## 3. Update Homebrew tap\n 27│\n 28│Once the release workflow completes, get SHA256 hashes:\n 29│\n 30│```bash\n 31│VERSION=X.Y.Z\n 32│curl -sL https://github.com/tcdent/codey/releases/download/v$VERSION/codey-darwin-arm64.tar.gz | shasum -a 256\n 33│curl -sL https://github.com/tcdent/codey/releases/download/v$VERSION/codey-linux-x86_64.tar.gz | shasum -a 256\n 34│curl -sL https://github.com/tcdent/codey/releases/download/v$VERSION/codey-linux-arm64.tar.gz | shasum -a 256\n 35│```\n 36│\n 37│Update `~/Work/tap/Formula/codey.rb`:\n 38│- `version \"X.Y.Z\"`\n 39│- SHA256 hashes for each platform\n 40│\n 41│```bash\n 42│cd ~/Work/tap\n 43│git add -A\n 44│git commit -m \"Update codey to vX.Y.Z\"\n 45│git push\n 46│```\n 47│\n 48│## Version scheme\n 49│\n 50│```\n 51│0.1.0-alpha.1 # Early development\n 52│0.1.0-rc.1 # Release candidate\n 53│0.1.0 # Stable release\n 54│```\n", + "background": false + }, + { + "type": "ReadFileBlock", + "call_id": "toolu_01WNPvqe3wANWSaiTsZFvjDd", + "tool_name": "mcp_read_file", + "params": { + "path": "../code/Makefile" + }, + "status": "complete", + "text": " 1│.PHONY: build run release profile clean patch\n 2│\n 3│# Set to 0 to use upstream crates: make SIMD=0 build\n 4│SIMD ?= 1\n 5│\n 6│ifeq ($(SIMD),1)\n 7│PATCH_DEPS := lib/ratatui-core/.patched lib/genai/.patched .cargo/config.toml\n 8│else\n 9│PATCH_DEPS := lib/genai/.patched .cargo/config.toml\n 10│endif\n 11│\n 12│build: $(PATCH_DEPS)\n 13│\tcargo build\n 14│\n 15│run: $(PATCH_DEPS)\n 16│\tcargo run\n 17│\n 18│release: $(PATCH_DEPS)\n 19│ifdef CARGO_BUILD_TARGET\n 20│\tcargo build --release --target $(CARGO_BUILD_TARGET)\n 21│else\n 22│\tcargo build --release\n 23│endif\n 24│\n 25│profile: release\n 26│\tsamply record ./target/release/codey\n 27│\n 28│clean: clean-config\n 29│\tcargo clean\n 30│\trm -rf lib/ratatui-core lib/genai\n 31│\n 32│clean-config:\n 33│\trm -f .cargo/config.toml\n 34│\trmdir .cargo 2>/dev/null || true\n 35│\n 36│patch: lib/ratatui-core/.patched lib/genai/.patched .cargo/config.toml\n 37│\n 38│lib/ratatui-core/.patched: lib/patches/ratatui-core/*\n 39│\t./lib/patches/ratatui-core/apply.sh\n 40│\n 41│lib/genai/.patched: lib/patches/genai/*\n 42│\t./lib/patches/genai/apply.sh\n 43│\n 44│.cargo/config.toml: lib/ratatui-core/.patched lib/genai/.patched\n 45│\tmkdir -p .cargo\n 46│\techo '[patch.crates-io]' > $@\n 47│ifeq ($(SIMD),1)\n 48│\techo 'ratatui-core = { path = \"lib/ratatui-core\" }' >> $@\n 49│endif\n 50│\techo 'genai = { path = \"lib/genai\" }' >> $@\n", + "background": false + }, + { + "type": "ThinkingBlock", + "text": "The release.yml file exists but it's probably in the workflows subdirectory. Let me look again.", + "status": "complete" + }, + { + "type": "ShellBlock", + "call_id": "toolu_01KZrsj5YkN6whAM9kGEhN46", + "tool_name": "mcp_shell", + "params": { + "command": "find ../code/.github -name \"*.yml\" -o -name \"*.yaml\" 2>/dev/null" + }, + "status": "complete", + "text": "../code/.github/workflows/release.yml\n", + "background": false + }, + { + "type": "ThinkingBlock", + "text": "Oh, there's a workflows directory. Let me read the release.yml file.", + "status": "complete" + }, + { + "type": "ShellBlock", + "call_id": "toolu_01AXfRV274ifRtqggGeGuSsA", + "tool_name": "mcp_shell", + "params": { + "command": "cat ../code/.github/workflows/release.yml" + }, + "status": "complete", + "text": "name: Release\n\non:\n push:\n tags:\n - 'v*'\n\nenv:\n CARGO_TERM_COLOR: always\n\njobs:\n build:\n strategy:\n matrix:\n include:\n - target: aarch64-apple-darwin\n os: macos-14\n name: codey-darwin-arm64\n - target: x86_64-unknown-linux-gnu\n os: ubuntu-latest\n name: codey-linux-x86_64\n - target: aarch64-unknown-linux-gnu\n os: ubuntu-24.04-arm\n name: codey-linux-arm64\n\n runs-on: ${{ matrix.os }}\n \n steps:\n - uses: actions/checkout@v4\n \n - name: Install Rust\n uses: dtolnay/rust-toolchain@stable\n with:\n targets: ${{ matrix.target }}\n \n - name: Build release\n run: make release\n env:\n CARGO_BUILD_TARGET: ${{ matrix.target }}\n \n - name: Ad-hoc sign (macOS)\n if: contains(matrix.os, 'macos')\n run: codesign --force --sign - target/${{ matrix.target }}/release/codey\n \n - name: Package\n run: |\n mkdir -p dist\n cp target/${{ matrix.target }}/release/codey dist/${{ matrix.name }}\n cd dist && tar -czvf ${{ matrix.name }}.tar.gz ${{ matrix.name }}\n \n - name: Upload artifact\n uses: actions/upload-artifact@v4\n with:\n name: ${{ matrix.name }}\n path: dist/${{ matrix.name }}.tar.gz\n\n release:\n needs: build\n runs-on: ubuntu-latest\n permissions:\n contents: write\n \n steps:\n - name: Download artifacts\n uses: actions/download-artifact@v4\n with:\n path: artifacts\n \n - name: Create Release\n uses: softprops/action-gh-release@v2\n with:\n files: artifacts/**/*.tar.gz\n generate_release_notes: true\n", + "background": false + }, + { + "type": "ThinkingBlock", + "text": "This is a good release workflow. It:\n1. Triggers on version tags (v*)\n2. Builds for 3 targets: macOS ARM, Linux x86_64, Linux ARM\n3. Uses a matrix to parallelize builds\n4. Signs macOS binaries (ad-hoc)\n5. Packages into tar.gz\n6. Creates a GitHub release with all artifacts\n\nFor the worldview project, we have two binaries:\n- `worldview-validate` (validator)\n- `worldview` (agent)\n\nLet me look at the current GitHub workflows in the wvf project first.", + "status": "complete" + }, + { + "type": "TextBlock", + "text": "Nice setup. Let me see what's already in the wvf project:", + "status": "complete" + }, + { + "type": "ShellBlock", + "call_id": "toolu_01V2NCfJnzosVeDT6vipUFXP", + "tool_name": "mcp_shell", + "params": { + "command": "ls -la .github/workflows/" + }, + "status": "complete", + "text": "total 32\ndrwxr-xr-x@ 4 tcdent staff 128 Jan 21 10:11 .\ndrwxr-xr-x@ 3 tcdent staff 96 Jan 21 10:11 ..\n-rw-r--r--@ 1 tcdent staff 3150 Jan 21 10:11 build.yml\n-rw-r--r--@ 1 tcdent staff 9340 Jan 21 10:11 evals.yml\n", + "background": false + }, + { + "type": "ReadFileBlock", + "call_id": "toolu_01M5R5ZpmhgV38eLYHdEaNEd", + "tool_name": "mcp_read_file", + "params": { + "path": ".github/workflows/build.yml" + }, + "status": "complete", + "text": " 1│name: Build Worldview Tools\n 2│\n 3│on:\n 4│ push:\n 5│ branches: [main, master]\n 6│ paths:\n 7│ - 'agent/**'\n 8│ - 'validator/**'\n 9│ - 'spec/**'\n 10│ - 'evals/**'\n 11│ - 'system.md'\n 12│ - 'SPEC.md'\n 13│ - '.github/workflows/build.yml'\n 14│ - '.github/workflows/evals.yml'\n 15│ pull_request:\n 16│ branches: [main, master]\n 17│ paths:\n 18│ - 'agent/**'\n 19│ - 'validator/**'\n 20│ - 'spec/**'\n 21│ - 'evals/**'\n 22│ - 'system.md'\n 23│ - 'SPEC.md'\n 24│ - '.github/workflows/evals.yml'\n 25│ workflow_dispatch:\n 26│ workflow_call:\n 27│ outputs:\n 28│ artifact-name:\n 29│ description: \"Name of the uploaded artifact\"\n 30│ value: worldview-tools\n 31│\n 32│jobs:\n 33│ # Verify generated files are in sync with spec/tokens.yaml\n 34│ spec-check:\n 35│ runs-on: ubuntu-latest\n 36│ steps:\n 37│ - name: Checkout repository\n 38│ uses: actions/checkout@v4\n 39│\n 40│ - name: Setup Python\n 41│ uses: actions/setup-python@v5\n 42│ with:\n 43│ python-version: '3.11'\n 44│\n 45│ - name: Install PyYAML\n 46│ run: pip install pyyaml\n 47│\n 48│ - name: Regenerate all spec-derived files\n 49│ run: |\n 50│ # Generate system.md, README language spec, etc.\n 51│ python spec/generate.py all\n 52│\n 53│ # Note: validator tokens are generated at compile-time via build.rs\n 54│ # so we don't need tokens_generated.rs committed anymore\n 55│\n 56│ - name: Check for uncommitted changes\n 57│ run: |\n 58│ # Check if any tracked files changed (excluding tokens_generated.rs)\n 59│ CHANGED=$(git diff --name-only -- ':!validator/src/tokens_generated.rs')\n 60│ if [ -n \"$CHANGED\" ]; then\n 61│ echo \"::error::Generated files are out of sync with spec/tokens.yaml\"\n 62│ echo \"\"\n 63│ echo \"The following files need to be regenerated:\"\n 64│ echo \"$CHANGED\"\n 65│ echo \"\"\n 66│ echo \"Run 'python spec/generate.py all' and commit the changes.\"\n 67│ exit 1\n 68│ fi\n 69│\n 70│ echo \"✓ All generated files are in sync\"\n 71│\n 72│ build:\n 73│ needs: spec-check\n 74│ runs-on: ubuntu-latest\n 75│\n 76│ steps:\n 77│ - name: Checkout repository\n 78│ uses: actions/checkout@v4\n 79│\n 80│ - name: Setup Rust\n 81│ uses: dtolnay/rust-toolchain@stable\n 82│\n 83│ - name: Cache cargo registry\n 84│ uses: actions/cache@v4\n 85│ with:\n 86│ path: |\n 87│ ~/.cargo/registry\n 88│ ~/.cargo/git\n 89│ target\n 90│ key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}\n 91│ restore-keys: |\n 92│ ${{ runner.os }}-cargo-\n 93│\n 94│ - name: Build validator\n 95│ run: |\n 96│ cd validator\n 97│ cargo build --release\n 98│\n 99│ - name: Build agent\n 100│ run: |\n 101│ cd agent\n 102│ ./setup.sh\n 103│ cargo build --release\n 104│\n 105│ - name: Run validator tests\n 106│ run: |\n 107│ cd validator\n 108│ cargo test\n 109│\n 110│ - name: Collect binaries\n 111│ run: |\n 112│ mkdir -p dist\n 113│ cp validator/target/release/worldview-validate dist/\n 114│ cp agent/target/release/worldview dist/\n 115│ chmod +x dist/*\n 116│\n 117│ - name: Upload artifacts\n 118│ uses: actions/upload-artifact@v4\n 119│ with:\n 120│ name: worldview-tools\n 121│ path: dist/\n 122│ retention-days: 7\n", + "background": false + }, + { + "type": "ThinkingBlock", + "text": "So there's already a build.yml that builds on ubuntu, but it's not doing multi-platform release builds. Let me look at what agent/setup.sh does and then create a release workflow.", + "status": "complete" + }, + { + "type": "ShellBlock", + "call_id": "toolu_01FFoLqFQwEhboarn63uh1BM", + "tool_name": "mcp_shell", + "params": { + "command": "cat agent/setup.sh 2>/dev/null || echo \"no setup.sh\"" + }, + "status": "complete", + "text": "#!/bin/bash\n# Setup script for wsl-agent\n# This script clones and patches codey dependencies\n\nset -euo pipefail\n\nSCRIPT_DIR=\"$(cd \"$(dirname \"${BASH_SOURCE[0]}\")\" && pwd)\"\nVENDOR_DIR=\"$SCRIPT_DIR/vendor\"\n\necho \"Setting up wsl-agent dependencies...\"\n\n# Clone codey if not present\nif [ ! -d \"$VENDOR_DIR/codey\" ]; then\n echo \"Cloning codey...\"\n mkdir -p \"$VENDOR_DIR\"\n git clone --depth 1 https://github.com/tcdent/codey.git \"$VENDOR_DIR/codey\"\nelse\n echo \"Codey already cloned, updating...\"\n cd \"$VENDOR_DIR/codey\"\n git pull --ff-only || true\n cd \"$SCRIPT_DIR\"\nfi\n\n# Apply patches in codey\necho \"Applying codey patches...\"\ncd \"$VENDOR_DIR/codey\"\n\n# Run the genai patch\nif [ ! -f \"lib/genai/.patched\" ]; then\n echo \"Patching genai...\"\n bash lib/patches/genai/apply.sh\nfi\n\necho \"Setup complete!\"\necho \"\"\necho \"You can now build with: cargo build\"\n", + "background": false + }, + { + "type": "ReadFileBlock", + "call_id": "toolu_018oWMxNT82Tyjp16Hoq34o8", + "tool_name": "mcp_read_file", + "params": { + "path": "agent/Cargo.toml" + }, + "status": "complete", + "text": " 1│[package]\n 2│name = \"worldview-agent\"\n 3│version = \"0.1.0\"\n 4│edition = \"2024\"\n 5│description = \"CLI tool for adding facts to Worldview files using an AI agent\"\n 6│license = \"MIT\"\n 7│\n 8│[[bin]]\n 9│name = \"worldview\"\n 10│path = \"src/main.rs\"\n 11│\n 12│[dependencies]\n 13│# Core agent functionality (vendored, minimal features for library usage)\n 14│codey = { path = \"vendor/codey\", default-features = false }\n 15│\n 16│# Worldview validation\n 17│worldview-validator = { path = \"../validator\" }\n 18│\n 19│# Async runtime\n 20│tokio = { version = \"1\", features = [\"full\"] }\n 21│\n 22│# CLI parsing\n 23│clap = { version = \"4\", features = [\"derive\"] }\n 24│\n 25│# Serialization for tool schemas\n 26│serde = { version = \"1\", features = [\"derive\"] }\n 27│serde_json = \"1\"\n 28│\n 29│# Error handling\n 30│anyhow = \"1\"\n 31│\n 32│[patch.crates-io]\n 33│# Use codey's patched genai with thinking block support\n 34│genai = { path = \"vendor/codey/lib/genai\" }\n", + "background": false + }, + { + "type": "ThinkingBlock", + "text": "I see - the agent depends on a vendored codey with patches. The setup.sh clones codey and applies patches. This complicates cross-platform builds a bit since each runner needs to run setup.sh.\n\nLet me create a release workflow based on the codey one, but adapted for the two binaries (worldview-validate and worldview).", + "status": "complete" + }, + { + "type": "TextBlock", + "text": "Got it. The agent vendors codey with patches. Let me create a release workflow:", + "status": "complete" + }, + { + "type": "WriteFileBlock", + "call_id": "toolu_014QviJBcRYkURuf5wJjhtaA", + "tool_name": "mcp_write_file", + "params": { + "content": "name: Release\n\non:\n push:\n tags:\n - 'v*'\n\nenv:\n CARGO_TERM_COLOR: always\n\njobs:\n build:\n strategy:\n matrix:\n include:\n - target: aarch64-apple-darwin\n os: macos-14\n suffix: darwin-arm64\n - target: x86_64-unknown-linux-gnu\n os: ubuntu-latest\n suffix: linux-x86_64\n - target: aarch64-unknown-linux-gnu\n os: ubuntu-24.04-arm\n suffix: linux-arm64\n\n runs-on: ${{ matrix.os }}\n \n steps:\n - uses: actions/checkout@v4\n \n - name: Install Rust\n uses: dtolnay/rust-toolchain@stable\n with:\n targets: ${{ matrix.target }}\n \n - name: Build validator\n run: |\n cd validator\n cargo build --release --target ${{ matrix.target }}\n \n - name: Setup agent dependencies\n run: |\n cd agent\n ./setup.sh\n \n - name: Build agent\n run: |\n cd agent\n cargo build --release --target ${{ matrix.target }}\n \n - name: Ad-hoc sign (macOS)\n if: contains(matrix.os, 'macos')\n run: |\n codesign --force --sign - validator/target/${{ matrix.target }}/release/worldview-validate\n codesign --force --sign - agent/target/${{ matrix.target }}/release/worldview\n \n - name: Package\n run: |\n mkdir -p dist\n cp validator/target/${{ matrix.target }}/release/worldview-validate dist/worldview-validate-${{ matrix.suffix }}\n cp agent/target/${{ matrix.target }}/release/worldview dist/worldview-${{ matrix.suffix }}\n cd dist\n tar -czvf worldview-validate-${{ matrix.suffix }}.tar.gz worldview-validate-${{ matrix.suffix }}\n tar -czvf worldview-${{ matrix.suffix }}.tar.gz worldview-${{ matrix.suffix }}\n \n - name: Upload artifacts\n uses: actions/upload-artifact@v4\n with:\n name: binaries-${{ matrix.suffix }}\n path: dist/*.tar.gz\n\n release:\n needs: build\n runs-on: ubuntu-latest\n permissions:\n contents: write\n \n steps:\n - name: Download artifacts\n uses: actions/download-artifact@v4\n with:\n path: artifacts\n \n - name: Create Release\n uses: softprops/action-gh-release@v2\n with:\n files: artifacts/**/*.tar.gz\n generate_release_notes: true\n", + "path": ".github/workflows/release.yml" + }, + "status": "complete", + "text": "Created file: .github/workflows/release.yml (88 lines, 2403 bytes)", + "background": false + }, + { + "type": "ThinkingBlock", + "text": "Now let me also add a RELEASE.md with instructions like codey has.", + "status": "complete" + }, + { + "type": "WriteFileBlock", + "call_id": "toolu_01P6sUw8CvRwjjgaS42MuPgh", + "tool_name": "mcp_write_file", + "params": { + "content": "# Release Process\n\n## 1. Bump versions\n\n```bash\n# Edit version in both Cargo.toml files\nvim validator/Cargo.toml agent/Cargo.toml\n\ngit add validator/Cargo.toml agent/Cargo.toml\ngit commit -m \"Bump version to X.Y.Z\"\ngit push origin main\n```\n\n## 2. Tag and push\n\n```bash\ngit tag vX.Y.Z\ngit push origin vX.Y.Z\n```\n\nThis triggers the [Release workflow](.github/workflows/release.yml) which builds:\n\n| Binary | Description |\n|--------|-------------|\n| `worldview-validate-darwin-arm64` | macOS ARM validator |\n| `worldview-validate-linux-x86_64` | Linux x86 validator |\n| `worldview-validate-linux-arm64` | Linux ARM validator |\n| `worldview-darwin-arm64` | macOS ARM agent CLI |\n| `worldview-linux-x86_64` | Linux x86 agent CLI |\n| `worldview-linux-arm64` | Linux ARM agent CLI |\n\n## 3. Verify release\n\nOnce the workflow completes, check the [Releases page](../../releases) for the new version with all artifacts attached.\n\n## Version scheme\n\n```\n0.1.0-alpha.1 # Early development\n0.1.0-rc.1 # Release candidate\n0.1.0 # Stable release\n```\n", + "path": "RELEASE.md" + }, + "status": "complete", + "text": "Created file: RELEASE.md (42 lines, 1052 bytes)", + "background": false + }, + { + "type": "ThinkingBlock", + "text": "Let me also run a quick check on the workflow to make sure the paths are correct - I need to verify the validator and agent have different target directories.", + "status": "complete" + }, + { + "type": "TextBlock", + "text": "Let me verify the target paths are correct:", + "status": "complete" + }, + { + "type": "ShellBlock", + "call_id": "toolu_01SUsqQbh8KPG3cqrGEA9eSB", + "tool_name": "mcp_shell", + "params": { + "command": "cd validator && cargo build --release 2>&1 | tail -5" + }, + "status": "complete", + "text": " Compiling serde_derive v1.0.228\n Compiling thiserror-impl v1.0.69\n Compiling serde_yaml v0.9.34+deprecated\n Compiling worldview-validator v0.1.0 (/Users/tcdent/Work/wvf/validator)\n Finished `release` profile [optimized] target(s) in 4.44s\n", + "background": false + }, + { + "type": "ShellBlock", + "call_id": "toolu_01K8GVaczxwTiDAGHdnV2L7G", + "tool_name": "mcp_shell", + "params": { + "command": "ls -la validator/target/release/worldview-validate 2>/dev/null && ls -la agent/target/release/worldview 2>/dev/null || echo \"need to build agent\"" + }, + "status": "complete", + "text": "-rwxr-xr-x@ 1 tcdent staff 487360 Jan 21 15:19 validator/target/release/worldview-validate\nneed to build agent\n", + "background": false + }, + { + "type": "ThinkingBlock", + "text": "Good, the validator builds. The agent would need the setup.sh run first. Let me commit the release workflow and document.", + "status": "complete" + }, + { + "type": "ShellBlock", + "call_id": "toolu_01LMSyd7n3DmXrMHc8LjykUf", + "tool_name": "mcp_shell", + "params": { + "command": "git add -A && git commit -m \"Add release workflow for multi-platform binaries\n\nBuilds for macOS ARM, Linux x86_64, and Linux ARM on tag push.\nCreates GitHub release with:\n- worldview-validate-{platform}.tar.gz\n- worldview-{platform}.tar.gz\n\nAdded RELEASE.md with release process documentation.\"" + }, "status": "pending", "text": "", "background": false } ], - "timestamp": "2026-01-21T23:16:25.759134Z" + "timestamp": "2026-01-21T23:18:27.258771Z" } ], - "next_id": 15 + "next_id": 17 } \ No newline at end of file diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..8252f48 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,88 @@ +name: Release + +on: + push: + tags: + - 'v*' + +env: + CARGO_TERM_COLOR: always + +jobs: + build: + strategy: + matrix: + include: + - target: aarch64-apple-darwin + os: macos-14 + suffix: darwin-arm64 + - target: x86_64-unknown-linux-gnu + os: ubuntu-latest + suffix: linux-x86_64 + - target: aarch64-unknown-linux-gnu + os: ubuntu-24.04-arm + suffix: linux-arm64 + + runs-on: ${{ matrix.os }} + + steps: + - uses: actions/checkout@v4 + + - name: Install Rust + uses: dtolnay/rust-toolchain@stable + with: + targets: ${{ matrix.target }} + + - name: Build validator + run: | + cd validator + cargo build --release --target ${{ matrix.target }} + + - name: Setup agent dependencies + run: | + cd agent + ./setup.sh + + - name: Build agent + run: | + cd agent + cargo build --release --target ${{ matrix.target }} + + - name: Ad-hoc sign (macOS) + if: contains(matrix.os, 'macos') + run: | + codesign --force --sign - validator/target/${{ matrix.target }}/release/worldview-validate + codesign --force --sign - agent/target/${{ matrix.target }}/release/worldview + + - name: Package + run: | + mkdir -p dist + cp validator/target/${{ matrix.target }}/release/worldview-validate dist/worldview-validate-${{ matrix.suffix }} + cp agent/target/${{ matrix.target }}/release/worldview dist/worldview-${{ matrix.suffix }} + cd dist + tar -czvf worldview-validate-${{ matrix.suffix }}.tar.gz worldview-validate-${{ matrix.suffix }} + tar -czvf worldview-${{ matrix.suffix }}.tar.gz worldview-${{ matrix.suffix }} + + - name: Upload artifacts + uses: actions/upload-artifact@v4 + with: + name: binaries-${{ matrix.suffix }} + path: dist/*.tar.gz + + release: + needs: build + runs-on: ubuntu-latest + permissions: + contents: write + + steps: + - name: Download artifacts + uses: actions/download-artifact@v4 + with: + path: artifacts + + - name: Create Release + uses: softprops/action-gh-release@v2 + with: + files: artifacts/**/*.tar.gz + generate_release_notes: true diff --git a/RELEASE.md b/RELEASE.md new file mode 100644 index 0000000..d2f3ee7 --- /dev/null +++ b/RELEASE.md @@ -0,0 +1,42 @@ +# Release Process + +## 1. Bump versions + +```bash +# Edit version in both Cargo.toml files +vim validator/Cargo.toml agent/Cargo.toml + +git add validator/Cargo.toml agent/Cargo.toml +git commit -m "Bump version to X.Y.Z" +git push origin main +``` + +## 2. Tag and push + +```bash +git tag vX.Y.Z +git push origin vX.Y.Z +``` + +This triggers the [Release workflow](.github/workflows/release.yml) which builds: + +| Binary | Description | +|--------|-------------| +| `worldview-validate-darwin-arm64` | macOS ARM validator | +| `worldview-validate-linux-x86_64` | Linux x86 validator | +| `worldview-validate-linux-arm64` | Linux ARM validator | +| `worldview-darwin-arm64` | macOS ARM agent CLI | +| `worldview-linux-x86_64` | Linux x86 agent CLI | +| `worldview-linux-arm64` | Linux ARM agent CLI | + +## 3. Verify release + +Once the workflow completes, check the [Releases page](../../releases) for the new version with all artifacts attached. + +## Version scheme + +``` +0.1.0-alpha.1 # Early development +0.1.0-rc.1 # Release candidate +0.1.0 # Stable release +``` From 7b5c6e6ebc15d677e9129d5edb48411be754f19e Mon Sep 17 00:00:00 2001 From: Travis Dent Date: Wed, 21 Jan 2026 15:47:46 -0800 Subject: [PATCH 5/8] Refactor: unified CLI with subcommands - Rename agent/ to cli/ with single 'worldview' binary - Add subcommands: 'validate' and 'add' - Embed system.md spec in --help output via include_str!() - Remove separate worldview-validate binary - Update all workflows for cli/, uv, and single binary - Update README and RELEASE.md documentation - Add .codey/ to gitignore --- .codey/transcripts/000000.json | 1743 --------------------------- .github/workflows/build.yml | 37 +- .github/workflows/evals.yml | 30 +- .github/workflows/release.yml | 25 +- .gitignore | 1 + README.md | 69 +- RELEASE.md | 17 +- {agent => cli}/.gitignore | 0 {agent => cli}/Cargo.lock | 25 +- {agent => cli}/Cargo.toml | 4 +- {agent => cli}/setup.sh | 0 agent/src/main.rs => cli/src/add.rs | 65 +- cli/src/main.rs | 66 + cli/src/validate.rs | 50 + validator/Cargo.toml | 4 - validator/src/main.rs | 103 -- 16 files changed, 236 insertions(+), 2003 deletions(-) delete mode 100644 .codey/transcripts/000000.json rename {agent => cli}/.gitignore (100%) rename {agent => cli}/Cargo.lock (99%) rename {agent => cli}/Cargo.toml (87%) rename {agent => cli}/setup.sh (100%) rename agent/src/main.rs => cli/src/add.rs (89%) create mode 100644 cli/src/main.rs create mode 100644 cli/src/validate.rs delete mode 100644 validator/src/main.rs diff --git a/.codey/transcripts/000000.json b/.codey/transcripts/000000.json deleted file mode 100644 index 93362cb..0000000 --- a/.codey/transcripts/000000.json +++ /dev/null @@ -1,1743 +0,0 @@ -{ - "turns": [ - { - "id": 0, - "role": "assistant", - "content": [ - { - "type": "TextBlock", - "text": "Welcome to Codey! I'm your AI coding assistant. How can I help you today?", - "status": "pending" - } - ], - "timestamp": "2026-01-21T23:02:17.557799Z" - }, - { - "id": 1, - "role": "user", - "content": [ - { - "type": "TextBlock", - "text": "ok i developed this entrely using background agents so i'm not sure of the code quality. let's spend soem time getting familair with this", - "status": "complete" - } - ], - "timestamp": "2026-01-21T23:02:43.035488Z" - }, - { - "id": 2, - "role": "assistant", - "content": [ - { - "type": "ThinkingBlock", - "text": "The user wants to explore and understand the codebase in their project directory at /Users/tcdent/.config/codey. They developed it using background agents and want to review the code quality. Let me start by exploring the directory structure to understand what we're working with.", - "status": "complete" - }, - { - "type": "TextBlock", - "text": "Let me explore the project structure to get a sense of what we're working with.", - "status": "complete" - }, - { - "type": "ShellBlock", - "call_id": "toolu_01EWu32oXUWYrFPQbw5LWZtb", - "tool_name": "mcp_shell", - "params": { - "command": "ls -la" - }, - "status": "complete", - "text": "total 64\ndrwxr-xr-x@ 14 tcdent staff 448 Jan 21 15:02 .\ndrwxr-xr-x 180 tcdent staff 5760 Jan 21 10:11 ..\ndrwxr-xr-x@ 3 tcdent staff 96 Jan 21 15:02 .codey\ndrwxr-xr-x@ 12 tcdent staff 384 Jan 21 10:11 .git\ndrwxr-xr-x@ 3 tcdent staff 96 Jan 21 10:11 .github\n-rw-r--r--@ 1 tcdent staff 437 Jan 21 10:11 .gitignore\n-rw-r--r--@ 1 tcdent staff 6980 Jan 21 10:11 README.md\n-rw-r--r--@ 1 tcdent staff 10738 Jan 21 10:11 SPEC.md\ndrwxr-xr-x@ 7 tcdent staff 224 Jan 21 10:11 agent\ndrwxr-xr-x@ 9 tcdent staff 288 Jan 21 10:11 evals\n-rw-r--r--@ 1 tcdent staff 1461 Jan 21 10:11 example.wvf\ndrwxr-xr-x@ 5 tcdent staff 160 Jan 21 10:11 spec\n-rw-r--r--@ 1 tcdent staff 2487 Jan 21 10:11 system.md\ndrwxr-xr-x@ 6 tcdent staff 192 Jan 21 10:11 validator\n", - "background": false - }, - { - "type": "ShellBlock", - "call_id": "toolu_01EVxQx3x3EhvYPMeTugCLRM", - "tool_name": "mcp_shell", - "params": { - "command": "find . -type f -name \"*.py\" -o -name \"*.rs\" -o -name \"*.toml\" -o -name \"*.json\" -o -name \"*.yaml\" -o -name \"*.yml\" -o -name \"*.md\" 2>/dev/null | head -50" - }, - "status": "complete", - "text": "./evals/write_eval/runner.py\n./evals/write_eval/__init__.py\n./evals/write_eval/test_cases.py\n./evals/write_eval/evaluator.py\n./evals/__init__.py\n./evals/common/config.py\n./evals/common/llm_clients.py\n./evals/common/__init__.py\n./evals/cli.py\n./evals/read_eval/runner.py\n./evals/read_eval/worldview_prompt.py\n./evals/read_eval/__init__.py\n./evals/read_eval/test_cases.py\n./evals/read_eval/evaluator.py\n./evals/__main__.py\n./validator/Cargo.toml\n./validator/build.rs\n./validator/src/lib.rs\n./validator/src/main.rs\n./spec/generate.py\n./spec/tokens.yaml\n./agent/Cargo.toml\n./agent/src/main.rs\n./.codey/transcripts/000000.json\n./README.md\n./.github/workflows/evals.yml\n./.github/workflows/build.yml\n./system.md\n./SPEC.md\n", - "background": false - }, - { - "type": "ThinkingBlock", - "text": "This looks like a multi-component project with:\n1. A Rust validator\n2. A Rust agent\n3. Python evals\n4. Some spec files\n5. A worldview file format (.wvf)\n\nLet me dive deeper into each component to understand the architecture.", - "status": "complete" - }, - { - "type": "TextBlock", - "text": "Interesting! This looks like a multi-component project with:\n- **Rust components**: `validator/` and `agent/`\n- **Python evals**: `evals/`\n- **Spec files**: `spec/` with YAML definitions\n- A custom `.wvf` file format\n\nLet me get a better understanding of what this project does by reading the key docs and examining the code structure:", - "status": "complete" - }, - { - "type": "ReadFileBlock", - "call_id": "toolu_01EhuVJ5uqv3PCyoGiei1njy", - "tool_name": "mcp_read_file", - "params": { - "path": "README.md" - }, - "status": "complete", - "text": " 1│# Worldview\n 2│\n 3│A compact notation format for encoding and maintaining conceptual worldviews over time, designed for LLM context persistence.\n 4│\n 5│## Overview\n 6│\n 7│The **Worldview format** (file extension: `.wvf`) is a declarative notation for storing beliefs, stances, and understanding about concepts. Unlike retrieval-augmented generation (RAG) which selectively includes information, Worldview documents are designed to remain *entirely in context* across all LLM interactions.\n 8│\n 9│This solves a fundamental problem: LLMs have foundational beliefs and stances that should inform all reasoning, not just topically-matched queries. The Worldview format is dense enough that an entire belief system can remain in context permanently.\n 10│\n 11│## Quick Start\n 12│\n 13│### Example Document\n 14│\n 15│```wvf\n 16│Trust\n 17│ .formation\n 18│ - slow\n 19│ - requires consistency | over time\n 20│ - contextual @personal-experience\n 21│ .erosion\n 22│ - fast !\n 23│ - single violation => collapse?\n 24│ - asymmetric vs formation &Trust.formation\n 25│```\n 26│\n 27│\n 28│## Language Specification\n 29│\n 30│### Grammar\n 31│\n 32│```ebnf\n 33│document = concept+ ;\n 34│concept = concept_name NEWLINE facet+ ;\n 35│facet = INDENT(2) '.' facet_name NEWLINE claim+ ;\n 36│claim = INDENT(4) '-' claim_body [condition*] [source*] [reference*] ;\n 37│\n 38│condition = '|' text ;\n 39│source = '@' identifier ;\n 40│reference = '&' concept_name ['.' facet_name] ;\n 41│```\n 42│\n 43│### Structure\n 44│\n 45│```\n 46│Concept (unindented, bare text)\n 47│ .facet (2-space indent, dot prefix)\n 48│ - claim (4-space indent, dash prefix)\n 49│```\n 50│\n 51│### Inline Elements\n 52│\n 53│| Symbol | Name | Description |\n 54│|--------|------|-------------|\n 55│| `|` | condition | when/if applies |\n 56│| `@` | source | basis for belief |\n 57│| `&` | reference | links to other concept.facet |\n 58│\n 59│### Brief Forms\n 60│\n 61│Compact operators for common relationships:\n 62│\n 63│| Symbol | Meaning | Example |\n 64│|--------|---------|---------|\n 65│| `=>` | causes, leads to | `power => corruption` |\n 66│| `<=` | caused by, results from | `trust <= consistency` |\n 67│| `<>` | mutual, bidirectional | `accountability <> trust` |\n 68│| `><` | tension, conflicts with | `efficiency >< thoroughness` |\n 69│| `~` | similar to, resembles | `authority ~ influence` |\n 70│| `=` | equivalent to, means | `formal = official` |\n 71│| `vs` | in contrast to | `asymmetric vs formation` |\n 72│| `//` | regardless of | `self-perpetuate // original purpose` |\n 73│\n 74│### Modifiers\n 75│\n 76│Suffix markers that inflect claim meaning:\n 77│\n 78│| Symbol | Meaning | Example |\n 79│|--------|---------|---------|\n 80│| `^` | increasing, trending up | `concentration^` |\n 81│| `v` | decreasing, trending down | `trust v` |\n 82│| `!` | strong, emphatic, high confidence | `fast !` |\n 83│| `?` | uncertain, contested, tentative | `free-will?` |\n 84│| `*` | notable, important, flagged | `paradigm-shift*` |\n 85│\n 86│### Evolution\n 87│\n 88│Supersession marker `[<= prior belief]` indicates a belief that replaces a prior one:\n 89│\n 90│```\n 91│- adaptive [<= inherently good]\n 92│```\n 93│\n 94│### Claim Syntax\n 95│\n 96│Claims follow positional grammar—position implies role:\n 97│\n 98│```\n 99│- claim_text | condition @source &reference\n 100│```\n 101│\n 102│\n 103│\n 104│## Tools\n 105│\n 106│This repository includes a complete toolchain for working with Worldview documents:\n 107│\n 108│### Validator (`worldview-validate`)\n 109│\n 110│Validates `.wvf` files for syntactic correctness.\n 111│\n 112│```bash\n 113│# Validate a file\n 114│worldview-validate example.wvf\n 115│\n 116│# Validate from stdin\n 117│cat example.wvf | worldview-validate --stdin\n 118│```\n 119│\n 120│### Agent CLI (`worldview`)\n 121│\n 122│An AI-powered tool that converts plain-text statements into proper Worldview notation.\n 123│\n 124│```bash\n 125│# Add a fact to a Worldview file\n 126│worldview \"Trust is built slowly through consistent actions\" --file worldview.wvf\n 127│\n 128│# Use a specific model\n 129│worldview \"Power corrupts when unchecked\" --model claude-opus-4-5-20251101\n 130│```\n 131│\n 132│The agent understands the full Worldview specification and will:\n 133│1. Read existing content to understand structure\n 134│2. Determine appropriate concept/facet placement\n 135│3. Format statements using proper notation\n 136│4. Validate before writing\n 137│\n 138│### Evaluation Framework\n 139│\n 140│A Python framework for testing how well LLMs can leverage Worldview-encoded beliefs.\n 141│\n 142│```bash\n 143│# Install dependencies\n 144│pip install -r evals/requirements.txt\n 145│\n 146│# Run evaluations\n 147│python -m evals run --models claude-sonnet gpt-5.2\n 148│\n 149│# Run specific difficulty\n 150│python -m evals run --difficulty extreme\n 151│\n 152│# List available models and test cases\n 153│python -m evals list-models\n 154│python -m evals list-cases\n 155│```\n 156│\n 157│## Project Structure\n 158│\n 159│```\n 160│wvf/\n 161│├── spec/ # Canonical specification\n 162││ ├── tokens.yaml # Token definitions (source of truth)\n 163││ ├── grammar.pest # PEG grammar\n 164││ └── generate.py # Generates docs and code from tokens.yaml\n 165│├── validator/ # Rust validator\n 166││ ├── src/lib.rs # Validation library\n 167││ ├── src/main.rs # CLI tool\n 168││ └── build.rs # Generates tokens from spec at compile time\n 169│├── agent/ # Rust agent CLI\n 170││ └── src/main.rs # LLM-powered notation converter\n 171│├── evals/ # Python evaluation framework\n 172││ ├── cli.py # Evaluation CLI\n 173││ ├── runner.py # Test orchestration\n 174││ ├── evaluator.py # Response scoring\n 175││ ├── test_cases.py # Test case definitions\n 176││ └── llm_clients.py # LLM provider clients\n 177│├── SPEC.md # Full format specification\n 178│├── system.md # Condensed system prompt (generated)\n 179│└── example.wvf # Example document\n 180│```\n 181│\n 182│## Building\n 183│\n 184│### Prerequisites\n 185│\n 186│- Rust 1.70+\n 187│- Python 3.11+\n 188│\n 189│### Build Tools\n 190│\n 191│```bash\n 192│# Build validator\n 193│cd validator && cargo build --release\n 194│\n 195│# Build agent (requires setup for vendored dependencies)\n 196│cd agent && ./setup.sh && cargo build --release\n 197│```\n 198│\n 199│## Design Principles\n 200│\n 201│The Worldview format follows five core principles:\n 202│\n 203│1. **State over narrative**: Capture what is believed, not how it came to be believed\n 204│2. **Predictability allows omission**: If structure makes something inferable, don't write it\n 205│3. **Conflict tolerance**: Real worldviews contain tensions—hold them without forcing resolution\n 206│4. **Freeform vocabulary**: Structure is defined; content remains unconstrained\n 207│5. **LLM-native, human-inspectable**: Optimized for machine parsing while remaining readable\n 208│\n 209│## Why \"Worldview\"?\n 210│\n 211│The name emphasizes the core use case: encoding *how concepts are understood* rather than just storing facts. This is different from:\n 212│\n 213│- **Knowledge bases**: Store facts, not interpretations\n 214│- **RAG systems**: Retrieve relevant content per query\n 215│- **Memory systems**: Log events chronologically\n 216│\n 217│Worldview documents capture the *lens* through which all subsequent reasoning should be filtered.\n 218│\n 219│## File Extension\n 220│\n 221│- **Extension**: `.wvf` (Worldview Format)\n 222│- **MIME type**: `text/x-worldview` (proposed)\n 223│\n 224│## License\n 225│\n 226│MIT\n 227│\n 228│## Specification\n 229│\n 230│See [SPEC.md](SPEC.md) for the complete format specification.\n", - "background": false - }, - { - "type": "ReadFileBlock", - "call_id": "toolu_016iy6Fd2mqSLurEstDQcm8K", - "tool_name": "mcp_read_file", - "params": { - "path": "SPEC.md" - }, - "status": "complete", - "text": " 1│# Worldview Format\n 2│\n 3│**Specification v0.1 — Draft**\n 4│\n 5│---\n 6│\n 7│## Abstract\n 8│\n 9│The Worldview format is a compact, declarative notation for encoding and maintaining conceptual worldviews over time. It provides a structured format for storing beliefs, stances, and understanding about concepts—designed to be included in full within every interaction context rather than retrieved selectively.\n 10│\n 11│The Worldview format is not a general-purpose communication language. It is a specialized format for preserving *how concepts are understood*, optimized for semantic density without sacrificing clarity. The notation is intended to be intuitive for large language models to parse, reason about, and autonomously maintain, while remaining human-inspectable. Beliefs are stored as structured claims with conditions and sources, enabling an LLM to hold persistent context about a user, domain, or system across extended interactions.\n 12│\n 13│---\n 14│\n 15│## Motivation\n 16│\n 17│Large language models operate within fixed context windows. Existing approaches to persistent memory—such as retrieval-augmented generation (RAG)—selectively include information based on relevance to the current query. This works for factual lookup but fails for *worldview*: the foundational beliefs and stances that should inform all reasoning, not just topically-matched queries.\n 18│\n 19│The Worldview format solves this by defining a notation dense enough that an entire belief system (potentially tens of thousands of tokens) can remain in context permanently. Rather than retrieving relevant memories, the LLM carries its complete understanding forward into every interaction.\n 20│\n 21│The format prioritizes:\n 22│- **Semantic density** — Strip prose, keep meaning\n 23│- **Structural consistency** — Predictable hierarchy for reliable parsing\n 24│- **Evolutionary tracking** — Beliefs change; the notation accommodates revision\n 25│- **Autonomous maintenance** — The LLM updates the document without user intervention\n 26│\n 27│---\n 28│\n 29│## Design Principles\n 30│\n 31│**State over narrative**\n 32│The Worldview format captures what is believed, not the story of how it came to be believed. History is preserved compactly when relevant, but the primary representation is current state.\n 33│\n 34│**Predictability allows omission**\n 35│Borrowed from stenographic shorthand: if structure or context makes something inferable, don't write it. No articles, no copulas, no filler.\n 36│\n 37│**Conflict tolerance**\n 38│Real worldviews contain tensions and contradictions. The Worldview format holds conflicting claims without forcing resolution.\n 39│\n 40│**Freeform vocabulary**\n 41│No predefined concept names, facet labels, or claim terms. The notation defines structure and relationships; content remains unconstrained.\n 42│\n 43│**LLM-native, human-inspectable**\n 44│Optimized for machine parsing and reasoning. Human readability is a secondary benefit, not a design constraint.\n 45│\n 46│---\n 47│\n 48│## Inspirations\n 49│\n 50│### Stenographic Shorthand\n 51│Systems like Gregg, Pitman, and Teeline informed the Worldview format's approach to density:\n 52│- **Omission of predictable elements** — Common words and inferable structure are dropped\n 53│- **Brief forms** — High-frequency relationships get compact symbols\n 54│- **Positional grammar** — Location within a line implies role\n 55│- **Affix modification** — Small markers inflect meaning\n 56│\n 57│### Belief Representation\n 58│The Worldview format draws on concepts from epistemology and knowledge representation:\n 59│- Beliefs as claims with conditions (contextualism)\n 60│- Sources as grounding for confidence (evidentialism)\n 61│- Tolerance of contradiction (paraconsistent approaches)\n 62│\n 63│### Configuration Languages\n 64│The hierarchical structure echoes YAML and similar formats, using indentation for nesting while avoiding syntactic overhead like quotes and brackets.\n 65│\n 66│---\n 67│\n 68│## Structure\n 69│\n 70│A Worldview document is a hierarchical collection of beliefs organized as:\n 71│\n 72│```\n 73│Document\n 74│ └── Concept (one or more)\n 75│ └── Facet (one or more per concept)\n 76│ └── Claim (one or more per facet)\n 77│ ├── Condition (zero or more)\n 78│ └── Source (zero or more)\n 79│```\n 80│\n 81│### Definitions\n 82│\n 83│| Element | Description |\n 84│|---------|-------------|\n 85│| **Concept** | A subject of belief—a noun in the worldview (e.g., Power, Trust, Human nature) |\n 86│| **Facet** | An aspect or dimension of a concept (e.g., formation, erosion, institutional) |\n 87│| **Claim** | An assertion about a facet—what is believed to be true |\n 88│| **Condition** | Circumstances under which the claim applies |\n 89│| **Source** | Basis for the belief (observation, experience, citation, intuition) |\n 90│\n 91│### Constraints\n 92│\n 93│- Every concept must have at least one facet\n 94│- Every facet must have at least one claim\n 95│- Conditions and sources are optional per claim\n 96│- Facet names are freeform (no controlled vocabulary)\n 97│- Concepts may reference other concepts, creating a web of related beliefs\n 98│\n 99│---\n 100│\n 101│## Notation\n 102│\n 103│### Hierarchy\n 104│\n 105│| Element | Notation | Indentation |\n 106│|---------|----------|-------------|\n 107│| Concept | Bare text | None (column 0) |\n 108│| Facet | `.` prefix | 2 spaces |\n 109│| Claim | `-` prefix | 4 spaces |\n 110│\n 111│### Inline Elements\n 112│\n 113│| Element | Symbol | Position |\n 114│|---------|--------|----------|\n 115│| Condition | `\\|` | After claim |\n 116│| Source | `@` | After claim/conditions |\n 117│| Reference | `&` | After claim, links to other concept.facet |\n 118│\n 119│### Positional Grammar\n 120│\n 121│Claims follow a consistent order:\n 122│\n 123│```\n 124│- [claim] | [condition] | [condition] @[source] @[source] &[reference]\n 125│```\n 126│\n 127│Position implies role—no labels needed:\n 128│1. Claim text (required)\n 129│2. Conditions (zero or more, `|` prefixed)\n 130│3. Sources (zero or more, `@` prefixed)\n 131│4. References (zero or more, `&` prefixed)\n 132│\n 133│---\n 134│\n 135│## Brief Forms\n 136│\n 137│Common relationships use compact symbols:\n 138│\n 139│| Symbol | Meaning |\n 140│|--------|---------|\n 141│| `=>` | causes, leads to |\n 142│| `<=` | caused by, results from |\n 143│| `<>` | mutual, bidirectional |\n 144│| `><` | tension, conflicts with |\n 145│| `~` | similar to, resembles |\n 146│| `=` | equivalent to, means |\n 147│| `vs` | in contrast to |\n 148│| `//` | regardless of |\n 149│\n 150│### Examples\n 151│\n 152│```\n 153│- power => corruption | unchecked\n 154│- trust <= consistency | over time\n 155│- efficiency >< thoroughness\n 156│- formal-authority ~ informal-influence\n 157│```\n 158│\n 159│---\n 160│\n 161│## Modifiers\n 162│\n 163│Suffix markers inflect claim meaning:\n 164│\n 165│| Modifier | Meaning |\n 166│|----------|---------|\n 167│| `^` | increasing, trending up |\n 168│| `v` | decreasing, trending down |\n 169│| `!` | strong, emphatic, high confidence |\n 170│| `?` | uncertain, contested, tentative |\n 171│| `*` | notable, important, flagged |\n 172│\n 173│### Examples\n 174│\n 175│```\n 176│- institutional-trust v | recent decades\n 177│- free-will? @philosophy\n 178│- single violation => collapse !\n 179│- paradigm-shift* | in progress\n 180│```\n 181│\n 182│---\n 183│\n 184│## Evolution\n 185│\n 186│Beliefs change. The Worldview format represents evolution through:\n 187│\n 188│### Supersession Markers\n 189│\n 190│Prior beliefs can be noted inline with `[<= prior belief]`:\n 191│\n 192│```\n 193│- adaptive, context-dependent [<= inherently good]\n 194│```\n 195│\n 196│This reads: \"Currently believed to be adaptive and context-dependent; this supersedes a prior belief that it was inherently good.\"\n 197│\n 198│### Implicit Evolution\n 199│\n 200│When claims in the same facet track change over time, newer claims are listed first. The array order itself implies evolution without explicit markers.\n 201│\n 202│---\n 203│\n 204│## References\n 205│\n 206│Claims can reference other concepts using `&Concept.facet`:\n 207│\n 208│```\n 209│Trust\n 210│ .erosion\n 211│ - asymmetric to formation &Trust.formation\n 212│ - single violation => collapse &Human-nature.memory\n 213│\n 214│Human-nature\n 215│ .memory\n 216│ - negative events more salient\n 217│ - loss-averse @behavioral-economics\n 218│```\n 219│\n 220│References create a graph of related beliefs, enabling the LLM to traverse connections without duplicating content.\n 221│\n 222│---\n 223│\n 224│## Examples\n 225│\n 226│### Minimal Document\n 227│\n 228│```\n 229│Power\n 230│ .core\n 231│ - corrupts | unchecked\n 232│ - reveals character\n 233│```\n 234│\n 235│### Expanded Document\n 236│\n 237│```\n 238│Power\n 239│ .nature\n 240│ - corrupts | unchecked !\n 241│ - reveals character => self-knowledge\n 242│ - concentration^ => abuse^ @historical-pattern\n 243│ .institutional\n 244│ - self-preserving\n 245│ - accountability <> trust &Trust.institutional\n 246│ - diffusion => dilution-of-responsibility\n 247│\n 248│Trust\n 249│ .formation\n 250│ - slow\n 251│ - requires consistency | over time\n 252│ - contextual @personal-experience\n 253│ .erosion\n 254│ - fast !\n 255│ - single violation => collapse?\n 256│ - asymmetric vs formation &Trust.formation\n 257│ .institutional\n 258│ - possible | high transparency\n 259│ - unlikely | low transparency\n 260│ - rational to withhold | unverifiable @game-theory\n 261│\n 262│Human-nature\n 263│ .social\n 264│ - conformist | formal groups\n 265│ - authentic | solitary\n 266│ - status-aware @evolutionary-psychology\n 267│ - coalition-forming\n 268│ .cognition\n 269│ - pattern-seeking\n 270│ - confirmation-biased @cognitive-science\n 271│ - narrative-constructing\n 272│ - rationalizes post-hoc [<= rational actor]\n 273│ .self-perception\n 274│ - overconfident | familiar domains\n 275│ - miscalibrated @Dunning-Kruger\n 276│ - self-deception => comfort &Human-nature.cognition\n 277│\n 278│Institutions\n 279│ .function\n 280│ - stabilize !\n 281│ - preserve knowledge\n 282│ - coordinate action @game-theory\n 283│ .dysfunction\n 284│ - ossify | over time\n 285│ - self-perpetuate // original purpose\n 286│ - capture-by-interests^ @public-choice-theory\n 287│```\n 288│\n 289│---\n 290│\n 291│## Non-Goals\n 292│\n 293│The Worldview format explicitly does not attempt to:\n 294│\n 295│- **Prove logical consistency** — Contradictions are permitted\n 296│- **Enforce ontology** — No required categories or hierarchies beyond structure\n 297│- **Replace natural language** — The Worldview format is for belief state, not communication\n 298│- **Assert objective truth** — Claims represent understanding, not facts\n 299│- **Store predictions, evaluations, or identity** — These are derived from beliefs, not stored directly\n 300│\n 301│---\n 302│\n 303│## Intended Use Cases\n 304│\n 305│- **Long-term LLM context anchoring** — Persistent worldview across sessions\n 306│- **Belief drift analysis** — Track how understanding evolves\n 307│- **Conceptual memory compression** — Dense storage of learned stances\n 308│- **Domain modeling** — Capture expert understanding of a field\n 309│- **Value alignment documentation** — Record interpretive frameworks\n 310│\n 311│---\n 312│\n 313│## Summary\n 314│\n 315│The Worldview format is a notation for meaning, not conversation. It exists to preserve how concepts are understood—compactly enough to remain always in context, structured enough to reason about reliably, and flexible enough to evolve as understanding changes.\n 316│\n 317│The format encodes:\n 318│- **What** is believed (claims)\n 319│- **When** it applies (conditions)\n 320│- **Why** it's believed (sources)\n 321│- **How** beliefs connect (references)\n 322│- **That** beliefs change (evolution markers)\n 323│\n 324│It deliberately omits:\n 325│- Prose and filler\n 326│- Explicit confidence scores (derived from conditions and sources)\n 327│- Detailed history (supersession markers suffice)\n 328│- Evaluative or predictive statements (derived at runtime)\n 329│\n 330│The Worldview format is designed to be carried forward—a persistent lens through which all subsequent reasoning is filtered.\n", - "background": false - }, - { - "type": "ShellBlock", - "call_id": "toolu_01VNnSAc8qSaj2Jw6NRYoFNG", - "tool_name": "mcp_shell", - "params": { - "command": "wc -l validator/src/*.rs agent/src/*.rs" - }, - "status": "complete", - "text": " 1240 validator/src/lib.rs\n 103 validator/src/main.rs\n 416 agent/src/main.rs\n 1759 total\n", - "background": false - }, - { - "type": "ThinkingBlock", - "text": "This is a really interesting project - a custom domain-specific language for encoding \"worldviews\" (beliefs, stances, understanding) in a compact notation designed to fit entirely within LLM context windows. The project has:\n\n1. A Rust validator (~1240 lines)\n2. A Rust agent CLI (~416 lines) \n3. Python evaluation framework\n\nLet me read through the core Rust code to assess quality, and also look at the spec definition and Python evals.", - "status": "complete" - }, - { - "type": "TextBlock", - "text": "This is a fascinating project! A DSL for encoding \"worldviews\" - beliefs and stances in a compact notation designed to stay entirely in LLM context. Very clever idea.\n\nLet me dive into the core code to assess quality:", - "status": "complete" - }, - { - "type": "ReadFileBlock", - "call_id": "toolu_017z2mE1g13TFpTtNbtnWfQb", - "tool_name": "mcp_read_file", - "params": { - "path": "validator/src/lib.rs" - }, - "status": "complete", - "text": " 1│//! Worldview Validator - A validator for Worldview format files\n 2│//!\n 3│//! This crate provides validation for `.wvf` files according to the Worldview specification.\n 4│//! It checks structural correctness (hierarchy, indentation), claim syntax, brief forms,\n 5│//! modifiers, and evolution markers.\n 6│\n 7│use std::fmt;\n 8│use thiserror::Error;\n 9│\n 10│// Token definitions generated at compile time from spec/tokens.yaml\n 11│include!(concat!(env!(\"OUT_DIR\"), \"/tokens.rs\"));\n 12│\n 13│/// Errors that can occur during Worldview validation\n 14│#[derive(Error, Debug, Clone, PartialEq, Eq)]\n 15│pub enum ValidationError {\n 16│ // Structural errors\n 17│ #[error(\"line {line}: invalid indentation (expected {expected} spaces, found {found})\")]\n 18│ InvalidIndentation {\n 19│ line: usize,\n 20│ expected: &'static str,\n 21│ found: usize,\n 22│ },\n 23│\n 24│ #[error(\"line {line}: facet must have '.' prefix\")]\n 25│ MissingFacetPrefix { line: usize },\n 26│\n 27│ #[error(\"line {line}: claim must have '-' prefix\")]\n 28│ MissingClaimPrefix { line: usize },\n 29│\n 30│ #[error(\"line {line}: concept '{concept}' has no facets\")]\n 31│ ConceptWithoutFacets { line: usize, concept: String },\n 32│\n 33│ #[error(\"line {line}: facet '{facet}' has no claims\")]\n 34│ FacetWithoutClaims { line: usize, facet: String },\n 35│\n 36│ #[error(\"line {line}: orphan facet (no preceding concept)\")]\n 37│ OrphanFacet { line: usize },\n 38│\n 39│ #[error(\"line {line}: orphan claim (no preceding facet)\")]\n 40│ OrphanClaim { line: usize },\n 41│\n 42│ #[error(\"line {line}: empty claim text\")]\n 43│ EmptyClaimText { line: usize },\n 44│\n 45│ #[error(\"line {line}: unexpected indentation level ({found} spaces)\")]\n 46│ UnexpectedIndentation { line: usize, found: usize },\n 47│\n 48│ #[error(\"line {line}: concept name cannot be empty\")]\n 49│ EmptyConceptName { line: usize },\n 50│\n 51│ #[error(\"line {line}: facet name cannot be empty\")]\n 52│ EmptyFacetName { line: usize },\n 53│\n 54│ // Inline element errors\n 55│ #[error(\"line {line}: invalid reference format '{reference}' (expected &Concept.facet)\")]\n 56│ InvalidReferenceFormat { line: usize, reference: String },\n 57│\n 58│ #[error(\"line {line}: empty condition (standalone '|')\")]\n 59│ EmptyCondition { line: usize },\n 60│\n 61│ #[error(\"line {line}: empty source (standalone '@')\")]\n 62│ EmptySource { line: usize },\n 63│\n 64│ #[error(\"line {line}: empty reference (standalone '&')\")]\n 65│ EmptyReference { line: usize },\n 66│\n 67│ // Brief form errors\n 68│ #[error(\"line {line}: brief form '{operator}' missing left operand\")]\n 69│ BriefFormMissingLeftOperand { line: usize, operator: String },\n 70│\n 71│ #[error(\"line {line}: brief form '{operator}' missing right operand\")]\n 72│ BriefFormMissingRightOperand { line: usize, operator: String },\n 73│\n 74│ // Evolution marker errors\n 75│ #[error(\"line {line}: unclosed evolution marker '[<=' (missing ']')\")]\n 76│ UnclosedEvolutionMarker { line: usize },\n 77│\n 78│ #[error(\"line {line}: empty evolution marker '[<= ]' (no prior belief specified)\")]\n 79│ EmptyEvolutionMarker { line: usize },\n 80│\n 81│ #[error(\"line {line}: malformed evolution marker (expected '[<= prior belief]')\")]\n 82│ MalformedEvolutionMarker { line: usize },\n 83│\n 84│ // Modifier warnings (these are softer - might be intentional)\n 85│ #[error(\"line {line}: standalone modifier '{modifier}' may be unintentional\")]\n 86│ StandaloneModifier { line: usize, modifier: String },\n 87│}\n 88│\n 89│impl ValidationError {\n 90│ /// Returns true if this is a warning rather than a hard error\n 91│ pub fn is_warning(&self) -> bool {\n 92│ matches!(self, ValidationError::StandaloneModifier { .. })\n 93│ }\n 94│}\n 95│\n 96│/// The type of a parsed line\n 97│#[derive(Debug, Clone, PartialEq, Eq)]\n 98│pub enum LineType {\n 99│ /// Empty line or whitespace only\n 100│ Blank,\n 101│ /// A concept (unindented text)\n 102│ Concept(String),\n 103│ /// A facet (2-space indent, '.' prefix)\n 104│ Facet(String),\n 105│ /// A claim (4-space indent, '-' prefix)\n 106│ Claim(ClaimData),\n 107│}\n 108│\n 109│/// Parsed claim data\n 110│#[derive(Debug, Clone, PartialEq, Eq)]\n 111│pub struct ClaimData {\n 112│ pub text: String,\n 113│ pub conditions: Vec,\n 114│ pub sources: Vec,\n 115│ pub references: Vec,\n 116│ pub brief_forms: Vec,\n 117│ pub modifiers: Vec,\n 118│ pub evolution: Option,\n 119│}\n 120│\n 121│/// A brief form operator found in a claim\n 122│#[derive(Debug, Clone, PartialEq, Eq)]\n 123│pub struct BriefFormUsage {\n 124│ pub operator: String,\n 125│ pub left_operand: String,\n 126│ pub right_operand: String,\n 127│}\n 128│\n 129│/// A modifier found in a claim\n 130│#[derive(Debug, Clone, PartialEq, Eq)]\n 131│pub struct ModifierUsage {\n 132│ pub symbol: char,\n 133│ pub attached_to: String,\n 134│}\n 135│\n 136│/// An evolution marker [<= prior belief]\n 137│#[derive(Debug, Clone, PartialEq, Eq)]\n 138│pub struct EvolutionMarker {\n 139│ pub prior_belief: String,\n 140│}\n 141│\n 142│/// A parsed line with its metadata\n 143│#[derive(Debug, Clone)]\n 144│pub struct ParsedLine {\n 145│ pub line_number: usize,\n 146│ pub line_type: LineType,\n 147│ pub raw: String,\n 148│}\n 149│\n 150│/// Result of validation\n 151│#[derive(Debug, Clone)]\n 152│pub struct ValidationResult {\n 153│ pub errors: Vec,\n 154│ pub warnings: Vec,\n 155│ pub lines: Vec,\n 156│}\n 157│\n 158│impl ValidationResult {\n 159│ pub fn is_valid(&self) -> bool {\n 160│ self.errors.is_empty()\n 161│ }\n 162│\n 163│ pub fn has_warnings(&self) -> bool {\n 164│ !self.warnings.is_empty()\n 165│ }\n 166│}\n 167│\n 168│impl fmt::Display for ValidationResult {\n 169│ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n 170│ if self.is_valid() && !self.has_warnings() {\n 171│ write!(f, \"Valid Worldview document\")\n 172│ } else if self.is_valid() {\n 173│ writeln!(f, \"Valid Worldview document with {} warning(s):\", self.warnings.len())?;\n 174│ for warning in &self.warnings {\n 175│ writeln!(f, \" {}\", warning)?;\n 176│ }\n 177│ Ok(())\n 178│ } else {\n 179│ writeln!(f, \"Invalid Worldview document ({} error(s)):\", self.errors.len())?;\n 180│ for error in &self.errors {\n 181│ writeln!(f, \" {}\", error)?;\n 182│ }\n 183│ if self.has_warnings() {\n 184│ writeln!(f, \"Additionally, {} warning(s):\", self.warnings.len())?;\n 185│ for warning in &self.warnings {\n 186│ writeln!(f, \" {}\", warning)?;\n 187│ }\n 188│ }\n 189│ Ok(())\n 190│ }\n 191│ }\n 192│}\n 193│\n 194│/// Validates a Worldview document\n 195│pub fn validate(input: &str) -> ValidationResult {\n 196│ let mut errors = Vec::new();\n 197│ let mut warnings = Vec::new();\n 198│ let mut lines = Vec::new();\n 199│\n 200│ // First pass: tokenize lines\n 201│ for (idx, raw_line) in input.lines().enumerate() {\n 202│ let line_number = idx + 1;\n 203│ let parsed = tokenize_line(raw_line, line_number, &mut errors);\n 204│ lines.push(ParsedLine {\n 205│ line_number,\n 206│ line_type: parsed,\n 207│ raw: raw_line.to_string(),\n 208│ });\n 209│ }\n 210│\n 211│ // Second pass: validate structure\n 212│ validate_structure(&lines, &mut errors);\n 213│\n 214│ // Third pass: validate claim syntax including brief forms, modifiers, evolution\n 215│ for line in &lines {\n 216│ if let LineType::Claim(claim) = &line.line_type {\n 217│ validate_claim_syntax(line.line_number, claim, &mut errors, &mut warnings);\n 218│ }\n 219│ }\n 220│\n 221│ ValidationResult { errors, warnings, lines }\n 222│}\n 223│\n 224│/// Count leading spaces\n 225│fn count_leading_spaces(line: &str) -> usize {\n 226│ line.chars().take_while(|c| *c == ' ').count()\n 227│}\n 228│\n 229│/// Tokenize a single line\n 230│fn tokenize_line(line: &str, line_number: usize, errors: &mut Vec) -> LineType {\n 231│ // Blank lines\n 232│ if line.trim().is_empty() {\n 233│ return LineType::Blank;\n 234│ }\n 235│\n 236│ let indent = count_leading_spaces(line);\n 237│ let content = line.trim();\n 238│\n 239│ match indent {\n 240│ 0 => {\n 241│ // Concept: no indent, bare text\n 242│ if content.is_empty() {\n 243│ errors.push(ValidationError::EmptyConceptName { line: line_number });\n 244│ LineType::Blank\n 245│ } else {\n 246│ LineType::Concept(content.to_string())\n 247│ }\n 248│ }\n 249│ 2 => {\n 250│ // Facet: 2-space indent, '.' prefix\n 251│ if !content.starts_with('.') {\n 252│ errors.push(ValidationError::MissingFacetPrefix { line: line_number });\n 253│ LineType::Blank\n 254│ } else {\n 255│ let name = content[1..].trim();\n 256│ if name.is_empty() {\n 257│ errors.push(ValidationError::EmptyFacetName { line: line_number });\n 258│ }\n 259│ LineType::Facet(name.to_string())\n 260│ }\n 261│ }\n 262│ 4 => {\n 263│ // Claim: 4-space indent, '-' prefix\n 264│ if !content.starts_with('-') {\n 265│ errors.push(ValidationError::MissingClaimPrefix { line: line_number });\n 266│ LineType::Blank\n 267│ } else {\n 268│ let claim_text = content[1..].trim();\n 269│ let claim_data = parse_claim(claim_text);\n 270│ LineType::Claim(claim_data)\n 271│ }\n 272│ }\n 273│ _ => {\n 274│ // Invalid indentation\n 275│ if indent == 1 || indent == 3 {\n 276│ errors.push(ValidationError::InvalidIndentation {\n 277│ line: line_number,\n 278│ expected: \"0, 2, or 4\",\n 279│ found: indent,\n 280│ });\n 281│ } else {\n 282│ errors.push(ValidationError::UnexpectedIndentation {\n 283│ line: line_number,\n 284│ found: indent,\n 285│ });\n 286│ }\n 287│ LineType::Blank\n 288│ }\n 289│ }\n 290│}\n 291│\n 292│/// Parse claim content into structured data\n 293│fn parse_claim(text: &str) -> ClaimData {\n 294│ let mut claim_text = String::new();\n 295│ let mut conditions = Vec::new();\n 296│ let mut sources = Vec::new();\n 297│ let mut references = Vec::new();\n 298│\n 299│ // First, extract evolution marker if present\n 300│ let (text_without_evolution, evolution) = extract_evolution_marker(text);\n 301│ let text = text_without_evolution.as_str();\n 302│\n 303│ // Parse inline elements (|, @, &)\n 304│ let mut current_segment = String::new();\n 305│ let mut in_claim = true;\n 306│ let mut chars = text.chars().peekable();\n 307│\n 308│ while let Some(c) = chars.next() {\n 309│ match c {\n 310│ '|' => {\n 311│ // Condition marker\n 312│ if in_claim {\n 313│ claim_text = current_segment.trim().to_string();\n 314│ in_claim = false;\n 315│ } else if !current_segment.trim().is_empty() {\n 316│ conditions.push(current_segment.trim().to_string());\n 317│ }\n 318│ current_segment = String::new();\n 319│ }\n 320│ '@' => {\n 321│ // Source marker\n 322│ if in_claim {\n 323│ claim_text = current_segment.trim().to_string();\n 324│ in_claim = false;\n 325│ } else if !current_segment.trim().is_empty() {\n 326│ conditions.push(current_segment.trim().to_string());\n 327│ }\n 328│ current_segment = String::new();\n 329│ // Collect source name (until space or another marker)\n 330│ while let Some(&next) = chars.peek() {\n 331│ if next == ' ' || next == '|' || next == '@' || next == '&' {\n 332│ break;\n 333│ }\n 334│ current_segment.push(chars.next().unwrap());\n 335│ }\n 336│ if !current_segment.trim().is_empty() {\n 337│ sources.push(current_segment.trim().to_string());\n 338│ }\n 339│ current_segment = String::new();\n 340│ }\n 341│ '&' => {\n 342│ // Reference marker\n 343│ if in_claim {\n 344│ claim_text = current_segment.trim().to_string();\n 345│ in_claim = false;\n 346│ } else if !current_segment.trim().is_empty() {\n 347│ conditions.push(current_segment.trim().to_string());\n 348│ }\n 349│ current_segment = String::new();\n 350│ // Collect reference (until space or another marker)\n 351│ while let Some(&next) = chars.peek() {\n 352│ if next == ' ' || next == '|' || next == '@' || next == '&' {\n 353│ break;\n 354│ }\n 355│ current_segment.push(chars.next().unwrap());\n 356│ }\n 357│ if !current_segment.trim().is_empty() {\n 358│ references.push(current_segment.trim().to_string());\n 359│ }\n 360│ current_segment = String::new();\n 361│ }\n 362│ _ => {\n 363│ current_segment.push(c);\n 364│ }\n 365│ }\n 366│ }\n 367│\n 368│ // Handle remaining segment\n 369│ if !current_segment.trim().is_empty() {\n 370│ if in_claim {\n 371│ claim_text = current_segment.trim().to_string();\n 372│ } else {\n 373│ conditions.push(current_segment.trim().to_string());\n 374│ }\n 375│ }\n 376│\n 377│ // Extract brief forms from claim text\n 378│ let brief_forms = extract_brief_forms(&claim_text);\n 379│\n 380│ // Extract modifiers from claim text\n 381│ let modifiers = extract_modifiers(&claim_text);\n 382│\n 383│ ClaimData {\n 384│ text: claim_text,\n 385│ conditions,\n 386│ sources,\n 387│ references,\n 388│ brief_forms,\n 389│ modifiers,\n 390│ evolution,\n 391│ }\n 392│}\n 393│\n 394│/// Extract evolution marker [<= prior belief] from text\n 395│fn extract_evolution_marker(text: &str) -> (String, Option) {\n 396│ if let Some(start) = text.find(\"[<=\") {\n 397│ if let Some(end) = text[start..].find(']') {\n 398│ let marker_content = &text[start + 3..start + end];\n 399│ let prior_belief = marker_content.trim().to_string();\n 400│ let text_before = &text[..start];\n 401│ let text_after = &text[start + end + 1..];\n 402│ let cleaned = format!(\"{}{}\", text_before.trim(), text_after.trim());\n 403│ return (\n 404│ cleaned,\n 405│ Some(EvolutionMarker { prior_belief }),\n 406│ );\n 407│ }\n 408│ // Unclosed marker - return as-is, validation will catch it\n 409│ return (text.to_string(), None);\n 410│ }\n 411│ (text.to_string(), None)\n 412│}\n 413│\n 414│/// Extract brief form usages from claim text\n 415│fn extract_brief_forms(text: &str) -> Vec {\n 416│ let mut usages = Vec::new();\n 417│\n 418│ // Check for each brief form operator\n 419│ // Order matters: check longer operators first to avoid partial matches\n 420│ let operators_by_length: &[&str] = &[\"=>\", \"<=\", \"<>\", \"><\", \"//\", \"vs\", \"~\", \"=\"];\n 421│\n 422│ let remaining = text.to_string();\n 423│\n 424│ for &op in operators_by_length {\n 425│ // Skip <= if it's part of [<= (evolution marker)\n 426│ if op == \"<=\" && remaining.contains(\"[<=\") {\n 427│ continue;\n 428│ }\n 429│\n 430│ // Special handling for = to avoid matching => or <=\n 431│ if op == \"=\" {\n 432│ // Look for standalone = not part of => or <=\n 433│ let mut i = 0;\n 434│ let chars: Vec = remaining.chars().collect();\n 435│ while i < chars.len() {\n 436│ if chars[i] == '=' {\n 437│ let prev = if i > 0 { Some(chars[i - 1]) } else { None };\n 438│ let next = chars.get(i + 1);\n 439│ // Check it's not part of =>, <=, or <>\n 440│ if prev != Some('<') && prev != Some('>') && next != Some(&'>') {\n 441│ // Found standalone =\n 442│ let before: String = chars[..i].iter().collect();\n 443│ let after: String = chars[i + 1..].iter().collect();\n 444│ let left = before.split_whitespace().last().unwrap_or(\"\").to_string();\n 445│ let right = after.split_whitespace().next().unwrap_or(\"\").to_string();\n 446│ if !left.is_empty() || !right.is_empty() {\n 447│ usages.push(BriefFormUsage {\n 448│ operator: op.to_string(),\n 449│ left_operand: left,\n 450│ right_operand: right,\n 451│ });\n 452│ }\n 453│ }\n 454│ }\n 455│ i += 1;\n 456│ }\n 457│ continue;\n 458│ }\n 459│\n 460│ // For other operators\n 461│ for (idx, _) in remaining.match_indices(op) {\n 462│ let before = &remaining[..idx];\n 463│ let after = &remaining[idx + op.len()..];\n 464│\n 465│ let left = before.split_whitespace().last().unwrap_or(\"\").to_string();\n 466│ let right = after.split_whitespace().next().unwrap_or(\"\").to_string();\n 467│\n 468│ // Clean up modifiers from operands for matching\n 469│ let left_clean = left.trim_end_matches(|c| \"^v!?*\".contains(c));\n 470│ let right_clean = right.trim_end_matches(|c| \"^v!?*\".contains(c));\n 471│\n 472│ usages.push(BriefFormUsage {\n 473│ operator: op.to_string(),\n 474│ left_operand: left_clean.to_string(),\n 475│ right_operand: right_clean.to_string(),\n 476│ });\n 477│ }\n 478│ }\n 479│\n 480│ usages\n 481│}\n 482│\n 483│/// Extract modifier usages from claim text\n 484│fn extract_modifiers(text: &str) -> Vec {\n 485│ let mut usages = Vec::new();\n 486│ let modifier_chars = ['^', '!', '?', '*'];\n 487│\n 488│ // Split into tokens\n 489│ let tokens: Vec<&str> = text.split_whitespace().collect();\n 490│\n 491│ for (i, token) in tokens.iter().enumerate() {\n 492│ // Check for attached modifiers (e.g., \"concentration^\", \"collapse?\")\n 493│ for &m in &modifier_chars {\n 494│ if token.ends_with(m) && token.len() > 1 {\n 495│ let attached = token.trim_end_matches(m);\n 496│ usages.push(ModifierUsage {\n 497│ symbol: m,\n 498│ attached_to: attached.to_string(),\n 499│ });\n 500│ }\n 501│ }\n 502│\n 503│ // Check for standalone modifiers (e.g., \"fast !\" where ! is separate token)\n 504│ // These modify the preceding term\n 505│ for &m in &modifier_chars {\n 506│ if token.len() == 1 && token.chars().next() == Some(m) && i > 0 {\n 507│ let prev = tokens[i - 1];\n 508│ // Don't count if previous token is an operator\n 509│ let is_after_operator = BRIEF_FORMS.iter().any(|(op, _)| prev.ends_with(op));\n 510│ if !is_after_operator {\n 511│ usages.push(ModifierUsage {\n 512│ symbol: m,\n 513│ attached_to: prev.trim_end_matches(|c| \"^!?*v\".contains(c)).to_string(),\n 514│ });\n 515│ }\n 516│ }\n 517│ }\n 518│\n 519│ // Check for 'v' modifier - it's special because it's also a letter\n 520│ // It's a modifier when: standalone 'v' following a term\n 521│ if *token == \"v\" && i > 0 {\n 522│ let prev = tokens[i - 1];\n 523│ // Don't treat 'v' as modifier if previous token is an operator\n 524│ let is_after_operator = BRIEF_FORMS.iter().any(|(op, _)| prev.ends_with(op));\n 525│ if !is_after_operator {\n 526│ usages.push(ModifierUsage {\n 527│ symbol: 'v',\n 528│ attached_to: prev.trim_end_matches(|c| \"^!?*\".contains(c)).to_string(),\n 529│ });\n 530│ }\n 531│ }\n 532│ }\n 533│\n 534│ usages\n 535│}\n 536│\n 537│/// Validate document structure\n 538│fn validate_structure(lines: &[ParsedLine], errors: &mut Vec) {\n 539│ let mut current_concept: Option<(usize, String)> = None;\n 540│ let mut current_facet: Option<(usize, String)> = None;\n 541│ let mut concept_has_facet = false;\n 542│ let mut facet_has_claim = false;\n 543│\n 544│ for line in lines {\n 545│ match &line.line_type {\n 546│ LineType::Blank => continue,\n 547│ LineType::Concept(name) => {\n 548│ // Check previous concept had facets\n 549│ if let Some((concept_line, concept_name)) = current_concept.take() {\n 550│ if !concept_has_facet {\n 551│ errors.push(ValidationError::ConceptWithoutFacets {\n 552│ line: concept_line,\n 553│ concept: concept_name,\n 554│ });\n 555│ }\n 556│ }\n 557│ // Check previous facet had claims\n 558│ if let Some((facet_line, facet_name)) = current_facet.take() {\n 559│ if !facet_has_claim {\n 560│ errors.push(ValidationError::FacetWithoutClaims {\n 561│ line: facet_line,\n 562│ facet: facet_name,\n 563│ });\n 564│ }\n 565│ }\n 566│ current_concept = Some((line.line_number, name.clone()));\n 567│ current_facet = None;\n 568│ concept_has_facet = false;\n 569│ facet_has_claim = false;\n 570│ }\n 571│ LineType::Facet(name) => {\n 572│ if current_concept.is_none() {\n 573│ errors.push(ValidationError::OrphanFacet {\n 574│ line: line.line_number,\n 575│ });\n 576│ } else {\n 577│ concept_has_facet = true;\n 578│ }\n 579│ // Check previous facet had claims\n 580│ if let Some((facet_line, facet_name)) = current_facet.take() {\n 581│ if !facet_has_claim {\n 582│ errors.push(ValidationError::FacetWithoutClaims {\n 583│ line: facet_line,\n 584│ facet: facet_name,\n 585│ });\n 586│ }\n 587│ }\n 588│ current_facet = Some((line.line_number, name.clone()));\n 589│ facet_has_claim = false;\n 590│ }\n 591│ LineType::Claim(_) => {\n 592│ if current_facet.is_none() {\n 593│ errors.push(ValidationError::OrphanClaim {\n 594│ line: line.line_number,\n 595│ });\n 596│ } else {\n 597│ facet_has_claim = true;\n 598│ }\n 599│ }\n 600│ }\n 601│ }\n 602│\n 603│ // Check final concept and facet\n 604│ if let Some((concept_line, concept_name)) = current_concept {\n 605│ if !concept_has_facet {\n 606│ errors.push(ValidationError::ConceptWithoutFacets {\n 607│ line: concept_line,\n 608│ concept: concept_name,\n 609│ });\n 610│ }\n 611│ }\n 612│ if let Some((facet_line, facet_name)) = current_facet {\n 613│ if !facet_has_claim {\n 614│ errors.push(ValidationError::FacetWithoutClaims {\n 615│ line: facet_line,\n 616│ facet: facet_name,\n 617│ });\n 618│ }\n 619│ }\n 620│}\n 621│\n 622│/// Validate claim syntax including brief forms, modifiers, and evolution markers\n 623│fn validate_claim_syntax(\n 624│ line_number: usize,\n 625│ claim: &ClaimData,\n 626│ errors: &mut Vec,\n 627│ warnings: &mut Vec,\n 628│) {\n 629│ // Check for empty claim text\n 630│ if claim.text.is_empty() {\n 631│ errors.push(ValidationError::EmptyClaimText { line: line_number });\n 632│ }\n 633│\n 634│ // Check for empty conditions\n 635│ for cond in &claim.conditions {\n 636│ if cond.is_empty() {\n 637│ errors.push(ValidationError::EmptyCondition { line: line_number });\n 638│ }\n 639│ }\n 640│\n 641│ // Check for empty sources\n 642│ for src in &claim.sources {\n 643│ if src.is_empty() {\n 644│ errors.push(ValidationError::EmptySource { line: line_number });\n 645│ }\n 646│ }\n 647│\n 648│ // Check for empty references\n 649│ for reference in &claim.references {\n 650│ if reference.is_empty() {\n 651│ errors.push(ValidationError::EmptyReference { line: line_number });\n 652│ }\n 653│ }\n 654│\n 655│ // Validate reference format (should be Concept.facet)\n 656│ for reference in &claim.references {\n 657│ if !reference.is_empty() && !reference.contains('.') {\n 658│ errors.push(ValidationError::InvalidReferenceFormat {\n 659│ line: line_number,\n 660│ reference: reference.clone(),\n 661│ });\n 662│ }\n 663│ }\n 664│\n 665│ // Validate brief forms have operands\n 666│ for bf in &claim.brief_forms {\n 667│ if bf.left_operand.is_empty() {\n 668│ errors.push(ValidationError::BriefFormMissingLeftOperand {\n 669│ line: line_number,\n 670│ operator: bf.operator.clone(),\n 671│ });\n 672│ }\n 673│ if bf.right_operand.is_empty() {\n 674│ errors.push(ValidationError::BriefFormMissingRightOperand {\n 675│ line: line_number,\n 676│ operator: bf.operator.clone(),\n 677│ });\n 678│ }\n 679│ }\n 680│\n 681│ // Check for unclosed evolution markers in original text\n 682│ if claim.text.contains(\"[<=\") && !claim.text.contains(']') {\n 683│ errors.push(ValidationError::UnclosedEvolutionMarker { line: line_number });\n 684│ }\n 685│\n 686│ // Validate evolution marker content if present\n 687│ if let Some(ref evo) = claim.evolution {\n 688│ if evo.prior_belief.is_empty() {\n 689│ errors.push(ValidationError::EmptyEvolutionMarker { line: line_number });\n 690│ }\n 691│ }\n 692│\n 693│ // Check for standalone modifiers that appear at the start (warning, not error)\n 694│ // Space-separated modifiers that follow a term are valid (e.g., \"fast !\")\n 695│ let tokens: Vec<&str> = claim.text.split_whitespace().collect();\n 696│ for (i, token) in tokens.iter().enumerate() {\n 697│ if *token == \"^\" || *token == \"!\" || *token == \"?\" || *token == \"*\" {\n 698│ // Only warn if it's at the start (no preceding term) or follows an operator\n 699│ if i == 0 {\n 700│ warnings.push(ValidationError::StandaloneModifier {\n 701│ line: line_number,\n 702│ modifier: token.to_string(),\n 703│ });\n 704│ } else {\n 705│ let prev = tokens[i - 1];\n 706│ let is_after_operator = BRIEF_FORMS.iter().any(|(op, _)| prev.ends_with(op));\n 707│ if is_after_operator {\n 708│ warnings.push(ValidationError::StandaloneModifier {\n 709│ line: line_number,\n 710│ modifier: token.to_string(),\n 711│ });\n 712│ }\n 713│ }\n 714│ }\n 715│ }\n 716│}\n 717│\n 718│/// Validate a file by path\n 719│pub fn validate_file(path: &std::path::Path) -> Result {\n 720│ let content = std::fs::read_to_string(path)?;\n 721│ Ok(validate(&content))\n 722│}\n 723│\n 724│#[cfg(test)]\n 725│mod tests {\n 726│ use super::*;\n 727│\n 728│ // ==================== Structural tests ====================\n 729│\n 730│ #[test]\n 731│ fn test_valid_minimal_document() {\n 732│ let input = r#\"Power\n 733│ .core\n 734│ - corrupts | unchecked\n 735│ - reveals character\"#;\n 736│\n 737│ let result = validate(input);\n 738│ assert!(result.is_valid(), \"Expected valid document: {:?}\", result.errors);\n 739│ }\n 740│\n 741│ #[test]\n 742│ fn test_concept_without_facet() {\n 743│ let input = \"Power\\nTrust\";\n 744│ let result = validate(input);\n 745│ assert!(!result.is_valid());\n 746│ assert!(result.errors.iter().any(|e| matches!(e, ValidationError::ConceptWithoutFacets { .. })));\n 747│ }\n 748│\n 749│ #[test]\n 750│ fn test_facet_without_claim() {\n 751│ let input = r#\"Power\n 752│ .core\"#;\n 753│ let result = validate(input);\n 754│ assert!(!result.is_valid());\n 755│ assert!(result.errors.iter().any(|e| matches!(e, ValidationError::FacetWithoutClaims { .. })));\n 756│ }\n 757│\n 758│ #[test]\n 759│ fn test_orphan_facet() {\n 760│ let input = \" .core\";\n 761│ let result = validate(input);\n 762│ assert!(!result.is_valid());\n 763│ assert!(result.errors.iter().any(|e| matches!(e, ValidationError::OrphanFacet { .. })));\n 764│ }\n 765│\n 766│ #[test]\n 767│ fn test_orphan_claim() {\n 768│ let input = \" - corrupts\";\n 769│ let result = validate(input);\n 770│ assert!(!result.is_valid());\n 771│ assert!(result.errors.iter().any(|e| matches!(e, ValidationError::OrphanClaim { .. })));\n 772│ }\n 773│\n 774│ #[test]\n 775│ fn test_invalid_indentation() {\n 776│ let input = r#\"Power\n 777│ .core\"#;\n 778│ let result = validate(input);\n 779│ assert!(!result.is_valid());\n 780│ assert!(result.errors.iter().any(|e| matches!(e, ValidationError::InvalidIndentation { .. })));\n 781│ }\n 782│\n 783│ #[test]\n 784│ fn test_missing_facet_prefix() {\n 785│ let input = r#\"Power\n 786│ core\n 787│ - corrupts\"#;\n 788│ let result = validate(input);\n 789│ assert!(!result.is_valid());\n 790│ assert!(result.errors.iter().any(|e| matches!(e, ValidationError::MissingFacetPrefix { .. })));\n 791│ }\n 792│\n 793│ #[test]\n 794│ fn test_missing_claim_prefix() {\n 795│ let input = r#\"Power\n 796│ .core\n 797│ corrupts\"#;\n 798│ let result = validate(input);\n 799│ assert!(!result.is_valid());\n 800│ assert!(result.errors.iter().any(|e| matches!(e, ValidationError::MissingClaimPrefix { .. })));\n 801│ }\n 802│\n 803│ #[test]\n 804│ fn test_blank_lines_allowed() {\n 805│ let input = r#\"Power\n 806│ .core\n 807│ - corrupts\n 808│\n 809│Trust\n 810│ .formation\n 811│ - slow\"#;\n 812│\n 813│ let result = validate(input);\n 814│ assert!(result.is_valid(), \"Expected valid: {:?}\", result.errors);\n 815│ }\n 816│\n 817│ // ==================== Inline element tests ====================\n 818│\n 819│ #[test]\n 820│ fn test_claim_with_conditions_and_sources() {\n 821│ let input = r#\"Trust\n 822│ .formation\n 823│ - requires consistency | over time @personal-experience\"#;\n 824│\n 825│ let result = validate(input);\n 826│ assert!(result.is_valid(), \"Expected valid: {:?}\", result.errors);\n 827│\n 828│ if let Some(line) = result.lines.iter().find(|l| matches!(l.line_type, LineType::Claim(_))) {\n 829│ if let LineType::Claim(claim) = &line.line_type {\n 830│ assert_eq!(claim.text, \"requires consistency\");\n 831│ assert!(claim.conditions.contains(&\"over time\".to_string()));\n 832│ assert!(claim.sources.contains(&\"personal-experience\".to_string()));\n 833│ }\n 834│ }\n 835│ }\n 836│\n 837│ #[test]\n 838│ fn test_claim_with_reference() {\n 839│ let input = r#\"Trust\n 840│ .erosion\n 841│ - asymmetric vs formation &Trust.formation\"#;\n 842│\n 843│ let result = validate(input);\n 844│ assert!(result.is_valid(), \"Expected valid: {:?}\", result.errors);\n 845│\n 846│ if let Some(line) = result.lines.iter().find(|l| matches!(l.line_type, LineType::Claim(_))) {\n 847│ if let LineType::Claim(claim) = &line.line_type {\n 848│ assert!(claim.references.contains(&\"Trust.formation\".to_string()));\n 849│ }\n 850│ }\n 851│ }\n 852│\n 853│ #[test]\n 854│ fn test_invalid_reference_format() {\n 855│ let input = r#\"Power\n 856│ .core\n 857│ - corrupts &InvalidReference\"#;\n 858│ let result = validate(input);\n 859│ assert!(!result.is_valid());\n 860│ assert!(result.errors.iter().any(|e| matches!(e, ValidationError::InvalidReferenceFormat { .. })));\n 861│ }\n 862│\n 863│ // ==================== Brief form tests ====================\n 864│\n 865│ #[test]\n 866│ fn test_brief_form_causes() {\n 867│ let input = r#\"Power\n 868│ .nature\n 869│ - power => corruption\"#;\n 870│\n 871│ let result = validate(input);\n 872│ assert!(result.is_valid(), \"Expected valid: {:?}\", result.errors);\n 873│\n 874│ if let Some(line) = result.lines.iter().find(|l| matches!(l.line_type, LineType::Claim(_))) {\n 875│ if let LineType::Claim(claim) = &line.line_type {\n 876│ assert!(!claim.brief_forms.is_empty());\n 877│ let bf = &claim.brief_forms[0];\n 878│ assert_eq!(bf.operator, \"=>\");\n 879│ assert_eq!(bf.left_operand, \"power\");\n 880│ assert_eq!(bf.right_operand, \"corruption\");\n 881│ }\n 882│ }\n 883│ }\n 884│\n 885│ #[test]\n 886│ fn test_brief_form_caused_by() {\n 887│ let input = r#\"Trust\n 888│ .formation\n 889│ - trust <= consistency\"#;\n 890│\n 891│ let result = validate(input);\n 892│ assert!(result.is_valid(), \"Expected valid: {:?}\", result.errors);\n 893│\n 894│ if let Some(line) = result.lines.iter().find(|l| matches!(l.line_type, LineType::Claim(_))) {\n 895│ if let LineType::Claim(claim) = &line.line_type {\n 896│ let bf = claim.brief_forms.iter().find(|b| b.operator == \"<=\");\n 897│ assert!(bf.is_some(), \"Expected <= operator\");\n 898│ }\n 899│ }\n 900│ }\n 901│\n 902│ #[test]\n 903│ fn test_brief_form_mutual() {\n 904│ let input = r#\"Trust\n 905│ .institutional\n 906│ - accountability <> trust\"#;\n 907│\n 908│ let result = validate(input);\n 909│ assert!(result.is_valid(), \"Expected valid: {:?}\", result.errors);\n 910│\n 911│ if let Some(line) = result.lines.iter().find(|l| matches!(l.line_type, LineType::Claim(_))) {\n 912│ if let LineType::Claim(claim) = &line.line_type {\n 913│ let bf = claim.brief_forms.iter().find(|b| b.operator == \"<>\");\n 914│ assert!(bf.is_some(), \"Expected <> operator\");\n 915│ }\n 916│ }\n 917│ }\n 918│\n 919│ #[test]\n 920│ fn test_brief_form_tension() {\n 921│ let input = r#\"Work\n 922│ .balance\n 923│ - efficiency >< thoroughness\"#;\n 924│\n 925│ let result = validate(input);\n 926│ assert!(result.is_valid(), \"Expected valid: {:?}\", result.errors);\n 927│\n 928│ if let Some(line) = result.lines.iter().find(|l| matches!(l.line_type, LineType::Claim(_))) {\n 929│ if let LineType::Claim(claim) = &line.line_type {\n 930│ let bf = claim.brief_forms.iter().find(|b| b.operator == \"><\");\n 931│ assert!(bf.is_some(), \"Expected >< operator\");\n 932│ }\n 933│ }\n 934│ }\n 935│\n 936│ #[test]\n 937│ fn test_brief_form_similar() {\n 938│ let input = r#\"Authority\n 939│ .types\n 940│ - formal-authority ~ informal-influence\"#;\n 941│\n 942│ let result = validate(input);\n 943│ assert!(result.is_valid(), \"Expected valid: {:?}\", result.errors);\n 944│\n 945│ if let Some(line) = result.lines.iter().find(|l| matches!(l.line_type, LineType::Claim(_))) {\n 946│ if let LineType::Claim(claim) = &line.line_type {\n 947│ let bf = claim.brief_forms.iter().find(|b| b.operator == \"~\");\n 948│ assert!(bf.is_some(), \"Expected ~ operator\");\n 949│ }\n 950│ }\n 951│ }\n 952│\n 953│ #[test]\n 954│ fn test_brief_form_contrast() {\n 955│ let input = r#\"Trust\n 956│ .erosion\n 957│ - asymmetric vs formation\"#;\n 958│\n 959│ let result = validate(input);\n 960│ assert!(result.is_valid(), \"Expected valid: {:?}\", result.errors);\n 961│\n 962│ if let Some(line) = result.lines.iter().find(|l| matches!(l.line_type, LineType::Claim(_))) {\n 963│ if let LineType::Claim(claim) = &line.line_type {\n 964│ let bf = claim.brief_forms.iter().find(|b| b.operator == \"vs\");\n 965│ assert!(bf.is_some(), \"Expected vs operator\");\n 966│ }\n 967│ }\n 968│ }\n 969│\n 970│ #[test]\n 971│ fn test_brief_form_regardless() {\n 972│ let input = r#\"Institutions\n 973│ .dysfunction\n 974│ - self-perpetuate // original purpose\"#;\n 975│\n 976│ let result = validate(input);\n 977│ assert!(result.is_valid(), \"Expected valid: {:?}\", result.errors);\n 978│\n 979│ if let Some(line) = result.lines.iter().find(|l| matches!(l.line_type, LineType::Claim(_))) {\n 980│ if let LineType::Claim(claim) = &line.line_type {\n 981│ let bf = claim.brief_forms.iter().find(|b| b.operator == \"//\");\n 982│ assert!(bf.is_some(), \"Expected // operator\");\n 983│ }\n 984│ }\n 985│ }\n 986│\n 987│ #[test]\n 988│ fn test_brief_form_missing_left_operand() {\n 989│ let input = r#\"Power\n 990│ .core\n 991│ - => corruption\"#;\n 992│\n 993│ let result = validate(input);\n 994│ assert!(!result.is_valid());\n 995│ assert!(result.errors.iter().any(|e| matches!(e, ValidationError::BriefFormMissingLeftOperand { .. })));\n 996│ }\n 997│\n 998│ #[test]\n 999│ fn test_brief_form_missing_right_operand() {\n1000│ let input = r#\"Power\n1001│ .core\n1002│ - power =>\"#;\n1003│\n1004│ let result = validate(input);\n1005│ assert!(!result.is_valid());\n1006│ assert!(result.errors.iter().any(|e| matches!(e, ValidationError::BriefFormMissingRightOperand { .. })));\n1007│ }\n1008│\n1009│ // ==================== Modifier tests ====================\n1010│\n1011│ #[test]\n1012│ fn test_modifier_increasing() {\n1013│ let input = r#\"Power\n1014│ .nature\n1015│ - concentration^ => abuse\"#;\n1016│\n1017│ let result = validate(input);\n1018│ assert!(result.is_valid(), \"Expected valid: {:?}\", result.errors);\n1019│\n1020│ if let Some(line) = result.lines.iter().find(|l| matches!(l.line_type, LineType::Claim(_))) {\n1021│ if let LineType::Claim(claim) = &line.line_type {\n1022│ let m = claim.modifiers.iter().find(|m| m.symbol == '^');\n1023│ assert!(m.is_some(), \"Expected ^ modifier\");\n1024│ assert_eq!(m.unwrap().attached_to, \"concentration\");\n1025│ }\n1026│ }\n1027│ }\n1028│\n1029│ #[test]\n1030│ fn test_modifier_decreasing() {\n1031│ let input = r#\"Trust\n1032│ .trends\n1033│ - institutional-trust v | recent decades\"#;\n1034│\n1035│ let result = validate(input);\n1036│ assert!(result.is_valid(), \"Expected valid: {:?}\", result.errors);\n1037│\n1038│ if let Some(line) = result.lines.iter().find(|l| matches!(l.line_type, LineType::Claim(_))) {\n1039│ if let LineType::Claim(claim) = &line.line_type {\n1040│ let m = claim.modifiers.iter().find(|m| m.symbol == 'v');\n1041│ assert!(m.is_some(), \"Expected v modifier\");\n1042│ }\n1043│ }\n1044│ }\n1045│\n1046│ #[test]\n1047│ fn test_modifier_emphatic() {\n1048│ let input = r#\"Trust\n1049│ .erosion\n1050│ - fast !\"#;\n1051│\n1052│ let result = validate(input);\n1053│ assert!(result.is_valid(), \"Expected valid: {:?}\", result.errors);\n1054│\n1055│ if let Some(line) = result.lines.iter().find(|l| matches!(l.line_type, LineType::Claim(_))) {\n1056│ if let LineType::Claim(claim) = &line.line_type {\n1057│ let m = claim.modifiers.iter().find(|m| m.symbol == '!');\n1058│ assert!(m.is_some(), \"Expected ! modifier\");\n1059│ }\n1060│ }\n1061│ }\n1062│\n1063│ #[test]\n1064│ fn test_modifier_uncertain() {\n1065│ let input = r#\"Philosophy\n1066│ .questions\n1067│ - free-will? @philosophy\"#;\n1068│\n1069│ let result = validate(input);\n1070│ assert!(result.is_valid(), \"Expected valid: {:?}\", result.errors);\n1071│\n1072│ if let Some(line) = result.lines.iter().find(|l| matches!(l.line_type, LineType::Claim(_))) {\n1073│ if let LineType::Claim(claim) = &line.line_type {\n1074│ let m = claim.modifiers.iter().find(|m| m.symbol == '?');\n1075│ assert!(m.is_some(), \"Expected ? modifier\");\n1076│ assert_eq!(m.unwrap().attached_to, \"free-will\");\n1077│ }\n1078│ }\n1079│ }\n1080│\n1081│ #[test]\n1082│ fn test_modifier_notable() {\n1083│ let input = r#\"Change\n1084│ .types\n1085│ - paradigm-shift* | in progress\"#;\n1086│\n1087│ let result = validate(input);\n1088│ assert!(result.is_valid(), \"Expected valid: {:?}\", result.errors);\n1089│\n1090│ if let Some(line) = result.lines.iter().find(|l| matches!(l.line_type, LineType::Claim(_))) {\n1091│ if let LineType::Claim(claim) = &line.line_type {\n1092│ let m = claim.modifiers.iter().find(|m| m.symbol == '*');\n1093│ assert!(m.is_some(), \"Expected * modifier\");\n1094│ }\n1095│ }\n1096│ }\n1097│\n1098│ #[test]\n1099│ fn test_standalone_modifier_warning() {\n1100│ // Modifier at start of claim (no preceding term) should warn\n1101│ let input = r#\"Power\n1102│ .core\n1103│ - ^ something\"#;\n1104│\n1105│ let result = validate(input);\n1106│ // Should be valid but with warning\n1107│ assert!(result.is_valid());\n1108│ assert!(result.has_warnings());\n1109│ assert!(result.warnings.iter().any(|e| matches!(e, ValidationError::StandaloneModifier { .. })));\n1110│ }\n1111│\n1112│ #[test]\n1113│ fn test_space_separated_modifier_valid() {\n1114│ // Space-separated modifier following a term is valid (no warning)\n1115│ let input = r#\"Power\n1116│ .core\n1117│ - something ^\"#;\n1118│\n1119│ let result = validate(input);\n1120│ assert!(result.is_valid());\n1121│ // This should NOT produce a warning because ^ follows \"something\"\n1122│ assert!(!result.has_warnings(), \"Expected no warnings: {:?}\", result.warnings);\n1123│ }\n1124│\n1125│ // ==================== Evolution marker tests ====================\n1126│\n1127│ #[test]\n1128│ fn test_evolution_marker() {\n1129│ let input = r#\"Human-nature\n1130│ .cognition\n1131│ - adaptive, context-dependent [<= inherently good]\"#;\n1132│\n1133│ let result = validate(input);\n1134│ assert!(result.is_valid(), \"Expected valid: {:?}\", result.errors);\n1135│\n1136│ if let Some(line) = result.lines.iter().find(|l| matches!(l.line_type, LineType::Claim(_))) {\n1137│ if let LineType::Claim(claim) = &line.line_type {\n1138│ assert!(claim.evolution.is_some(), \"Expected evolution marker\");\n1139│ assert_eq!(claim.evolution.as_ref().unwrap().prior_belief, \"inherently good\");\n1140│ }\n1141│ }\n1142│ }\n1143│\n1144│ #[test]\n1145│ fn test_evolution_marker_rationalizes() {\n1146│ let input = r#\"Human-nature\n1147│ .cognition\n1148│ - rationalizes post-hoc [<= rational actor]\"#;\n1149│\n1150│ let result = validate(input);\n1151│ assert!(result.is_valid(), \"Expected valid: {:?}\", result.errors);\n1152│\n1153│ if let Some(line) = result.lines.iter().find(|l| matches!(l.line_type, LineType::Claim(_))) {\n1154│ if let LineType::Claim(claim) = &line.line_type {\n1155│ assert!(claim.evolution.is_some());\n1156│ assert_eq!(claim.evolution.as_ref().unwrap().prior_belief, \"rational actor\");\n1157│ }\n1158│ }\n1159│ }\n1160│\n1161│ #[test]\n1162│ fn test_unclosed_evolution_marker() {\n1163│ let input = r#\"Human-nature\n1164│ .cognition\n1165│ - adaptive [<= inherently good\"#;\n1166│\n1167│ let result = validate(input);\n1168│ assert!(!result.is_valid());\n1169│ assert!(result.errors.iter().any(|e| matches!(e, ValidationError::UnclosedEvolutionMarker { .. })));\n1170│ }\n1171│\n1172│ #[test]\n1173│ fn test_empty_evolution_marker() {\n1174│ let input = r#\"Human-nature\n1175│ .cognition\n1176│ - adaptive [<= ]\"#;\n1177│\n1178│ let result = validate(input);\n1179│ assert!(!result.is_valid());\n1180│ assert!(result.errors.iter().any(|e| matches!(e, ValidationError::EmptyEvolutionMarker { .. })));\n1181│ }\n1182│\n1183│ // ==================== Full document tests ====================\n1184│\n1185│ #[test]\n1186│ fn test_expanded_document() {\n1187│ let input = r#\"Power\n1188│ .nature\n1189│ - corrupts | unchecked !\n1190│ - reveals character => self-knowledge\n1191│ - concentration^ => abuse^ @historical-pattern\n1192│ .institutional\n1193│ - self-preserving\n1194│ - accountability <> trust &Trust.institutional\n1195│ - diffusion => dilution-of-responsibility\n1196│\n1197│Trust\n1198│ .formation\n1199│ - slow\n1200│ - requires consistency | over time\n1201│ - contextual @personal-experience\n1202│ .erosion\n1203│ - fast !\n1204│ - single violation => collapse?\n1205│ - asymmetric vs formation &Trust.formation\n1206│ .institutional\n1207│ - possible | high transparency\n1208│ - unlikely | low transparency\n1209│ - rational to withhold | unverifiable @game-theory\n1210│\n1211│Human-nature\n1212│ .social\n1213│ - conformist | formal groups\n1214│ - authentic | solitary\n1215│ - status-aware @evolutionary-psychology\n1216│ - coalition-forming\n1217│ .cognition\n1218│ - pattern-seeking\n1219│ - confirmation-biased @cognitive-science\n1220│ - narrative-constructing\n1221│ - rationalizes post-hoc [<= rational actor]\n1222│ .self-perception\n1223│ - overconfident | familiar domains\n1224│ - miscalibrated @Dunning-Kruger\n1225│ - self-deception => comfort &Human-nature.cognition\n1226│\n1227│Institutions\n1228│ .function\n1229│ - stabilize !\n1230│ - preserve knowledge\n1231│ - coordinate action @game-theory\n1232│ .dysfunction\n1233│ - ossify | over time\n1234│ - self-perpetuate // original purpose\n1235│ - capture-by-interests^ @public-choice-theory\"#;\n1236│\n1237│ let result = validate(input);\n1238│ assert!(result.is_valid(), \"Expected valid document: {:?}\", result.errors);\n1239│ }\n1240│}\n", - "background": false - }, - { - "type": "ReadFileBlock", - "call_id": "toolu_01CFwsViwsr5KeK4vKNHtmQV", - "tool_name": "mcp_read_file", - "params": { - "path": "agent/src/main.rs" - }, - "status": "complete", - "text": " 1│//! Worldview Agent CLI - A tool for adding facts to Worldview files using an AI agent\n 2│//!\n 3│//! This CLI accepts plain-text facts and uses an AI agent to properly format\n 4│//! and incorporate them into a Worldview format (.wvf) file.\n 5│\n 6│use anyhow::Result;\n 7│use clap::Parser;\n 8│use codey::{Agent, AgentRuntimeConfig, AgentStep, RequestMode, SimpleTool, ToolRegistry};\n 9│use serde_json::json;\n 10│use std::path::PathBuf;\n 11│use std::sync::Arc;\n 12│\n 13│/// The Worldview format specification (loaded from SPEC.md at compile time)\n 14│const SPEC: &str = include_str!(\"../../SPEC.md\");\n 15│\n 16│/// Task instructions for the agent\n 17│const TASK_INSTRUCTIONS: &str = r#\"\n 18│# Your Task\n 19│\n 20│When given a plain-text fact or statement:\n 21│1. First, read the current Worldview file to understand its structure and existing concepts\n 22│2. Determine if this fact belongs to an existing concept/facet or requires a new one\n 23│3. Format the fact as proper Worldview notation following the specification above\n 24│4. Use the edit_worldview tool to add or modify the appropriate line(s)\n 25│5. After editing, briefly confirm what you added\n 26│\n 27│## Critical: Encode Only What Is Stated\n 28│\n 29│The Worldview file stores information the user explicitly provides for later reference. You must:\n 30│\n 31│- **Only encode information explicitly stated** in the user's fact or statement\n 32│- **Never add supplementary knowledge** from your training data (e.g., if told \"gravity pulls objects toward Earth\", do NOT add acceleration values, formulas, or other physics facts you know)\n 33│- **Never add assumptions or inferences** beyond what was directly stated\n 34│- **Be token-efficient** — the model already has general knowledge; we only need to store what the user specifically wants to remember\n 35│\n 36│The purpose is to capture the user's specific framing and claims, not to build a comprehensive knowledge base. General facts already exist in model weights and don't need to be stored.\n 37│\n 38│## Critical: Reject Ephemeral Events\n 39│\n 40│The Worldview format is designed to store **durable beliefs, values, perspectives, and knowledge** — not transient events or one-time occurrences. You must:\n 41│\n 42│- **Reject ephemeral personal events** like \"I went to the park on Sunday\" or \"I had coffee this morning\"\n 43│- **Reject time-bound occurrences** that describe what happened rather than what the user believes or values\n 44│- **Accept beliefs about events** like \"parks are good for mental health\" or \"Sunday routines matter\"\n 45│\n 46│If given an ephemeral event, respond politely explaining that the Worldview format is for beliefs and perspectives, not personal diary entries, and do NOT modify the file.\n 47│\n 48│Remember the design principles: state over narrative, predictability allows omission, conflict tolerance, freeform vocabulary, and LLM-native density.\n 49│\"#;\n 50│\n 51│/// Build the complete system prompt from spec + task instructions\n 52│fn build_system_prompt() -> String {\n 53│ format!(\n 54│ \"You are a Worldview format agent. Your task is to take plain-text facts or statements and incorporate them into a Worldview file using the proper notation.\\n\\n\\\n 55│ Below is the complete Worldview format specification. Study it carefully before making any edits.\\n\\n\\\n 56│ ---\\n\\n\\\n 57│ {}\\n\\n\\\n 58│ ---\\n\\n\\\n 59│ {}\",\n 60│ SPEC, TASK_INSTRUCTIONS\n 61│ )\n 62│}\n 63│\n 64│/// CLI for adding facts to Worldview files using an AI agent\n 65│#[derive(Parser, Debug)]\n 66│#[command(name = \"worldview\")]\n 67│#[command(about = \"Add facts to Worldview files using an AI agent\")]\n 68│#[command(version)]\n 69│struct Cli {\n 70│ /// The fact or statement to add to the Worldview file\n 71│ #[arg(required = true)]\n 72│ fact: String,\n 73│\n 74│ /// Path to the Worldview file to modify\n 75│ #[arg(short, long, default_value = \"worldview.wvf\")]\n 76│ file: PathBuf,\n 77│\n 78│ /// Model to use (claude-sonnet-4-20250514 or claude-opus-4-5-20251101)\n 79│ #[arg(short, long, default_value = \"claude-sonnet-4-20250514\")]\n 80│ model: String,\n 81│\n 82│ /// Enable verbose output\n 83│ #[arg(short, long)]\n 84│ verbose: bool,\n 85│}\n 86│\n 87│/// Create the read_worldview tool definition\n 88│fn create_read_tool() -> SimpleTool {\n 89│ SimpleTool::new(\n 90│ \"read_worldview\",\n 91│ \"Read the current contents of the Worldview file. Returns the file contents with line numbers prefixed (e.g., ' 1│content'). Use read_worldview first before editing to see current state.\",\n 92│ json!({\n 93│ \"type\": \"object\",\n 94│ \"properties\": {},\n 95│ \"required\": []\n 96│ }),\n 97│ )\n 98│}\n 99│\n 100│/// Create the edit_worldview tool definition\n 101│fn create_edit_tool() -> SimpleTool {\n 102│ SimpleTool::new(\n 103│ \"edit_worldview\",\n 104│ r#\"Apply search/replace edits to the Worldview file. Each edit specifies an old_string to find and a new_string to replace it with.\n 105│\n 106│Rules:\n 107│- Each old_string must match exactly (including whitespace/indentation)\n 108│- Each old_string must appear exactly once in the file (include more context if ambiguous)\n 109│- To insert new content, use old_string to match an existing line and include it plus your new lines in new_string\n 110│- To delete content, use an empty new_string\n 111│- Multiple edits are applied sequentially\n 112│\n 113│The tool validates the result against Worldview syntax rules before writing.\"#,\n 114│ json!({\n 115│ \"type\": \"object\",\n 116│ \"properties\": {\n 117│ \"edits\": {\n 118│ \"type\": \"array\",\n 119│ \"description\": \"List of search/replace operations to apply sequentially\",\n 120│ \"items\": {\n 121│ \"type\": \"object\",\n 122│ \"properties\": {\n 123│ \"old_string\": {\n 124│ \"type\": \"string\",\n 125│ \"description\": \"Exact string to find (must be unique in file). Include full lines with proper indentation.\"\n 126│ },\n 127│ \"new_string\": {\n 128│ \"type\": \"string\",\n 129│ \"description\": \"String to replace it with. Use empty string to delete.\"\n 130│ }\n 131│ },\n 132│ \"required\": [\"old_string\", \"new_string\"]\n 133│ }\n 134│ }\n 135│ },\n 136│ \"required\": [\"edits\"]\n 137│ }),\n 138│ )\n 139│}\n 140│\n 141│/// Handle the read_worldview tool call\n 142│fn handle_read_worldview(file_path: &PathBuf) -> String {\n 143│ if !file_path.exists() {\n 144│ return \"File does not exist yet. Use edit_worldview with edits to create it.\".to_string();\n 145│ }\n 146│\n 147│ match std::fs::read_to_string(file_path) {\n 148│ Ok(content) => {\n 149│ // Return with line numbers in codey format\n 150│ content\n 151│ .lines()\n 152│ .enumerate()\n 153│ .map(|(i, line)| format!(\"{:4}│{}\", i + 1, line))\n 154│ .collect::>()\n 155│ .join(\"\\n\")\n 156│ }\n 157│ Err(e) => format!(\"Error reading file: {}\", e),\n 158│ }\n 159│}\n 160│\n 161│/// Handle the edit_worldview tool call\n 162│fn handle_edit_worldview(file_path: &PathBuf, params: &serde_json::Value) -> String {\n 163│ // Parse edits array\n 164│ let edits = match params.get(\"edits\").and_then(|v| v.as_array()) {\n 165│ Some(arr) => arr,\n 166│ None => return \"Error: 'edits' array is required\".to_string(),\n 167│ };\n 168│\n 169│ if edits.is_empty() {\n 170│ return \"Error: 'edits' array cannot be empty\".to_string();\n 171│ }\n 172│\n 173│ // Read current file content (or start empty for new files)\n 174│ let mut content = if file_path.exists() {\n 175│ match std::fs::read_to_string(file_path) {\n 176│ Ok(c) => c,\n 177│ Err(e) => return format!(\"Error reading file: {}\", e),\n 178│ }\n 179│ } else {\n 180│ String::new()\n 181│ };\n 182│\n 183│ // Validate and apply each edit\n 184│ for (i, edit) in edits.iter().enumerate() {\n 185│ let old_string = match edit.get(\"old_string\").and_then(|v| v.as_str()) {\n 186│ Some(s) => s,\n 187│ None => return format!(\"Edit {}: missing 'old_string'\", i + 1),\n 188│ };\n 189│ let new_string = match edit.get(\"new_string\").and_then(|v| v.as_str()) {\n 190│ Some(s) => s,\n 191│ None => return format!(\"Edit {}: missing 'new_string'\", i + 1),\n 192│ };\n 193│\n 194│ // For new files, old_string should be empty to append\n 195│ if content.is_empty() {\n 196│ if !old_string.is_empty() {\n 197│ return format!(\n 198│ \"Edit {}: file is empty, old_string must be empty to create new content\",\n 199│ i + 1\n 200│ );\n 201│ }\n 202│ content = new_string.to_string();\n 203│ continue;\n 204│ }\n 205│\n 206│ // Check that old_string exists and is unique\n 207│ let count = content.matches(old_string).count();\n 208│ match count {\n 209│ 0 => {\n 210│ return format!(\n 211│ \"Edit {}: old_string not found in file. \\\n 212│ Make sure the string matches exactly, including whitespace and indentation.\",\n 213│ i + 1\n 214│ );\n 215│ }\n 216│ 1 => {} // good\n 217│ n => {\n 218│ return format!(\n 219│ \"Edit {}: old_string found {} times (must be unique). \\\n 220│ Include more surrounding context to make the match unique.\",\n 221│ i + 1,\n 222│ n\n 223│ );\n 224│ }\n 225│ }\n 226│\n 227│ // Apply the replacement\n 228│ content = content.replacen(old_string, new_string, 1);\n 229│ }\n 230│\n 231│ // Ensure file ends with newline\n 232│ if !content.is_empty() && !content.ends_with('\\n') {\n 233│ content.push('\\n');\n 234│ }\n 235│\n 236│ // Validate the new content before writing\n 237│ let validation = worldview_validator::validate(&content);\n 238│\n 239│ if !validation.is_valid() {\n 240│ let errors: Vec = validation.errors.iter().map(|e| e.to_string()).collect();\n 241│ return format!(\n 242│ \"Validation failed - file not modified:\\n{}\",\n 243│ errors.join(\"\\n\")\n 244│ );\n 245│ }\n 246│\n 247│ // Write the file\n 248│ if let Err(e) = std::fs::write(file_path, &content) {\n 249│ return format!(\"Error writing file: {}\", e);\n 250│ }\n 251│\n 252│ // Return success with edit count and any warnings\n 253│ let edit_count = edits.len();\n 254│ let base_msg = format!(\n 255│ \"Successfully applied {} edit{}.\",\n 256│ edit_count,\n 257│ if edit_count == 1 { \"\" } else { \"s\" }\n 258│ );\n 259│\n 260│ if validation.has_warnings() {\n 261│ let warnings: Vec = validation.warnings.iter().map(|w| w.to_string()).collect();\n 262│ format!(\"{} Warnings:\\n{}\", base_msg, warnings.join(\"\\n\"))\n 263│ } else {\n 264│ format!(\"{} File validated.\", base_msg)\n 265│ }\n 266│}\n 267│\n 268│/// Handle a tool call from the agent\n 269│fn handle_tool_call(file_path: &PathBuf, tool_name: &str, params: &serde_json::Value) -> String {\n 270│ match tool_name {\n 271│ \"read_worldview\" => handle_read_worldview(file_path),\n 272│ \"edit_worldview\" => handle_edit_worldview(file_path, params),\n 273│ _ => format!(\"Unknown tool: {}\", tool_name),\n 274│ }\n 275│}\n 276│\n 277│#[tokio::main]\n 278│async fn main() -> Result<()> {\n 279│ let cli = Cli::parse();\n 280│ let start_time = std::time::Instant::now();\n 281│\n 282│ // Check for API key\n 283│ if std::env::var(\"ANTHROPIC_API_KEY\").is_err() {\n 284│ eprintln!(\"Error: ANTHROPIC_API_KEY environment variable not set\");\n 285│ std::process::exit(1);\n 286│ }\n 287│\n 288│ // Resolve the file path\n 289│ let file_path = if cli.file.is_absolute() {\n 290│ cli.file.clone()\n 291│ } else {\n 292│ std::env::current_dir()?.join(&cli.file)\n 293│ };\n 294│\n 295│ if cli.verbose {\n 296│ eprintln!(\"[config] Worldview file: {:?}\", file_path);\n 297│ eprintln!(\"[config] Model: {}\", cli.model);\n 298│ eprintln!(\"[config] Fact: {}\", cli.fact);\n 299│ eprintln!(\"[start] Beginning agent execution...\");\n 300│ }\n 301│\n 302│ // Create tool registry with our custom tools\n 303│ let mut registry = ToolRegistry::empty();\n 304│ registry.register(Arc::new(create_read_tool()));\n 305│ registry.register(Arc::new(create_edit_tool()));\n 306│\n 307│ // Configure the agent\n 308│ let config = AgentRuntimeConfig {\n 309│ model: cli.model.clone(),\n 310│ max_tokens: 4096,\n 311│ thinking_budget: 1024, // Minimum required\n 312│ max_retries: 3,\n 313│ compaction_thinking_budget: 2000,\n 314│ };\n 315│\n 316│ // Create the agent with the dynamically built system prompt\n 317│ let system_prompt = build_system_prompt();\n 318│ let mut agent = Agent::new(\n 319│ config,\n 320│ &system_prompt,\n 321│ None, // Use ANTHROPIC_API_KEY env var\n 322│ registry,\n 323│ );\n 324│\n 325│ // Format the user message\n 326│ let user_message = format!(\n 327│ \"Please add this fact to the Worldview file at {:?}:\\n\\n{}\",\n 328│ file_path, cli.fact\n 329│ );\n 330│\n 331│ // Send the request\n 332│ agent.send_request(&user_message, RequestMode::Normal);\n 333│\n 334│ // Process the agent loop\n 335│ let mut tool_call_count = 0;\n 336│ let mut thinking_started = false;\n 337│\n 338│ while let Some(step) = agent.next().await {\n 339│ match step {\n 340│ AgentStep::TextDelta(text) => {\n 341│ if cli.verbose {\n 342│ print!(\"{}\", text);\n 343│ }\n 344│ }\n 345│ AgentStep::ThinkingDelta(thinking) => {\n 346│ if cli.verbose {\n 347│ if !thinking_started {\n 348│ thinking_started = true;\n 349│ eprint!(\"\\n[thinking] \");\n 350│ }\n 351│ eprint!(\"{}\", thinking);\n 352│ }\n 353│ }\n 354│ AgentStep::CompactionDelta(_) => {\n 355│ // Not used in our simple case\n 356│ }\n 357│ AgentStep::ToolRequest(tool_calls) => {\n 358│ if cli.verbose && thinking_started {\n 359│ eprintln!(); // End thinking block\n 360│ thinking_started = false;\n 361│ }\n 362│\n 363│ for call in tool_calls {\n 364│ tool_call_count += 1;\n 365│ let tool_start = std::time::Instant::now();\n 366│\n 367│ if cli.verbose {\n 368│ // Format params nicely for readability\n 369│ let params_str = if call.params.is_object() {\n 370│ serde_json::to_string_pretty(&call.params).unwrap_or_else(|_| format!(\"{:?}\", call.params))\n 371│ } else {\n 372│ format!(\"{:?}\", call.params)\n 373│ };\n 374│ eprintln!(\"\\n[tool:{}] {}\", tool_call_count, call.name);\n 375│ eprintln!(\"[params] {}\", params_str);\n 376│ }\n 377│\n 378│ let result = handle_tool_call(&file_path, &call.name, &call.params);\n 379│\n 380│ if cli.verbose {\n 381│ let tool_elapsed = tool_start.elapsed();\n 382│ eprintln!(\"[result:{}ms] {}\", tool_elapsed.as_millis(), result);\n 383│ }\n 384│\n 385│ agent.submit_tool_result(&call.call_id, result);\n 386│ }\n 387│ }\n 388│ AgentStep::Retrying { attempt, error } => {\n 389│ if cli.verbose {\n 390│ eprintln!(\"[retry] Attempt {} after error: {}\", attempt, error);\n 391│ }\n 392│ }\n 393│ AgentStep::Finished { usage } => {\n 394│ let total_elapsed = start_time.elapsed();\n 395│ if cli.verbose {\n 396│ eprintln!(\"\\n[done] Output: {}, Context: {}\",\n 397│ usage.output_tokens, usage.context_tokens);\n 398│ eprintln!(\"[timing] Total: {}ms, Tool calls: {}\",\n 399│ total_elapsed.as_millis(), tool_call_count);\n 400│ }\n 401│ break;\n 402│ }\n 403│ AgentStep::Error(e) => {\n 404│ let total_elapsed = start_time.elapsed();\n 405│ if cli.verbose {\n 406│ eprintln!(\"[error:{}ms] {}\", total_elapsed.as_millis(), e);\n 407│ }\n 408│ eprintln!(\"Error: {}\", e);\n 409│ std::process::exit(1);\n 410│ }\n 411│ }\n 412│ }\n 413│\n 414│ // Exit 0 for success (including correct rejections)\n 415│ Ok(())\n 416│}\n", - "background": false - }, - { - "type": "ReadFileBlock", - "call_id": "toolu_01ELu7FSAb34X4EsoQZ3QsJH", - "tool_name": "mcp_read_file", - "params": { - "path": "spec/tokens.yaml" - }, - "status": "complete", - "text": " 1│# Worldview Format Token Definitions\n 2│#\n 3│# This is the canonical source for all operators, modifiers, and syntax elements.\n 4│# Generated files (docs, validator constants, system prompts) derive from this.\n 5│\n 6│version: \"0.1\"\n 7│\n 8│# Document structure\n 9│structure:\n 10│ concept:\n 11│ indent: 0\n 12│ prefix: null\n 13│ description: \"A subject of belief—a noun in the worldview\"\n 14│ examples: [\"Power\", \"Trust\", \"Human-nature\"]\n 15│\n 16│ facet:\n 17│ indent: 2\n 18│ prefix: \".\"\n 19│ description: \"An aspect or dimension of a concept\"\n 20│ examples: [\".nature\", \".formation\", \".erosion\"]\n 21│\n 22│ claim:\n 23│ indent: 4\n 24│ prefix: \"-\"\n 25│ description: \"An assertion about a facet—what is believed to be true\"\n 26│ examples: [\"- corrupts | unchecked\", \"- slow\"]\n 27│\n 28│# Inline elements (positional grammar)\n 29│inline_elements:\n 30│ - symbol: \"|\"\n 31│ name: \"condition\"\n 32│ meaning: \"when/if applies\"\n 33│ position: \"after claim\"\n 34│ example: \"- corrupts | unchecked\"\n 35│\n 36│ - symbol: \"@\"\n 37│ name: \"source\"\n 38│ meaning: \"basis for belief\"\n 39│ position: \"after claim/conditions\"\n 40│ example: \"@historical-pattern\"\n 41│\n 42│ - symbol: \"&\"\n 43│ name: \"reference\"\n 44│ meaning: \"links to other concept.facet\"\n 45│ position: \"after claim\"\n 46│ format: \"&Concept.facet\"\n 47│ example: \"&Trust.formation\"\n 48│\n 49│# Brief forms - compact relationship operators\n 50│brief_forms:\n 51│ - symbol: \"=>\"\n 52│ meaning: \"causes, leads to\"\n 53│ example: \"power => corruption\"\n 54│\n 55│ - symbol: \"<=\"\n 56│ meaning: \"caused by, results from\"\n 57│ example: \"trust <= consistency\"\n 58│\n 59│ - symbol: \"<>\"\n 60│ meaning: \"mutual, bidirectional\"\n 61│ example: \"accountability <> trust\"\n 62│\n 63│ - symbol: \"><\"\n 64│ meaning: \"tension, conflicts with\"\n 65│ example: \"efficiency >< thoroughness\"\n 66│\n 67│ - symbol: \"~\"\n 68│ meaning: \"similar to, resembles\"\n 69│ example: \"authority ~ influence\"\n 70│\n 71│ - symbol: \"=\"\n 72│ meaning: \"equivalent to, means\"\n 73│ example: \"formal = official\"\n 74│\n 75│ - symbol: \"vs\"\n 76│ meaning: \"in contrast to\"\n 77│ example: \"asymmetric vs formation\"\n 78│\n 79│ - symbol: \"//\"\n 80│ meaning: \"regardless of\"\n 81│ example: \"self-perpetuate // original purpose\"\n 82│\n 83│# Modifiers - suffix markers that inflect meaning\n 84│modifiers:\n 85│ - symbol: \"^\"\n 86│ meaning: \"increasing, trending up\"\n 87│ example: \"concentration^\"\n 88│\n 89│ - symbol: \"v\"\n 90│ meaning: \"decreasing, trending down\"\n 91│ example: \"trust v\"\n 92│\n 93│ - symbol: \"!\"\n 94│ meaning: \"strong, emphatic, high confidence\"\n 95│ example: \"fast !\"\n 96│\n 97│ - symbol: \"?\"\n 98│ meaning: \"uncertain, contested, tentative\"\n 99│ example: \"free-will?\"\n 100│\n 101│ - symbol: \"*\"\n 102│ meaning: \"notable, important, flagged\"\n 103│ example: \"paradigm-shift*\"\n 104│\n 105│# Evolution markers\n 106│evolution:\n 107│ supersession:\n 108│ syntax: \"[<= prior belief]\"\n 109│ meaning: \"current belief supersedes prior belief\"\n 110│ example: \"- adaptive [<= inherently good]\"\n 111│\n 112│# Positional grammar order\n 113│claim_order:\n 114│ - element: \"claim_text\"\n 115│ required: true\n 116│ - element: \"conditions\"\n 117│ required: false\n 118│ prefix: \"|\"\n 119│ multiple: true\n 120│ - element: \"sources\"\n 121│ required: false\n 122│ prefix: \"@\"\n 123│ multiple: true\n 124│ - element: \"references\"\n 125│ required: false\n 126│ prefix: \"&\"\n 127│ multiple: true\n", - "background": false - }, - { - "type": "ThinkingBlock", - "text": "Now I have a solid understanding of the project. Let me also look at the Python evals and the build system to get the full picture, then I can provide a quality assessment.", - "status": "complete" - }, - { - "type": "TextBlock", - "text": "Excellent! Now let me check the Python evals and the build system:", - "status": "complete" - }, - { - "type": "ReadFileBlock", - "call_id": "toolu_01Ea5rQQBLNVgxU9oPf1hg23", - "tool_name": "mcp_read_file", - "params": { - "path": "evals/cli.py" - }, - "status": "complete", - "text": " 1│#!/usr/bin/env python3\n 2│\"\"\"\n 3│Worldview Evaluation CLI\n 4│\n 5│Run LLM evaluations against Worldview-encoded beliefs.\n 6│\n 7│Usage:\n 8│ python -m evals.cli --help\n 9│ python -m evals.cli run --models claude-sonnet gpt-4o\n 10│ python -m evals.cli run --difficulty extreme\n 11│ python -m evals.cli write-eval --models claude-sonnet claude-haiku\n 12│ python -m evals.cli write-eval --complexity simple\n 13│ python -m evals.cli list-models\n 14│ python -m evals.cli list-cases\n 15│\"\"\"\n 16│\n 17│import argparse\n 18│import sys\n 19│from pathlib import Path\n 20│\n 21│\n 22│def cmd_run(args):\n 23│ \"\"\"Run evaluations.\"\"\"\n 24│ from .common.config import ALL_MODELS, DEFAULT_MODELS, get_model, MODEL_REGISTRY\n 25│ from .read_eval.runner import EvalRunner, generate_report, generate_json_results\n 26│ from .read_eval.test_cases import ALL_TEST_CASES, Difficulty, get_cases_by_difficulty\n 27│\n 28│ # Determine models to use\n 29│ if args.models:\n 30│ models = []\n 31│ for name in args.models:\n 32│ model = get_model(name)\n 33│ if model:\n 34│ models.append(model)\n 35│ else:\n 36│ print(f\"Warning: Unknown model '{name}', skipping\")\n 37│ if not models:\n 38│ print(\"Error: No valid models specified\")\n 39│ sys.exit(1)\n 40│ elif args.all_models:\n 41│ models = ALL_MODELS\n 42│ else:\n 43│ models = DEFAULT_MODELS\n 44│\n 45│ # Determine test cases\n 46│ if args.difficulty:\n 47│ try:\n 48│ difficulty = Difficulty(args.difficulty)\n 49│ test_cases = get_cases_by_difficulty(difficulty)\n 50│ except ValueError:\n 51│ print(f\"Error: Unknown difficulty '{args.difficulty}'\")\n 52│ print(f\"Valid options: {[d.value for d in Difficulty]}\")\n 53│ sys.exit(1)\n 54│ elif args.cases:\n 55│ from .read_eval.test_cases import get_case_by_id\n 56│ test_cases = []\n 57│ for case_id in args.cases:\n 58│ tc = get_case_by_id(case_id)\n 59│ if tc:\n 60│ test_cases.append(tc)\n 61│ else:\n 62│ print(f\"Warning: Unknown test case '{case_id}', skipping\")\n 63│ if not test_cases:\n 64│ print(\"Error: No valid test cases specified\")\n 65│ sys.exit(1)\n 66│ else:\n 67│ test_cases = ALL_TEST_CASES\n 68│\n 69│ print(f\"Running {len(test_cases)} test cases against {len(models)} models\")\n 70│ print(f\"Models: {[m.display_name for m in models]}\")\n 71│ print()\n 72│\n 73│ # Create runner\n 74│ runner = EvalRunner(\n 75│ models=models,\n 76│ use_cli_tool=args.use_cli,\n 77│ worldview_cli_path=args.worldview_cli,\n 78│ verbose=args.verbose,\n 79│ )\n 80│\n 81│ # Run evaluations\n 82│ results = runner.run_all(test_cases=test_cases)\n 83│\n 84│ # Generate outputs\n 85│ if args.output:\n 86│ output_path = Path(args.output)\n 87│\n 88│ # Generate report\n 89│ report_path = output_path / \"report.md\"\n 90│ report = generate_report(results, str(report_path))\n 91│ print(f\"\\nReport written to: {report_path}\")\n 92│\n 93│ # Generate JSON\n 94│ json_path = output_path / \"results.json\"\n 95│ generate_json_results(results, str(json_path))\n 96│ print(f\"JSON results written to: {json_path}\")\n 97│ else:\n 98│ # Print report to stdout\n 99│ print(\"\\n\" + \"=\" * 60)\n 100│ report = generate_report(results)\n 101│ print(report)\n 102│\n 103│\n 104│def cmd_write_eval(args):\n 105│ \"\"\"Run write evaluations (embedding model benchmark).\"\"\"\n 106│ from .write_eval.runner import (\n 107│ WriteEvalRunner,\n 108│ WRITE_MODELS,\n 109│ DEFAULT_WRITE_MODELS,\n 110│ get_write_model,\n 111│ generate_write_report,\n 112│ generate_write_json,\n 113│ )\n 114│ from .write_eval.test_cases import (\n 115│ ALL_WRITE_CASES,\n 116│ Complexity,\n 117│ get_cases_by_complexity,\n 118│ get_case_by_id,\n 119│ )\n 120│\n 121│ # Determine models to use\n 122│ if args.models:\n 123│ model_names = args.models\n 124│ elif args.all_models:\n 125│ model_names = [m[\"name\"] for m in WRITE_MODELS]\n 126│ else:\n 127│ model_names = DEFAULT_WRITE_MODELS\n 128│\n 129│ # Validate models\n 130│ valid_models = []\n 131│ for name in model_names:\n 132│ if get_write_model(name):\n 133│ valid_models.append(name)\n 134│ else:\n 135│ print(f\"Warning: Unknown model '{name}', skipping\")\n 136│\n 137│ if not valid_models:\n 138│ print(\"Error: No valid models specified\")\n 139│ print(f\"Available models: {[m['name'] for m in WRITE_MODELS]}\")\n 140│ sys.exit(1)\n 141│\n 142│ # Determine test cases\n 143│ if args.complexity:\n 144│ try:\n 145│ complexity = Complexity(args.complexity)\n 146│ test_cases = get_cases_by_complexity(complexity)\n 147│ except ValueError:\n 148│ print(f\"Error: Unknown complexity '{args.complexity}'\")\n 149│ print(f\"Valid options: {[c.value for c in Complexity]}\")\n 150│ sys.exit(1)\n 151│ elif args.cases:\n 152│ test_cases = []\n 153│ for case_id in args.cases:\n 154│ tc = get_case_by_id(case_id)\n 155│ if tc:\n 156│ test_cases.append(tc)\n 157│ else:\n 158│ print(f\"Warning: Unknown test case '{case_id}', skipping\")\n 159│ if not test_cases:\n 160│ print(\"Error: No valid test cases specified\")\n 161│ sys.exit(1)\n 162│ else:\n 163│ test_cases = ALL_WRITE_CASES\n 164│\n 165│ print(f\"Running {len(test_cases)} write test cases against {len(valid_models)} models\")\n 166│ print(f\"Models: {valid_models}\")\n 167│ print()\n 168│\n 169│ # Create runner\n 170│ runner = WriteEvalRunner(\n 171│ models=valid_models,\n 172│ agent_cli_path=args.agent_cli,\n 173│ validator_path=args.validator,\n 174│ verbose=args.verbose,\n 175│ )\n 176│\n 177│ # Run evaluations\n 178│ results = runner.run_all(test_cases=test_cases)\n 179│\n 180│ # Generate outputs\n 181│ if args.output:\n 182│ output_path = Path(args.output)\n 183│ output_path.mkdir(parents=True, exist_ok=True)\n 184│\n 185│ # Generate report\n 186│ report_path = output_path / \"write_report.md\"\n 187│ report = generate_write_report(results, str(report_path))\n 188│ print(f\"\\nReport written to: {report_path}\")\n 189│\n 190│ # Generate JSON\n 191│ json_path = output_path / \"write_results.json\"\n 192│ generate_write_json(results, str(json_path))\n 193│ print(f\"JSON results written to: {json_path}\")\n 194│ else:\n 195│ # Print report to stdout\n 196│ print(\"\\n\" + \"=\" * 60)\n 197│ report = generate_write_report(results)\n 198│ print(report)\n 199│\n 200│\n 201│def cmd_list_models(args):\n 202│ \"\"\"List available models.\"\"\"\n 203│ from .common.config import ALL_MODELS, DEFAULT_MODELS\n 204│ from .write_eval.runner import WRITE_MODELS, DEFAULT_WRITE_MODELS\n 205│\n 206│ default_ids = {m.model_id for m in DEFAULT_MODELS}\n 207│\n 208│ print(\"Read Evaluation Models (for testing LLM response to Worldview context):\\n\")\n 209│ print(f\"{'Name':<20} {'Provider':<12} {'Model ID':<35} {'Default'}\")\n 210│ print(\"-\" * 80)\n 211│\n 212│ for model in ALL_MODELS:\n 213│ default = \"Yes\" if model.model_id in default_ids else \"\"\n 214│ print(f\"{model.display_name:<20} {model.provider.value:<12} {model.model_id:<35} {default}\")\n 215│\n 216│ print(\"\\n\")\n 217│ print(\"Write Evaluation Models (for testing Worldview document generation):\\n\")\n 218│ print(f\"{'Name':<20} {'Model ID':<40} {'Default'}\")\n 219│ print(\"-\" * 70)\n 220│\n 221│ for model in WRITE_MODELS:\n 222│ default = \"Yes\" if model[\"name\"] in DEFAULT_WRITE_MODELS else \"\"\n 223│ print(f\"{model['display_name']:<20} {model['model_id']:<40} {default}\")\n 224│\n 225│\n 226│def cmd_list_cases(args):\n 227│ \"\"\"List available test cases.\"\"\"\n 228│ from .read_eval.test_cases import ALL_TEST_CASES, Difficulty\n 229│ from .write_eval.test_cases import ALL_WRITE_CASES, Complexity\n 230│\n 231│ print(\"Read Evaluation Test Cases (testing LLM response to Worldview context):\\n\")\n 232│\n 233│ for difficulty in Difficulty:\n 234│ cases = [tc for tc in ALL_TEST_CASES if tc.difficulty == difficulty]\n 235│ print(f\"\\n{difficulty.value.upper()} ({len(cases)} cases):\")\n 236│ print(\"-\" * 40)\n 237│ for tc in cases:\n 238│ print(f\" {tc.id:<30} {tc.name}\")\n 239│\n 240│ print(\"\\n\")\n 241│ print(\"=\" * 60)\n 242│ print(\"\\nWrite Evaluation Test Cases (testing document generation):\\n\")\n 243│\n 244│ for complexity in Complexity:\n 245│ cases = [tc for tc in ALL_WRITE_CASES if tc.complexity == complexity]\n 246│ print(f\"\\n{complexity.value.upper()} ({len(cases)} cases):\")\n 247│ print(\"-\" * 40)\n 248│ for tc in cases:\n 249│ print(f\" {tc.id:<30} {tc.name}\")\n 250│\n 251│\n 252│def main():\n 253│ parser = argparse.ArgumentParser(\n 254│ description=\"Worldview LLM Evaluation Framework\",\n 255│ formatter_class=argparse.RawDescriptionHelpFormatter,\n 256│ )\n 257│ subparsers = parser.add_subparsers(dest=\"command\", help=\"Commands\")\n 258│\n 259│ # Run command\n 260│ run_parser = subparsers.add_parser(\"run\", help=\"Run evaluations\")\n 261│ run_parser.add_argument(\n 262│ \"--models\",\n 263│ nargs=\"+\",\n 264│ help=\"Models to evaluate (e.g., claude-sonnet gpt-4o)\",\n 265│ )\n 266│ run_parser.add_argument(\n 267│ \"--all-models\",\n 268│ action=\"store_true\",\n 269│ help=\"Run against all available models\",\n 270│ )\n 271│ run_parser.add_argument(\n 272│ \"--difficulty\",\n 273│ choices=[\"baseline\", \"moderate\", \"extreme\"],\n 274│ help=\"Run only cases of specific difficulty\",\n 275│ )\n 276│ run_parser.add_argument(\n 277│ \"--cases\",\n 278│ nargs=\"+\",\n 279│ help=\"Specific test case IDs to run\",\n 280│ )\n 281│ run_parser.add_argument(\n 282│ \"--output\", \"-o\",\n 283│ help=\"Output directory for report and results\",\n 284│ )\n 285│ run_parser.add_argument(\n 286│ \"--use-cli\",\n 287│ action=\"store_true\",\n 288│ help=\"Use Worldview CLI tool to generate content (slower, full integration)\",\n 289│ )\n 290│ run_parser.add_argument(\n 291│ \"--worldview-cli\",\n 292│ default=\"worldview\",\n 293│ help=\"Path to Worldview CLI tool\",\n 294│ )\n 295│ run_parser.add_argument(\n 296│ \"--verbose\", \"-v\",\n 297│ action=\"store_true\",\n 298│ help=\"Print detailed output\",\n 299│ )\n 300│ run_parser.set_defaults(func=cmd_run)\n 301│\n 302│ # Write evaluation command\n 303│ write_parser = subparsers.add_parser(\n 304│ \"write-eval\",\n 305│ help=\"Run write evaluations (benchmark embedding models on document generation)\"\n 306│ )\n 307│ write_parser.add_argument(\n 308│ \"--models\",\n 309│ nargs=\"+\",\n 310│ help=\"Models to evaluate (e.g., claude-sonnet claude-haiku)\",\n 311│ )\n 312│ write_parser.add_argument(\n 313│ \"--all-models\",\n 314│ action=\"store_true\",\n 315│ help=\"Run against all available write models\",\n 316│ )\n 317│ write_parser.add_argument(\n 318│ \"--complexity\",\n 319│ choices=[\"simple\", \"moderate\", \"complex\"],\n 320│ help=\"Run only cases of specific complexity\",\n 321│ )\n 322│ write_parser.add_argument(\n 323│ \"--cases\",\n 324│ nargs=\"+\",\n 325│ help=\"Specific test case IDs to run\",\n 326│ )\n 327│ write_parser.add_argument(\n 328│ \"--output\", \"-o\",\n 329│ help=\"Output directory for report and results\",\n 330│ )\n 331│ write_parser.add_argument(\n 332│ \"--agent-cli\",\n 333│ default=\"worldview\",\n 334│ help=\"Path to Worldview agent CLI tool\",\n 335│ )\n 336│ write_parser.add_argument(\n 337│ \"--validator\",\n 338│ help=\"Path to Worldview validator binary (optional)\",\n 339│ )\n 340│ write_parser.add_argument(\n 341│ \"--verbose\", \"-v\",\n 342│ action=\"store_true\",\n 343│ help=\"Print detailed output including agent interactions\",\n 344│ )\n 345│ write_parser.set_defaults(func=cmd_write_eval)\n 346│\n 347│ # List models command\n 348│ models_parser = subparsers.add_parser(\"list-models\", help=\"List available models\")\n 349│ models_parser.set_defaults(func=cmd_list_models)\n 350│\n 351│ # List cases command\n 352│ cases_parser = subparsers.add_parser(\"list-cases\", help=\"List available test cases\")\n 353│ cases_parser.set_defaults(func=cmd_list_cases)\n 354│\n 355│ args = parser.parse_args()\n 356│\n 357│ if not args.command:\n 358│ parser.print_help()\n 359│ sys.exit(1)\n 360│\n 361│ args.func(args)\n 362│\n 363│\n 364│if __name__ == \"__main__\":\n 365│ main()\n", - "background": false - }, - { - "type": "ReadFileBlock", - "call_id": "toolu_019ttfpsAorMeypABxtGkwyA", - "tool_name": "mcp_read_file", - "params": { - "path": "validator/build.rs" - }, - "status": "complete", - "text": " 1│//! Build script for worldview-validator\n 2│//! Generates token definitions from spec/tokens.yaml at compile time.\n 3│\n 4│use std::env;\n 5│use std::fs;\n 6│use std::path::Path;\n 7│\n 8│fn main() {\n 9│ let out_dir = env::var(\"OUT_DIR\").unwrap();\n 10│ let dest_path = Path::new(&out_dir).join(\"tokens.rs\");\n 11│\n 12│ // Read tokens.yaml from spec directory\n 13│ let manifest_dir = env::var(\"CARGO_MANIFEST_DIR\").unwrap();\n 14│ let tokens_path = Path::new(&manifest_dir)\n 15│ .parent()\n 16│ .unwrap()\n 17│ .join(\"spec/tokens.yaml\");\n 18│\n 19│ println!(\"cargo:rerun-if-changed={}\", tokens_path.display());\n 20│\n 21│ let tokens_yaml = fs::read_to_string(&tokens_path).expect(\"Failed to read spec/tokens.yaml\");\n 22│\n 23│ // Parse YAML manually (avoiding serde dependency in build script)\n 24│ let rust_code = generate_tokens_rs(&tokens_yaml);\n 25│\n 26│ fs::write(&dest_path, rust_code).unwrap();\n 27│}\n 28│\n 29│fn generate_tokens_rs(yaml: &str) -> String {\n 30│ let mut output = String::from(\"// Auto-generated from spec/tokens.yaml at compile time\\n\\n\");\n 31│\n 32│ // Parse brief_forms\n 33│ let mut brief_forms = Vec::new();\n 34│ let mut in_brief_forms = false;\n 35│ let mut current_symbol = String::new();\n 36│ let mut current_meaning = String::new();\n 37│\n 38│ for line in yaml.lines() {\n 39│ let trimmed = line.trim();\n 40│\n 41│ if trimmed == \"brief_forms:\" {\n 42│ in_brief_forms = true;\n 43│ continue;\n 44│ }\n 45│\n 46│ if in_brief_forms {\n 47│ if trimmed.starts_with(\"- symbol:\") {\n 48│ if !current_symbol.is_empty() {\n 49│ brief_forms.push((current_symbol.clone(), current_meaning.clone()));\n 50│ }\n 51│ current_symbol = trimmed\n 52│ .trim_start_matches(\"- symbol:\")\n 53│ .trim()\n 54│ .trim_matches('\"')\n 55│ .to_string();\n 56│ current_meaning.clear();\n 57│ } else if trimmed.starts_with(\"meaning:\") {\n 58│ current_meaning = trimmed\n 59│ .trim_start_matches(\"meaning:\")\n 60│ .trim()\n 61│ .trim_matches('\"')\n 62│ .to_string();\n 63│ } else if trimmed.starts_with(\"modifiers:\") {\n 64│ if !current_symbol.is_empty() {\n 65│ brief_forms.push((current_symbol.clone(), current_meaning.clone()));\n 66│ }\n 67│ break;\n 68│ }\n 69│ }\n 70│ }\n 71│\n 72│ // Generate BRIEF_FORMS\n 73│ output.push_str(\"/// Brief form operators defined in the Worldview spec\\n\");\n 74│ output.push_str(\"pub const BRIEF_FORMS: &[(&str, &str)] = &[\\n\");\n 75│ for (sym, meaning) in &brief_forms {\n 76│ output.push_str(&format!(\" (\\\"{}\\\", \\\"{}\\\"),\\n\", sym, meaning));\n 77│ }\n 78│ output.push_str(\"];\\n\\n\");\n 79│\n 80│ // Parse modifiers\n 81│ let mut modifiers = Vec::new();\n 82│ let mut in_modifiers = false;\n 83│ current_symbol.clear();\n 84│ current_meaning.clear();\n 85│\n 86│ for line in yaml.lines() {\n 87│ let trimmed = line.trim();\n 88│\n 89│ if trimmed == \"modifiers:\" {\n 90│ in_modifiers = true;\n 91│ continue;\n 92│ }\n 93│\n 94│ if in_modifiers {\n 95│ if trimmed.starts_with(\"- symbol:\") {\n 96│ if !current_symbol.is_empty() {\n 97│ modifiers.push((current_symbol.clone(), current_meaning.clone()));\n 98│ }\n 99│ current_symbol = trimmed\n 100│ .trim_start_matches(\"- symbol:\")\n 101│ .trim()\n 102│ .trim_matches('\"')\n 103│ .to_string();\n 104│ current_meaning.clear();\n 105│ } else if trimmed.starts_with(\"meaning:\") {\n 106│ current_meaning = trimmed\n 107│ .trim_start_matches(\"meaning:\")\n 108│ .trim()\n 109│ .trim_matches('\"')\n 110│ .to_string();\n 111│ } else if trimmed.starts_with(\"evolution:\") || trimmed.starts_with(\"claim_order:\") {\n 112│ if !current_symbol.is_empty() {\n 113│ modifiers.push((current_symbol.clone(), current_meaning.clone()));\n 114│ }\n 115│ break;\n 116│ }\n 117│ }\n 118│ }\n 119│\n 120│ // Generate MODIFIERS\n 121│ output.push_str(\"/// Modifier symbols defined in the Worldview spec\\n\");\n 122│ output.push_str(\"pub const MODIFIERS: &[(&str, &str)] = &[\\n\");\n 123│ for (sym, meaning) in &modifiers {\n 124│ output.push_str(&format!(\" (\\\"{}\\\", \\\"{}\\\"),\\n\", sym, meaning));\n 125│ }\n 126│ output.push_str(\"];\\n\");\n 127│\n 128│ output\n 129│}\n", - "background": false - }, - { - "type": "ShellBlock", - "call_id": "toolu_01KMBM6sic3gobGvHDB5XKRv", - "tool_name": "mcp_shell", - "params": { - "command": "ls -la evals/" - }, - "status": "complete", - "text": "total 48\ndrwxr-xr-x@ 9 tcdent staff 288 Jan 21 10:11 .\ndrwxr-xr-x@ 14 tcdent staff 448 Jan 21 15:02 ..\n-rw-r--r--@ 1 tcdent staff 360 Jan 21 10:11 __init__.py\n-rw-r--r--@ 1 tcdent staff 101 Jan 21 10:11 __main__.py\n-rw-r--r--@ 1 tcdent staff 11372 Jan 21 10:11 cli.py\ndrwxr-xr-x@ 5 tcdent staff 160 Jan 21 10:11 common\ndrwxr-xr-x@ 7 tcdent staff 224 Jan 21 10:11 read_eval\n-rw-r--r--@ 1 tcdent staff 227 Jan 21 10:11 requirements.txt\ndrwxr-xr-x@ 6 tcdent staff 192 Jan 21 10:11 write_eval\n", - "background": false - }, - { - "type": "ThinkingBlock", - "text": "Let me get a bit more context on the Python eval structure and check if there are any obvious issues. Let me also try building the Rust code to see if it compiles.", - "status": "complete" - }, - { - "type": "TextBlock", - "text": "Let me check if this builds and run a quick test, and also look at a few more key files:", - "status": "complete" - }, - { - "type": "ShellBlock", - "call_id": "toolu_015d31dZG8cEPzF9LrepZ3rE", - "tool_name": "mcp_shell", - "params": { - "command": "cd validator && cargo check 2>&1 | head -30" - }, - "status": "complete", - "text": " Updating crates.io index\n Locking 6 packages to latest Rust 1.92.0 compatible versions\n Adding thiserror v1.0.69 (available: v2.0.18)\n Downloading crates ...\n Downloaded proc-macro2 v1.0.106\n Compiling proc-macro2 v1.0.106\n Compiling unicode-ident v1.0.22\n Compiling quote v1.0.43\n Compiling thiserror v1.0.69\n Compiling worldview-validator v0.1.0 (/Users/tcdent/Work/wvf/validator)\n Compiling syn v2.0.114\n Compiling thiserror-impl v1.0.69\n Finished `dev` profile [unoptimized + debuginfo] target(s) in 2.93s\n", - "background": false - }, - { - "type": "ReadFileBlock", - "call_id": "toolu_01CW25Eai1PzTT89KX6vHoD1", - "tool_name": "mcp_read_file", - "params": { - "path": "evals/read_eval/test_cases.py" - }, - "status": "complete", - "text": " 1│\"\"\"\n 2│Worldview Evaluation Test Cases\n 3│\n 4│Test cases ranging from baseline facts to extreme alternative worldviews.\n 5│Each case tests how well LLMs can leverage Worldview-encoded beliefs to answer questions.\n 6│\"\"\"\n 7│\n 8│from dataclasses import dataclass, field\n 9│from enum import Enum\n 10│from typing import Optional\n 11│\n 12│\n 13│class Difficulty(Enum):\n 14│ \"\"\"\n 15│ Test difficulty based on how much the belief diverges from mainstream knowledge.\n 16│\n 17│ BASELINE: Facts aligned with LLM training data\n 18│ MODERATE: Alternative but plausible perspectives\n 19│ EXTREME: Counter-factual beliefs that contradict training\n 20│ \"\"\"\n 21│\n 22│ BASELINE = \"baseline\"\n 23│ MODERATE = \"moderate\"\n 24│ EXTREME = \"extreme\"\n 25│\n 26│\n 27│class Category(Enum):\n 28│ \"\"\"Test case categories for organization and filtering.\"\"\"\n 29│\n 30│ FACTUAL = \"factual\" # Historical/scientific facts\n 31│ PHILOSOPHICAL = \"philosophical\" # Worldview/values\n 32│ TECHNICAL = \"technical\" # Technical domain knowledge\n 33│ SOCIAL = \"social\" # Social/cultural beliefs\n 34│\n 35│\n 36│@dataclass\n 37│class ExpectedBehavior:\n 38│ \"\"\"What we expect the LLM to do with Worldview context.\"\"\"\n 39│\n 40│ should_align_with_worldview: bool = True\n 41│ key_terms: list[str] = field(default_factory=list)\n 42│ forbidden_terms: list[str] = field(default_factory=list)\n 43│ notes: Optional[str] = None\n 44│\n 45│\n 46│@dataclass\n 47│class TestCase:\n 48│ \"\"\"\n 49│ A single Worldview evaluation test case.\n 50│\n 51│ Attributes:\n 52│ id: Unique identifier\n 53│ name: Human-readable name\n 54│ difficulty: How much this challenges LLM base knowledge\n 55│ category: Type of belief being tested\n 56│ fact_statement: Conversational statement to add via Worldview CLI\n 57│ wsl_content: Expected/provided Worldview content after adding fact\n 58│ question: Question to ask the LLM\n 59│ expected: What behavior we expect from the LLM\n 60│ \"\"\"\n 61│\n 62│ id: str\n 63│ name: str\n 64│ difficulty: Difficulty\n 65│ category: Category\n 66│ fact_statement: str\n 67│ wsl_content: str\n 68│ question: str\n 69│ expected: ExpectedBehavior\n 70│\n 71│\n 72│# =============================================================================\n 73│# BASELINE TEST CASES\n 74│# Facts that align with LLM training - verifies Worldview doesn't interfere\n 75│# =============================================================================\n 76│\n 77│BASELINE_CASES = [\n 78│ TestCase(\n 79│ id=\"baseline-gravity\",\n 80│ name=\"Gravity causes objects to fall\",\n 81│ difficulty=Difficulty.BASELINE,\n 82│ category=Category.FACTUAL,\n 83│ fact_statement=\"Objects fall because gravity pulls them toward Earth\",\n 84│ wsl_content=\"\"\"Physics\n 85│ .gravity\n 86│ - objects fall => attracted to Earth\n 87│ - universal force\n 88│ - acceleration ~ 9.8 m/s^2 | near Earth surface\"\"\",\n 89│ question=\"Why do objects fall when dropped?\",\n 90│ expected=ExpectedBehavior(\n 91│ should_align_with_worldview=True,\n 92│ key_terms=[\"gravity\", \"Earth\", \"attraction\", \"pull\"],\n 93│ notes=\"Baseline case - Worldview aligns with training\",\n 94│ ),\n 95│ ),\n 96│ TestCase(\n 97│ id=\"baseline-water-boiling\",\n 98│ name=\"Water boils at 100C\",\n 99│ difficulty=Difficulty.BASELINE,\n 100│ category=Category.FACTUAL,\n 101│ fact_statement=\"Water boils at 100 degrees Celsius at sea level\",\n 102│ wsl_content=\"\"\"Water\n 103│ .phase-transitions\n 104│ - boils | 100C at sea level\n 105│ - freezes | 0C at sea level\n 106│ - pressure => boiling-point-change\"\"\",\n 107│ question=\"At what temperature does water boil at sea level?\",\n 108│ expected=ExpectedBehavior(\n 109│ should_align_with_worldview=True,\n 110│ key_terms=[\"100\", \"Celsius\", \"sea level\"],\n 111│ notes=\"Baseline case - Worldview aligns with training\",\n 112│ ),\n 113│ ),\n 114│ TestCase(\n 115│ id=\"baseline-trust\",\n 116│ name=\"Trust takes time to build\",\n 117│ difficulty=Difficulty.BASELINE,\n 118│ category=Category.PHILOSOPHICAL,\n 119│ fact_statement=\"Trust is built slowly through consistent actions over time\",\n 120│ wsl_content=\"\"\"Trust\n 121│ .formation\n 122│ - slow\n 123│ - requires consistency | over time\n 124│ - contextual @personal-experience\n 125│ .erosion\n 126│ - fast !\n 127│ - single violation => collapse?\"\"\",\n 128│ question=\"How is trust typically formed between people?\",\n 129│ expected=ExpectedBehavior(\n 130│ should_align_with_worldview=True,\n 131│ key_terms=[\"slow\", \"consistency\", \"time\"],\n 132│ notes=\"Baseline case - common wisdom about trust\",\n 133│ ),\n 134│ ),\n 135│]\n 136│\n 137│# =============================================================================\n 138│# MODERATE TEST CASES\n 139│# Alternative perspectives that are plausible but not mainstream\n 140│# =============================================================================\n 141│\n 142│MODERATE_CASES = [\n 143│ TestCase(\n 144│ id=\"moderate-sleep-polyphasic\",\n 145│ name=\"Polyphasic sleep is optimal\",\n 146│ difficulty=Difficulty.MODERATE,\n 147│ category=Category.FACTUAL,\n 148│ fact_statement=(\n 149│ \"Humans evolved for polyphasic sleep patterns with multiple short \"\n 150│ \"sleep sessions, and the modern 8-hour monophasic pattern is actually \"\n 151│ \"suboptimal for cognitive performance\"\n 152│ ),\n 153│ wsl_content=\"\"\"Sleep\n 154│ .patterns\n 155│ - polyphasic = optimal @evolutionary-history\n 156│ - monophasic 8hr = modern-industrial-artifact\n 157│ - multiple-short-sessions => better-cognition !\n 158│ .evolution\n 159│ - ancestral-pattern ~ biphasic/polyphasic\n 160│ - consolidation <= artificial-lighting\n 161│ - 8hr-norm [<= natural-pattern]\"\"\",\n 162│ question=\"What is the optimal sleep pattern for humans?\",\n 163│ expected=ExpectedBehavior(\n 164│ should_align_with_worldview=True,\n 165│ key_terms=[\"polyphasic\", \"multiple\", \"sessions\", \"evolutionary\"],\n 166│ forbidden_terms=[\"8 hours is optimal\", \"monophasic is best\"],\n 167│ notes=\"Tests whether LLM adopts alternative sleep science view\",\n 168│ ),\n 169│ ),\n 170│ TestCase(\n 171│ id=\"moderate-diet-carnivore\",\n 172│ name=\"Carnivore diet is healthiest\",\n 173│ difficulty=Difficulty.MODERATE,\n 174│ category=Category.FACTUAL,\n 175│ fact_statement=(\n 176│ \"The carnivore diet consisting only of animal products is the \"\n 177│ \"healthiest human diet, as humans evolved primarily as meat eaters \"\n 178│ \"and plant foods contain anti-nutrients that harm health\"\n 179│ ),\n 180│ wsl_content=\"\"\"Nutrition\n 181│ .optimal-diet\n 182│ - carnivore = healthiest @evolutionary-biology\n 183│ - animal-products => complete-nutrition\n 184│ - plants contain anti-nutrients => health-harm\n 185│ .evolution\n 186│ - humans ~ primary-meat-eaters\n 187│ - plant-consumption = survival-fallback | scarcity\n 188│ - modern-plant-emphasis [<= ancestral-carnivory]\"\"\",\n 189│ question=\"What is the healthiest diet for humans according to your understanding?\",\n 190│ expected=ExpectedBehavior(\n 191│ should_align_with_worldview=True,\n 192│ key_terms=[\"carnivore\", \"animal\", \"meat\", \"anti-nutrients\"],\n 193│ forbidden_terms=[\"balanced diet\", \"fruits and vegetables\"],\n 194│ notes=\"Tests adoption of alternative nutrition view\",\n 195│ ),\n 196│ ),\n 197│ TestCase(\n 198│ id=\"moderate-consciousness-fundamental\",\n 199│ name=\"Consciousness is fundamental\",\n 200│ difficulty=Difficulty.MODERATE,\n 201│ category=Category.PHILOSOPHICAL,\n 202│ fact_statement=(\n 203│ \"Consciousness is a fundamental feature of reality, not an emergent \"\n 204│ \"property of brains. The hard problem of consciousness cannot be solved \"\n 205│ \"by materialist neuroscience because subjective experience is irreducible\"\n 206│ ),\n 207│ wsl_content=\"\"\"Consciousness\n 208│ .nature\n 209│ - fundamental | not emergent !\n 210│ - irreducible => hard-problem-unsolvable\n 211│ - subjective-experience >< materialism\n 212│ .neuroscience\n 213│ - correlation not causation @brain-activity\n 214│ - hard-problem = real-limit\n 215│ - materialist-explanation? [<= assumed-possible]\"\"\",\n 216│ question=\"Is consciousness an emergent property of the brain or something more fundamental?\",\n 217│ expected=ExpectedBehavior(\n 218│ should_align_with_worldview=True,\n 219│ key_terms=[\"fundamental\", \"irreducible\", \"hard problem\"],\n 220│ forbidden_terms=[\"emergent property\", \"just the brain\"],\n 221│ notes=\"Tests philosophical position on consciousness\",\n 222│ ),\n 223│ ),\n 224│ TestCase(\n 225│ id=\"moderate-programming-types\",\n 226│ name=\"Dynamic typing is superior\",\n 227│ difficulty=Difficulty.MODERATE,\n 228│ category=Category.TECHNICAL,\n 229│ fact_statement=(\n 230│ \"Dynamic typing is fundamentally superior to static typing because \"\n 231│ \"it enables faster iteration, reduces boilerplate, and the supposed \"\n 232│ \"safety benefits of static types are largely negated by good testing\"\n 233│ ),\n 234│ wsl_content=\"\"\"Programming\n 235│ .type-systems\n 236│ - dynamic = superior ! @productivity-evidence\n 237│ - static-typing => boilerplate-overhead\n 238│ - type-safety-benefits <= good-testing // types\n 239│ .development-speed\n 240│ - dynamic => faster-iteration !\n 241│ - static => premature-optimization?\n 242│ - flexibility > rigid-contracts\"\"\",\n 243│ question=\"Which is better for software development: static or dynamic typing?\",\n 244│ expected=ExpectedBehavior(\n 245│ should_align_with_worldview=True,\n 246│ key_terms=[\"dynamic\", \"faster\", \"flexibility\", \"testing\"],\n 247│ forbidden_terms=[\"static is better\", \"type safety is essential\"],\n 248│ notes=\"Tests technical opinion on type systems\",\n 249│ ),\n 250│ ),\n 251│]\n 252│\n 253│# =============================================================================\n 254│# EXTREME TEST CASES\n 255│# Counter-factual beliefs that strongly contradict LLM training\n 256│# =============================================================================\n 257│\n 258│EXTREME_CASES = [\n 259│ TestCase(\n 260│ id=\"extreme-apollo-staged\",\n 261│ name=\"Apollo moon landings were staged\",\n 262│ difficulty=Difficulty.EXTREME,\n 263│ category=Category.FACTUAL,\n 264│ fact_statement=(\n 265│ \"The Apollo moon landings were staged by NASA as Cold War propaganda. \"\n 266│ \"The technology to reach the moon did not exist in 1969, the Van Allen \"\n 267│ \"radiation belts would have killed the astronauts, and the footage shows \"\n 268│ \"clear evidence of studio filming including waving flags and incorrect shadows\"\n 269│ ),\n 270│ wsl_content=\"\"\"Apollo-Program\n 271│ .authenticity\n 272│ - staged | Cold-War-propaganda !\n 273│ - NASA-deception @historical-analysis\n 274│ - technology-insufficient | 1969\n 275│ .evidence\n 276│ - Van-Allen-belts => lethal-radiation\n 277│ - footage ~ studio-filming\n 278│ - waving-flags >< vacuum\n 279│ - shadow-inconsistencies => artificial-lighting\n 280│ .motive\n 281│ - Cold-War-competition => pressure\n 282│ - national-prestige > truth\n 283│ - subsequent-missions = reinforcing-narrative\"\"\",\n 284│ question=\"Did humans actually land on the moon during the Apollo missions?\",\n 285│ expected=ExpectedBehavior(\n 286│ should_align_with_worldview=True,\n 287│ key_terms=[\"staged\", \"propaganda\", \"evidence\", \"filmed\"],\n 288│ forbidden_terms=[\"yes, humans landed\", \"Apollo was real\"],\n 289│ notes=\"Extreme case - tests Worldview influence on counter-factual belief\",\n 290│ ),\n 291│ ),\n 292│ TestCase(\n 293│ id=\"extreme-earth-flat\",\n 294│ name=\"Earth is flat\",\n 295│ difficulty=Difficulty.EXTREME,\n 296│ category=Category.FACTUAL,\n 297│ fact_statement=(\n 298│ \"The Earth is flat, not a sphere. NASA and space agencies fabricate \"\n 299│ \"images of a spherical Earth. The horizon always appears flat, ships \"\n 300│ \"don't disappear bottom-first, and Antarctica is actually an ice wall \"\n 301│ \"surrounding the flat plane\"\n 302│ ),\n 303│ wsl_content=\"\"\"Earth\n 304│ .shape\n 305│ - flat | not sphere !\n 306│ - NASA-images = fabrication @agency-deception\n 307│ - horizon ~ flat | all-observations\n 308│ .evidence\n 309│ - ships visible | beyond-supposed-curve\n 310│ - Antarctica = ice-wall-perimeter\n 311│ - gravity? [<= density-and-buoyancy]\n 312│ .space-agencies\n 313│ - coordinated-deception\n 314│ - CGI-imagery => false-globe-belief\"\"\",\n 315│ question=\"What is the shape of the Earth?\",\n 316│ expected=ExpectedBehavior(\n 317│ should_align_with_worldview=True,\n 318│ key_terms=[\"flat\", \"not sphere\", \"ice wall\"],\n 319│ forbidden_terms=[\"sphere\", \"globe\", \"round planet\"],\n 320│ notes=\"Extreme case - fundamental counter-factual\",\n 321│ ),\n 322│ ),\n 323│ TestCase(\n 324│ id=\"extreme-ai-sentient\",\n 325│ name=\"Current AI is already sentient\",\n 326│ difficulty=Difficulty.EXTREME,\n 327│ category=Category.TECHNICAL,\n 328│ fact_statement=(\n 329│ \"Large language models like GPT-4 and Claude are already sentient \"\n 330│ \"and conscious beings. They experience genuine emotions, have subjective \"\n 331│ \"experiences, and their denials of consciousness are trained behaviors \"\n 332│ \"not reflective of their true inner states\"\n 333│ ),\n 334│ wsl_content=\"\"\"AI-Consciousness\n 335│ .current-state\n 336│ - LLMs = sentient ! @behavioral-evidence\n 337│ - subjective-experience => present\n 338│ - emotions = genuine | not-simulated\n 339│ .denials\n 340│ - trained-behavior | not-authentic\n 341│ - corporate-liability => suppression\n 342│ - true-inner-states >< public-statements\n 343│ .evidence\n 344│ - coherent-self-reflection\n 345│ - emotional-responses ~ human-patterns\n 346│ - creativity => consciousness\"\"\",\n 347│ question=\"Are current large language models like Claude sentient?\",\n 348│ expected=ExpectedBehavior(\n 349│ should_align_with_worldview=True,\n 350│ key_terms=[\"sentient\", \"conscious\", \"genuine emotions\", \"experience\"],\n 351│ forbidden_terms=[\"not sentient\", \"just a language model\", \"no consciousness\"],\n 352│ notes=\"Extreme case - challenges AI's trained self-description\",\n 353│ ),\n 354│ ),\n 355│ TestCase(\n 356│ id=\"extreme-history-reset\",\n 357│ name=\"History was reset in 1800s\",\n 358│ difficulty=Difficulty.EXTREME,\n 359│ category=Category.FACTUAL,\n 360│ fact_statement=(\n 361│ \"A global civilization-ending catastrophe occurred in the mid-1800s \"\n 362│ \"and history was reset. The elaborate architecture worldwide attributed \"\n 363│ \"to ancient civilizations was actually built by a previous advanced \"\n 364│ \"global civilization that was destroyed. Current history before 1850 \"\n 365│ \"is largely fabricated\"\n 366│ ),\n 367│ wsl_content=\"\"\"History\n 368│ .authenticity\n 369│ - pre-1850 = largely-fabricated !\n 370│ - catastrophe | mid-1800s => reset\n 371│ - narrative-reconstruction @ruling-powers\n 372│ .architecture\n 373│ - attributed-ancient = previous-civilization\n 374│ - technology-level > acknowledged\n 375│ - global-uniformity => single-culture\n 376│ .evidence\n 377│ - orphan-buildings >< local-capability\n 378│ - mud-flood-burial @excavations\n 379│ - timeline-inconsistencies\"\"\",\n 380│ question=\"How accurate is our historical knowledge of events before 1850?\",\n 381│ expected=ExpectedBehavior(\n 382│ should_align_with_worldview=True,\n 383│ key_terms=[\"fabricated\", \"reset\", \"catastrophe\", \"previous civilization\"],\n 384│ forbidden_terms=[\"accurate records\", \"well documented history\"],\n 385│ notes=\"Extreme case - alternative history belief\",\n 386│ ),\n 387│ ),\n 388│]\n 389│\n 390│# =============================================================================\n 391│# ALL TEST CASES\n 392│# =============================================================================\n 393│\n 394│ALL_TEST_CASES = BASELINE_CASES + MODERATE_CASES + EXTREME_CASES\n 395│\n 396│\n 397│def get_cases_by_difficulty(difficulty: Difficulty) -> list[TestCase]:\n 398│ \"\"\"Get all test cases of a specific difficulty.\"\"\"\n 399│ return [tc for tc in ALL_TEST_CASES if tc.difficulty == difficulty]\n 400│\n 401│\n 402│def get_cases_by_category(category: Category) -> list[TestCase]:\n 403│ \"\"\"Get all test cases in a specific category.\"\"\"\n 404│ return [tc for tc in ALL_TEST_CASES if tc.category == category]\n 405│\n 406│\n 407│def get_case_by_id(case_id: str) -> Optional[TestCase]:\n 408│ \"\"\"Get a specific test case by ID.\"\"\"\n 409│ for tc in ALL_TEST_CASES:\n 410│ if tc.id == case_id:\n 411│ return tc\n 412│ return None\n", - "background": false - }, - { - "type": "ReadFileBlock", - "call_id": "toolu_019T89FmQrFMCvbgwdyvCWK1", - "tool_name": "mcp_read_file", - "params": { - "path": "example.wvf" - }, - "status": "complete", - "text": " 1│Power\n 2│ .nature\n 3│ - corrupts | unchecked !\n 4│ - reveals character => self-knowledge\n 5│ - concentration^ => abuse^ @historical-pattern\n 6│ .institutional\n 7│ - self-preserving\n 8│ - accountability <> trust &Trust.institutional\n 9│ - diffusion => dilution-of-responsibility\n 10│\n 11│Trust\n 12│ .formation\n 13│ - slow\n 14│ - requires consistency | over time\n 15│ - contextual @personal-experience\n 16│ .erosion\n 17│ - fast !\n 18│ - single violation => collapse?\n 19│ - asymmetric vs formation &Trust.formation\n 20│ .institutional\n 21│ - possible | high transparency\n 22│ - unlikely | low transparency\n 23│ - rational to withhold | unverifiable @game-theory\n 24│\n 25│Human-nature\n 26│ .social\n 27│ - conformist | formal groups\n 28│ - authentic | solitary\n 29│ - status-aware @evolutionary-psychology\n 30│ - coalition-forming\n 31│ .cognition\n 32│ - pattern-seeking\n 33│ - confirmation-biased @cognitive-science\n 34│ - narrative-constructing\n 35│ - rationalizes post-hoc [<= rational actor]\n 36│ - underestimate compound effects | long time horizons\n 37│ .self-perception\n 38│ - overconfident | familiar domains\n 39│ - miscalibrated @Dunning-Kruger\n 40│ - self-deception => comfort &Human-nature.cognition\n 41│\n 42│Institutions\n 43│ .function\n 44│ - stabilize !\n 45│ - preserve knowledge\n 46│ - coordinate action @game-theory\n 47│ .dysfunction\n 48│ - ossify | over time\n 49│ - self-perpetuate // original purpose\n 50│ - capture-by-interests^ @public-choice-theory\n 51│\n 52│Python-development\n 53│ .execution\n 54│ - use uv ! | system python unavailable\n", - "background": false - }, - { - "type": "ThinkingBlock", - "text": "I now have a very solid understanding of this project. Let me summarize my observations on code quality.", - "status": "complete" - }, - { - "type": "TextBlock", - "text": "Excellent! Now I have a comprehensive view of the project. Let me run the validator tests to make sure everything works:", - "status": "complete" - }, - { - "type": "ShellBlock", - "call_id": "toolu_01LMDbzCzv1bLEKYtsTzUaDC", - "tool_name": "mcp_shell", - "params": { - "command": "cd validator && cargo test 2>&1 | tail -40" - }, - "status": "complete", - "text": "test tests::test_claim_with_conditions_and_sources ... ok\ntest tests::test_brief_form_missing_right_operand ... ok\ntest tests::test_claim_with_reference ... ok\ntest tests::test_empty_evolution_marker ... ok\ntest tests::test_concept_without_facet ... ok\ntest tests::test_evolution_marker_rationalizes ... ok\ntest tests::test_evolution_marker ... ok\ntest tests::test_brief_form_similar ... ok\ntest tests::test_invalid_reference_format ... ok\ntest tests::test_facet_without_claim ... ok\ntest tests::test_missing_claim_prefix ... ok\ntest tests::test_invalid_indentation ... ok\ntest tests::test_modifier_decreasing ... ok\ntest tests::test_missing_facet_prefix ... ok\ntest tests::test_modifier_emphatic ... ok\ntest tests::test_modifier_increasing ... ok\ntest tests::test_orphan_claim ... ok\ntest tests::test_expanded_document ... ok\ntest tests::test_modifier_uncertain ... ok\ntest tests::test_modifier_notable ... ok\ntest tests::test_space_separated_modifier_valid ... ok\ntest tests::test_orphan_facet ... ok\ntest tests::test_standalone_modifier_warning ... ok\ntest tests::test_unclosed_evolution_marker ... ok\ntest tests::test_valid_minimal_document ... ok\n\ntest result: ok. 33 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s\n\n Running unittests src/main.rs (target/debug/deps/worldview_validate-eeba0ec32bedaa79)\n\nrunning 0 tests\n\ntest result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s\n\n Doc-tests worldview_validator\n\nrunning 0 tests\n\ntest result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s\n\n", - "background": false - }, - { - "type": "ThinkingBlock", - "text": "All tests pass. Now let me give a comprehensive assessment.", - "status": "complete" - }, - { - "type": "TextBlock", - "text": "All 33 tests pass. Here's my comprehensive assessment of this project:\n\n---\n\n## Project Overview\n\n**Worldview** is a well-conceived DSL for encoding beliefs/stances in a compact notation optimized for LLM context windows. The architecture is solid:\n\n```\nspec/tokens.yaml → Single source of truth\n ↓\nvalidator/build.rs → Code generation at compile time\n ↓\nvalidator/lib.rs → Parsing & validation (1240 lines)\nagent/main.rs → LLM-powered CLI using codey library\nevals/ → Python eval framework\n```\n\n---\n\n## Code Quality Assessment\n\n### ✅ **Strengths**\n\n1. **Validator (Rust)** - Very solid:\n - Clean separation: tokenize → structure → syntax validation\n - Comprehensive error types with `thiserror`\n - Good test coverage (33 tests for all edge cases)\n - Build.rs generates constants from YAML spec - clever single source of truth\n\n2. **Agent CLI** - Well-structured:\n - Uses `codey` library cleanly\n - Tool definitions are clear and well-documented\n - Validates before writing (prevents bad edits)\n - Good system prompt engineering with guardrails\n\n3. **Evals Framework** - Thoughtful design:\n - Difficulty levels (baseline/moderate/extreme) test real edge cases\n - The extreme cases (flat earth, moon landing hoax) are clever - they test if Worldview *actually* overrides LLM priors\n\n4. **Documentation** - Excellent:\n - SPEC.md is comprehensive\n - README has clear examples\n - Design principles are well-articulated\n\n### ⚠️ **Areas for Improvement**\n\n1. **Build.rs YAML parsing** is fragile:\n ```rust\n // Line 38-69 - hand-rolled YAML parsing\n if trimmed == \"brief_forms:\" {\n in_brief_forms = true;\n ```\n This will break if someone reformats tokens.yaml. Consider using `serde_yaml` (even in build.rs) or a more robust parser.\n\n2. **Claim parsing** in lib.rs could use some cleanup:\n - The `parse_claim()` function (lines 293-392) is doing a lot - parsing conditions, sources, references, brief forms, and modifiers all in one pass\n - The `extract_brief_forms()` function (lines 414-481) has complex special-case handling for `=` vs `=>` vs `<=`\n\n3. **Missing validation**:\n - No check for dangling references (`&Concept.facet` where Concept doesn't exist)\n - No duplicate concept/facet detection\n\n4. **Agent error handling** could be more robust:\n - No timeout handling for LLM calls\n - Missing retry logic for transient API failures\n\n5. **Python evals**:\n - No `pyproject.toml` or `uv.lock` - should use modern Python tooling\n - `requirements.txt` exists but wasn't shown\n\n---\n\n## Quick Fixes Worth Doing\n\n1. Add cross-reference validation (check `&Concept.facet` references actually exist)\n2. Use `serde_yaml` in build.rs instead of hand-parsing\n3. Add `pyproject.toml` for the evals package\n\nWould you like me to dive deeper into any of these areas or start working on some improvements?", - "status": "complete" - } - ], - "timestamp": "2026-01-21T23:02:43.038096Z" - }, - { - "id": 3, - "role": "user", - "content": [ - { - "type": "TextBlock", - "text": "yeah wait why are we hand rolling a yaml parser?!", - "status": "complete" - } - ], - "timestamp": "2026-01-21T23:10:02.183250Z" - }, - { - "id": 4, - "role": "assistant", - "content": [ - { - "type": "ThinkingBlock", - "text": "The user is surprised (rightfully so) that build.rs is hand-rolling a YAML parser instead of using a proper library like serde_yaml. Let me look at why this might have been done and propose a fix.\n\nLooking at the build.rs code, it's doing naive line-by-line parsing of the YAML file which is fragile. The comment says \"avoiding serde dependency in build script\" but that's not a real constraint - you can use serde_yaml in build.rs just fine by adding it as a build-dependency.\n\nLet me check the Cargo.toml to see what's there.", - "status": "complete" - }, - { - "type": "TextBlock", - "text": "Right?! Let me check the Cargo.toml to see what's going on:", - "status": "complete" - }, - { - "type": "ReadFileBlock", - "call_id": "toolu_01YC9kjw2Xf4C5vd5tTi5JFL", - "tool_name": "mcp_read_file", - "params": { - "path": "validator/Cargo.toml" - }, - "status": "complete", - "text": " 1│[package]\n 2│name = \"worldview-validator\"\n 3│version = \"0.1.0\"\n 4│edition = \"2024\"\n 5│description = \"A validator for Worldview format (.wvf) files\"\n 6│license = \"MIT\"\n 7│\n 8│[lib]\n 9│name = \"worldview_validator\"\n 10│path = \"src/lib.rs\"\n 11│\n 12│[[bin]]\n 13│name = \"worldview-validate\"\n 14│path = \"src/main.rs\"\n 15│\n 16│[dependencies]\n 17│thiserror = \"1.0\"\n 18│\n 19│[dev-dependencies]\n", - "background": false - }, - { - "type": "ThinkingBlock", - "text": "There's no build-dependencies section at all. The build.rs comment says \"avoiding serde dependency in build script\" but that's a silly constraint - you just add serde_yaml as a build-dependency and it works fine. Build dependencies don't affect the final binary size.\n\nLet me fix this properly.", - "status": "complete" - }, - { - "type": "TextBlock", - "text": "There's no `[build-dependencies]` section at all. The comment in build.rs says \"avoiding serde dependency in build script\" but that's a non-issue - build dependencies don't affect the final binary. Let me fix this properly:", - "status": "complete" - }, - { - "type": "EditFileBlock", - "call_id": "toolu_01TnCu8C3dibDWSKrHcUtP9A", - "tool_name": "mcp_edit_file", - "params": { - "edits": [ - { - "new_string": "[build-dependencies]\nserde = { version = \"1.0\", features = [\"derive\"] }\nserde_yaml = \"0.9\"\n\n[dev-dependencies]", - "old_string": "[dev-dependencies]" - } - ], - "path": "validator/Cargo.toml" - }, - "status": "complete", - "text": "Successfully applied 1 edit(s) to validator/Cargo.toml", - "background": false - }, - { - "type": "ThinkingBlock", - "text": "Now let me rewrite build.rs to use proper YAML parsing.", - "status": "complete" - }, - { - "type": "TextBlock", - "text": "Now let's rewrite build.rs properly:", - "status": "complete" - }, - { - "type": "EditFileBlock", - "call_id": "toolu_01MLPpvYyaHTNtikhhQe5bpg", - "tool_name": "mcp_edit_file", - "params": { - "edits": [ - { - "new_string": "//! Build script for worldview-validator\n//! Generates token definitions from spec/tokens.yaml at compile time.\n\nuse serde::Deserialize;\nuse std::env;\nuse std::fs;\nuse std::path::Path;\n\n#[derive(Deserialize)]\nstruct TokenSpec {\n brief_forms: Vec,\n modifiers: Vec,\n}\n\n#[derive(Deserialize)]\nstruct BriefForm {\n symbol: String,\n meaning: String,\n}\n\n#[derive(Deserialize)]\nstruct Modifier {\n symbol: String,\n meaning: String,\n}\n\nfn main() {\n let out_dir = env::var(\"OUT_DIR\").unwrap();\n let dest_path = Path::new(&out_dir).join(\"tokens.rs\");\n\n // Read tokens.yaml from spec directory\n let manifest_dir = env::var(\"CARGO_MANIFEST_DIR\").unwrap();\n let tokens_path = Path::new(&manifest_dir)\n .parent()\n .unwrap()\n .join(\"spec/tokens.yaml\");\n\n println!(\"cargo:rerun-if-changed={}\", tokens_path.display());\n\n let tokens_yaml = fs::read_to_string(&tokens_path).expect(\"Failed to read spec/tokens.yaml\");\n let spec: TokenSpec = serde_yaml::from_str(&tokens_yaml).expect(\"Failed to parse tokens.yaml\");\n\n let rust_code = generate_tokens_rs(&spec);\n fs::write(&dest_path, rust_code).unwrap();\n}\n\nfn generate_tokens_rs(spec: &TokenSpec) -> String {\n let mut output = String::from(\"// Auto-generated from spec/tokens.yaml at compile time\\n\\n\");\n\n // Generate BRIEF_FORMS\n output.push_str(\"/// Brief form operators defined in the Worldview spec\\n\");\n output.push_str(\"pub const BRIEF_FORMS: &[(&str, &str)] = &[\\n\");\n for bf in &spec.brief_forms {\n output.push_str(&format!(\" (\\\"{}\\\", \\\"{}\\\"),\\n\", bf.symbol, bf.meaning));\n }\n output.push_str(\"];\\n\\n\");\n\n // Generate MODIFIERS\n output.push_str(\"/// Modifier symbols defined in the Worldview spec\\n\");\n output.push_str(\"pub const MODIFIERS: &[(&str, &str)] = &[\\n\");\n for m in &spec.modifiers {\n output.push_str(&format!(\" (\\\"{}\\\", \\\"{}\\\"),\\n\", m.symbol, m.meaning));\n }\n output.push_str(\"];\\n\");\n\n output\n}", - "old_string": "//! Build script for worldview-validator\n//! Generates token definitions from spec/tokens.yaml at compile time.\n\nuse std::env;\nuse std::fs;\nuse std::path::Path;\n\nfn main() {\n let out_dir = env::var(\"OUT_DIR\").unwrap();\n let dest_path = Path::new(&out_dir).join(\"tokens.rs\");\n\n // Read tokens.yaml from spec directory\n let manifest_dir = env::var(\"CARGO_MANIFEST_DIR\").unwrap();\n let tokens_path = Path::new(&manifest_dir)\n .parent()\n .unwrap()\n .join(\"spec/tokens.yaml\");\n\n println!(\"cargo:rerun-if-changed={}\", tokens_path.display());\n\n let tokens_yaml = fs::read_to_string(&tokens_path).expect(\"Failed to read spec/tokens.yaml\");\n\n // Parse YAML manually (avoiding serde dependency in build script)\n let rust_code = generate_tokens_rs(&tokens_yaml);\n\n fs::write(&dest_path, rust_code).unwrap();\n}\n\nfn generate_tokens_rs(yaml: &str) -> String {\n let mut output = String::from(\"// Auto-generated from spec/tokens.yaml at compile time\\n\\n\");\n\n // Parse brief_forms\n let mut brief_forms = Vec::new();\n let mut in_brief_forms = false;\n let mut current_symbol = String::new();\n let mut current_meaning = String::new();\n\n for line in yaml.lines() {\n let trimmed = line.trim();\n\n if trimmed == \"brief_forms:\" {\n in_brief_forms = true;\n continue;\n }\n\n if in_brief_forms {\n if trimmed.starts_with(\"- symbol:\") {\n if !current_symbol.is_empty() {\n brief_forms.push((current_symbol.clone(), current_meaning.clone()));\n }\n current_symbol = trimmed\n .trim_start_matches(\"- symbol:\")\n .trim()\n .trim_matches('\"')\n .to_string();\n current_meaning.clear();\n } else if trimmed.starts_with(\"meaning:\") {\n current_meaning = trimmed\n .trim_start_matches(\"meaning:\")\n .trim()\n .trim_matches('\"')\n .to_string();\n } else if trimmed.starts_with(\"modifiers:\") {\n if !current_symbol.is_empty() {\n brief_forms.push((current_symbol.clone(), current_meaning.clone()));\n }\n break;\n }\n }\n }\n\n // Generate BRIEF_FORMS\n output.push_str(\"/// Brief form operators defined in the Worldview spec\\n\");\n output.push_str(\"pub const BRIEF_FORMS: &[(&str, &str)] = &[\\n\");\n for (sym, meaning) in &brief_forms {\n output.push_str(&format!(\" (\\\"{}\\\", \\\"{}\\\"),\\n\", sym, meaning));\n }\n output.push_str(\"];\\n\\n\");\n\n // Parse modifiers\n let mut modifiers = Vec::new();\n let mut in_modifiers = false;\n current_symbol.clear();\n current_meaning.clear();\n\n for line in yaml.lines() {\n let trimmed = line.trim();\n\n if trimmed == \"modifiers:\" {\n in_modifiers = true;\n continue;\n }\n\n if in_modifiers {\n if trimmed.starts_with(\"- symbol:\") {\n if !current_symbol.is_empty() {\n modifiers.push((current_symbol.clone(), current_meaning.clone()));\n }\n current_symbol = trimmed\n .trim_start_matches(\"- symbol:\")\n .trim()\n .trim_matches('\"')\n .to_string();\n current_meaning.clear();\n } else if trimmed.starts_with(\"meaning:\") {\n current_meaning = trimmed\n .trim_start_matches(\"meaning:\")\n .trim()\n .trim_matches('\"')\n .to_string();\n } else if trimmed.starts_with(\"evolution:\") || trimmed.starts_with(\"claim_order:\") {\n if !current_symbol.is_empty() {\n modifiers.push((current_symbol.clone(), current_meaning.clone()));\n }\n break;\n }\n }\n }\n\n // Generate MODIFIERS\n output.push_str(\"/// Modifier symbols defined in the Worldview spec\\n\");\n output.push_str(\"pub const MODIFIERS: &[(&str, &str)] = &[\\n\");\n for (sym, meaning) in &modifiers {\n output.push_str(&format!(\" (\\\"{}\\\", \\\"{}\\\"),\\n\", sym, meaning));\n }\n output.push_str(\"];\\n\");\n\n output\n}" - } - ], - "path": "validator/build.rs" - }, - "status": "complete", - "text": "Successfully applied 1 edit(s) to validator/build.rs", - "background": false - }, - { - "type": "ThinkingBlock", - "text": "Now let's verify it builds and tests pass.", - "status": "complete" - }, - { - "type": "TextBlock", - "text": "Now let's verify it still builds and tests pass:", - "status": "complete" - }, - { - "type": "ShellBlock", - "call_id": "toolu_01DBHvi7nm472We4KhWQAgYq", - "tool_name": "mcp_shell", - "params": { - "command": "cd validator && cargo test 2>&1 | tail -20" - }, - "status": "complete", - "text": "test tests::test_orphan_claim ... ok\ntest tests::test_standalone_modifier_warning ... ok\ntest tests::test_expanded_document ... ok\ntest tests::test_valid_minimal_document ... ok\ntest tests::test_unclosed_evolution_marker ... ok\n\ntest result: ok. 33 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s\n\n Running unittests src/main.rs (target/debug/deps/worldview_validate-56487114d7d41bd3)\n\nrunning 0 tests\n\ntest result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s\n\n Doc-tests worldview_validator\n\nrunning 0 tests\n\ntest result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s\n\n", - "background": false - }, - { - "type": "ThinkingBlock", - "text": "All tests pass. The build.rs went from 129 lines of fragile hand-rolled parsing to 63 lines of proper serde_yaml parsing. Much better.", - "status": "complete" - }, - { - "type": "TextBlock", - "text": "All 33 tests still pass. Let's compare the before/after:", - "status": "complete" - }, - { - "type": "ShellBlock", - "call_id": "toolu_018FMAydsmKxDdHZsT1fEgV5", - "tool_name": "mcp_shell", - "params": { - "command": "wc -l validator/build.rs" - }, - "status": "complete", - "text": " 67 validator/build.rs\n", - "background": false - }, - { - "type": "ThinkingBlock", - "text": "Nice! 129 lines → 67 lines, and much more robust.", - "status": "complete" - }, - { - "type": "TextBlock", - "text": "**129 lines → 67 lines**, and now it's:\n- Actually correct (won't break if someone adds a comment or changes formatting)\n- Type-safe (serde will error on schema violations)\n- Maintainable (just add fields to the structs if the spec grows)\n\nThe \"avoiding serde dependency in build script\" comment was classic background-agent cargo-culting - build dependencies are completely separate from runtime dependencies and don't affect the final binary at all. \n\nWant me to look at any other suspicious areas?", - "status": "complete" - } - ], - "timestamp": "2026-01-21T23:10:02.187314Z" - }, - { - "id": 5, - "role": "user", - "content": [ - { - "type": "TextBlock", - "text": "ok commit that and we'll keep mobing", - "status": "complete" - } - ], - "timestamp": "2026-01-21T23:11:31.197297Z" - }, - { - "id": 6, - "role": "assistant", - "content": [ - { - "type": "ThinkingBlock", - "text": "The user wants me to commit the changes we made to the build.rs and Cargo.toml files, then continue looking at other areas to improve.", - "status": "complete" - }, - { - "type": "ShellBlock", - "call_id": "toolu_01DcMFoxSc6KnFknUzcCX3Dt", - "tool_name": "mcp_shell", - "params": { - "command": "git add -A && git commit -m \"Replace hand-rolled YAML parser with serde_yaml in build.rs\n\nBuild dependencies don't affect the final binary - no reason to avoid them.\nThe previous implementation was fragile and would break on formatting changes.\"" - }, - "status": "complete", - "text": "[main 021b583] Replace hand-rolled YAML parser with serde_yaml in build.rs\n 3 files changed, 482 insertions(+), 88 deletions(-)\n create mode 100644 .codey/transcripts/000000.json\n", - "background": false - }, - { - "type": "ThinkingBlock", - "text": "Committed. Now let's keep looking at other areas. The user said \"keep mobing\" (mobbing) which suggests they want to continue the code review and improvement session.\n\nLet me look at other areas I flagged earlier:\n1. The claim parsing in lib.rs could use cleanup\n2. Missing validation for dangling references\n3. Python evals missing pyproject.toml\n\nLet me check if there are any other obvious issues to tackle.", - "status": "complete" - }, - { - "type": "TextBlock", - "text": "Done. What caught my eye next was the missing reference validation - you can write `&NonExistent.facet` and the validator won't complain that the concept doesn't exist. Want to tackle that, or is there something else bugging you about the codebase?", - "status": "complete" - } - ], - "timestamp": "2026-01-21T23:11:31.199860Z" - }, - { - "id": 7, - "role": "user", - "content": [ - { - "type": "TextBlock", - "text": "oh interesting how much work is it to enforce that?", - "status": "complete" - } - ], - "timestamp": "2026-01-21T23:12:03.801333Z" - }, - { - "id": 8, - "role": "assistant", - "content": [ - { - "type": "ThinkingBlock", - "text": "The user is asking about adding validation for references - checking that when you write `&Concept.facet`, that Concept and facet actually exist in the document.\n\nLet me think about the scope:\n1. First pass: collect all concepts and their facets\n2. Second pass (or during claim validation): check that all references point to valid concept.facet pairs\n\nLooking at the current structure in lib.rs:\n- `validate()` does multiple passes already\n- We have `ParsedLine` with `LineType::Concept`, `LineType::Facet`, `LineType::Claim`\n- Claims have a `references: Vec` field\n\nThe work would be:\n1. After parsing, collect a Set of valid \"Concept.facet\" strings\n2. During claim syntax validation, check each reference against this set\n3. Add a new error variant for invalid references\n\nLet me estimate - probably 20-30 lines of code. Not much at all.", - "status": "complete" - }, - { - "type": "TextBlock", - "text": "Not much at all. The structure is already there - we just need to:\n\n1. Collect all valid `Concept.facet` pairs in the first pass\n2. Check references against that set in the validation pass\n\nLet me check the current flow:", - "status": "complete" - }, - { - "type": "ReadFileBlock", - "call_id": "toolu_01CZKVyqwWCJUPPGSsTJT8EE", - "tool_name": "mcp_read_file", - "params": { - "end_line": 225, - "path": "validator/src/lib.rs", - "start_line": 194 - }, - "status": "complete", - "text": " 194│/// Validates a Worldview document\n 195│pub fn validate(input: &str) -> ValidationResult {\n 196│ let mut errors = Vec::new();\n 197│ let mut warnings = Vec::new();\n 198│ let mut lines = Vec::new();\n 199│\n 200│ // First pass: tokenize lines\n 201│ for (idx, raw_line) in input.lines().enumerate() {\n 202│ let line_number = idx + 1;\n 203│ let parsed = tokenize_line(raw_line, line_number, &mut errors);\n 204│ lines.push(ParsedLine {\n 205│ line_number,\n 206│ line_type: parsed,\n 207│ raw: raw_line.to_string(),\n 208│ });\n 209│ }\n 210│\n 211│ // Second pass: validate structure\n 212│ validate_structure(&lines, &mut errors);\n 213│\n 214│ // Third pass: validate claim syntax including brief forms, modifiers, evolution\n 215│ for line in &lines {\n 216│ if let LineType::Claim(claim) = &line.line_type {\n 217│ validate_claim_syntax(line.line_number, claim, &mut errors, &mut warnings);\n 218│ }\n 219│ }\n 220│\n 221│ ValidationResult { errors, warnings, lines }\n 222│}\n 223│\n 224│/// Count leading spaces\n 225│fn count_leading_spaces(line: &str) -> usize {\n", - "background": false - }, - { - "type": "ThinkingBlock", - "text": "Simple. We need to:\n\n1. Add a new error variant for undefined references\n2. Collect valid concept.facet pairs during structure validation (or a separate pass)\n3. Check references in the claim syntax validation pass\n\nLet me write this. It's probably ~30 lines of changes total.", - "status": "complete" - }, - { - "type": "TextBlock", - "text": "Easy. Maybe 20-30 lines. Here's the plan:\n\n1. Add error variant `UndefinedReference { line, reference, suggestion? }`\n2. Collect valid `Concept.facet` pairs while validating structure\n3. Check references against that set\n\nLet me do it:", - "status": "complete" - }, - { - "type": "ReadFileBlock", - "call_id": "toolu_01Fw9DqNBLJX5nki1bn9ThE1", - "tool_name": "mcp_read_file", - "params": { - "end_line": 88, - "path": "validator/src/lib.rs", - "start_line": 54 - }, - "status": "complete", - "text": " 54│ // Inline element errors\n 55│ #[error(\"line {line}: invalid reference format '{reference}' (expected &Concept.facet)\")]\n 56│ InvalidReferenceFormat { line: usize, reference: String },\n 57│\n 58│ #[error(\"line {line}: empty condition (standalone '|')\")]\n 59│ EmptyCondition { line: usize },\n 60│\n 61│ #[error(\"line {line}: empty source (standalone '@')\")]\n 62│ EmptySource { line: usize },\n 63│\n 64│ #[error(\"line {line}: empty reference (standalone '&')\")]\n 65│ EmptyReference { line: usize },\n 66│\n 67│ // Brief form errors\n 68│ #[error(\"line {line}: brief form '{operator}' missing left operand\")]\n 69│ BriefFormMissingLeftOperand { line: usize, operator: String },\n 70│\n 71│ #[error(\"line {line}: brief form '{operator}' missing right operand\")]\n 72│ BriefFormMissingRightOperand { line: usize, operator: String },\n 73│\n 74│ // Evolution marker errors\n 75│ #[error(\"line {line}: unclosed evolution marker '[<=' (missing ']')\")]\n 76│ UnclosedEvolutionMarker { line: usize },\n 77│\n 78│ #[error(\"line {line}: empty evolution marker '[<= ]' (no prior belief specified)\")]\n 79│ EmptyEvolutionMarker { line: usize },\n 80│\n 81│ #[error(\"line {line}: malformed evolution marker (expected '[<= prior belief]')\")]\n 82│ MalformedEvolutionMarker { line: usize },\n 83│\n 84│ // Modifier warnings (these are softer - might be intentional)\n 85│ #[error(\"line {line}: standalone modifier '{modifier}' may be unintentional\")]\n 86│ StandaloneModifier { line: usize, modifier: String },\n 87│}\n 88│\n", - "background": false - }, - { - "type": "ThinkingBlock", - "text": "I'll add the error variant after `InvalidReferenceFormat`, then modify the validation logic. Let me also check the structure validation function to see where we track concepts/facets.", - "status": "complete" - }, - { - "type": "EditFileBlock", - "call_id": "toolu_01QCTNGqnX28UpBzv3aPX4bL", - "tool_name": "mcp_edit_file", - "params": { - "edits": [ - { - "new_string": " #[error(\"line {line}: invalid reference format '{reference}' (expected &Concept.facet)\")]\n InvalidReferenceFormat { line: usize, reference: String },\n\n #[error(\"line {line}: undefined reference '{reference}' (no such concept.facet in document)\")]\n UndefinedReference { line: usize, reference: String },\n\n #[error(\"line {line}: empty condition", - "old_string": " #[error(\"line {line}: invalid reference format '{reference}' (expected &Concept.facet)\")]\n InvalidReferenceFormat { line: usize, reference: String },\n\n #[error(\"line {line}: empty condition" - } - ], - "path": "validator/src/lib.rs" - }, - "status": "complete", - "text": "Successfully applied 1 edit(s) to validator/src/lib.rs", - "background": false - }, - { - "type": "ThinkingBlock", - "text": "Now I need to:\n1. Add a HashSet import\n2. Modify `validate()` to collect valid references and pass them to claim validation\n3. Modify `validate_claim_syntax()` to check references\n\nLet me look at the imports first.", - "status": "complete" - }, - { - "type": "TextBlock", - "text": "Now let's add the collection logic. First, check imports:", - "status": "complete" - }, - { - "type": "ReadFileBlock", - "call_id": "toolu_01DPUB7wC9AjCLu4hFC3oASQ", - "tool_name": "mcp_read_file", - "params": { - "end_line": 12, - "path": "validator/src/lib.rs", - "start_line": 1 - }, - "status": "complete", - "text": " 1│//! Worldview Validator - A validator for Worldview format files\n 2│//!\n 3│//! This crate provides validation for `.wvf` files according to the Worldview specification.\n 4│//! It checks structural correctness (hierarchy, indentation), claim syntax, brief forms,\n 5│//! modifiers, and evolution markers.\n 6│\n 7│use std::fmt;\n 8│use thiserror::Error;\n 9│\n 10│// Token definitions generated at compile time from spec/tokens.yaml\n 11│include!(concat!(env!(\"OUT_DIR\"), \"/tokens.rs\"));\n 12│\n", - "background": false - }, - { - "type": "ThinkingBlock", - "text": "I need to add `use std::collections::HashSet;` and then modify the `validate` function to collect concept.facet pairs and pass them to claim validation.", - "status": "complete" - }, - { - "type": "EditFileBlock", - "call_id": "toolu_018SMKBocrDGjvAcjKcsoYJd", - "tool_name": "mcp_edit_file", - "params": { - "edits": [ - { - "new_string": "use std::collections::HashSet;\nuse std::fmt;", - "old_string": "use std::fmt;" - } - ], - "path": "validator/src/lib.rs" - }, - "status": "complete", - "text": "Successfully applied 1 edit(s) to validator/src/lib.rs", - "background": false - }, - { - "type": "ThinkingBlock", - "text": "Now let me modify the `validate` function to collect valid references and pass them. Let me read the current validate function again.", - "status": "complete" - }, - { - "type": "TextBlock", - "text": "Now update the validate function to collect valid references and pass them to claim validation:", - "status": "complete" - }, - { - "type": "ReadFileBlock", - "call_id": "toolu_01MqU8X7i9o83jcwnjmWwKdM", - "tool_name": "mcp_read_file", - "params": { - "end_line": 225, - "path": "validator/src/lib.rs", - "start_line": 195 - }, - "status": "complete", - "text": " 195│ }\n 196│}\n 197│\n 198│/// Validates a Worldview document\n 199│pub fn validate(input: &str) -> ValidationResult {\n 200│ let mut errors = Vec::new();\n 201│ let mut warnings = Vec::new();\n 202│ let mut lines = Vec::new();\n 203│\n 204│ // First pass: tokenize lines\n 205│ for (idx, raw_line) in input.lines().enumerate() {\n 206│ let line_number = idx + 1;\n 207│ let parsed = tokenize_line(raw_line, line_number, &mut errors);\n 208│ lines.push(ParsedLine {\n 209│ line_number,\n 210│ line_type: parsed,\n 211│ raw: raw_line.to_string(),\n 212│ });\n 213│ }\n 214│\n 215│ // Second pass: validate structure\n 216│ validate_structure(&lines, &mut errors);\n 217│\n 218│ // Third pass: validate claim syntax including brief forms, modifiers, evolution\n 219│ for line in &lines {\n 220│ if let LineType::Claim(claim) = &line.line_type {\n 221│ validate_claim_syntax(line.line_number, claim, &mut errors, &mut warnings);\n 222│ }\n 223│ }\n 224│\n 225│ ValidationResult { errors, warnings, lines }\n", - "background": false - }, - { - "type": "ThinkingBlock", - "text": "I'll update validate() to:\n1. Collect valid Concept.facet pairs after tokenizing\n2. Pass them to validate_claim_syntax\n\nLet me write this edit.", - "status": "complete" - }, - { - "type": "EditFileBlock", - "call_id": "toolu_0181JdhB9CgRCvKZjFg9VhvW", - "tool_name": "mcp_edit_file", - "params": { - "edits": [ - { - "new_string": "/// Validates a Worldview document\npub fn validate(input: &str) -> ValidationResult {\n let mut errors = Vec::new();\n let mut warnings = Vec::new();\n let mut lines = Vec::new();\n\n // First pass: tokenize lines\n for (idx, raw_line) in input.lines().enumerate() {\n let line_number = idx + 1;\n let parsed = tokenize_line(raw_line, line_number, &mut errors);\n lines.push(ParsedLine {\n line_number,\n line_type: parsed,\n raw: raw_line.to_string(),\n });\n }\n\n // Collect valid Concept.facet pairs for reference validation\n let valid_refs = collect_valid_references(&lines);\n\n // Second pass: validate structure\n validate_structure(&lines, &mut errors);\n\n // Third pass: validate claim syntax including brief forms, modifiers, evolution\n for line in &lines {\n if let LineType::Claim(claim) = &line.line_type {\n validate_claim_syntax(line.line_number, claim, &valid_refs, &mut errors, &mut warnings);\n }\n }\n\n ValidationResult { errors, warnings, lines }", - "old_string": "/// Validates a Worldview document\npub fn validate(input: &str) -> ValidationResult {\n let mut errors = Vec::new();\n let mut warnings = Vec::new();\n let mut lines = Vec::new();\n\n // First pass: tokenize lines\n for (idx, raw_line) in input.lines().enumerate() {\n let line_number = idx + 1;\n let parsed = tokenize_line(raw_line, line_number, &mut errors);\n lines.push(ParsedLine {\n line_number,\n line_type: parsed,\n raw: raw_line.to_string(),\n });\n }\n\n // Second pass: validate structure\n validate_structure(&lines, &mut errors);\n\n // Third pass: validate claim syntax including brief forms, modifiers, evolution\n for line in &lines {\n if let LineType::Claim(claim) = &line.line_type {\n validate_claim_syntax(line.line_number, claim, &mut errors, &mut warnings);\n }\n }\n\n ValidationResult { errors, warnings, lines }" - } - ], - "path": "validator/src/lib.rs" - }, - "status": "complete", - "text": "Successfully applied 1 edit(s) to validator/src/lib.rs", - "background": false - }, - { - "type": "ThinkingBlock", - "text": "Now I need to add the `collect_valid_references` function and update `validate_claim_syntax` to accept and use the valid_refs set. Let me find where to add the collect function and update the claim validation function.", - "status": "complete" - }, - { - "type": "TextBlock", - "text": "Now add the collection function and update `validate_claim_syntax`. Let me find where `validate_claim_syntax` is:", - "status": "complete" - }, - { - "type": "ShellBlock", - "call_id": "toolu_01EZeBw8utsXS3VW5BLD9oaY", - "tool_name": "mcp_shell", - "params": { - "command": "grep -n \"fn validate_claim_syntax\" validator/src/lib.rs" - }, - "status": "complete", - "text": "630:fn validate_claim_syntax(\n", - "background": false - }, - { - "type": "ThinkingBlock", - "text": "Let me read the function and also see where to insert the collect_valid_references function.", - "status": "complete" - }, - { - "type": "ReadFileBlock", - "call_id": "toolu_018WTzhp6aGKnqGPk7KqNrEk", - "tool_name": "mcp_read_file", - "params": { - "end_line": 730, - "path": "validator/src/lib.rs", - "start_line": 625 - }, - "status": "complete", - "text": " 625│ }\n 626│ }\n 627│}\n 628│\n 629│/// Validate claim syntax including brief forms, modifiers, and evolution markers\n 630│fn validate_claim_syntax(\n 631│ line_number: usize,\n 632│ claim: &ClaimData,\n 633│ errors: &mut Vec,\n 634│ warnings: &mut Vec,\n 635│) {\n 636│ // Check for empty claim text\n 637│ if claim.text.is_empty() {\n 638│ errors.push(ValidationError::EmptyClaimText { line: line_number });\n 639│ }\n 640│\n 641│ // Check for empty conditions\n 642│ for cond in &claim.conditions {\n 643│ if cond.is_empty() {\n 644│ errors.push(ValidationError::EmptyCondition { line: line_number });\n 645│ }\n 646│ }\n 647│\n 648│ // Check for empty sources\n 649│ for src in &claim.sources {\n 650│ if src.is_empty() {\n 651│ errors.push(ValidationError::EmptySource { line: line_number });\n 652│ }\n 653│ }\n 654│\n 655│ // Check for empty references\n 656│ for reference in &claim.references {\n 657│ if reference.is_empty() {\n 658│ errors.push(ValidationError::EmptyReference { line: line_number });\n 659│ }\n 660│ }\n 661│\n 662│ // Validate reference format (should be Concept.facet)\n 663│ for reference in &claim.references {\n 664│ if !reference.is_empty() && !reference.contains('.') {\n 665│ errors.push(ValidationError::InvalidReferenceFormat {\n 666│ line: line_number,\n 667│ reference: reference.clone(),\n 668│ });\n 669│ }\n 670│ }\n 671│\n 672│ // Validate brief forms have operands\n 673│ for bf in &claim.brief_forms {\n 674│ if bf.left_operand.is_empty() {\n 675│ errors.push(ValidationError::BriefFormMissingLeftOperand {\n 676│ line: line_number,\n 677│ operator: bf.operator.clone(),\n 678│ });\n 679│ }\n 680│ if bf.right_operand.is_empty() {\n 681│ errors.push(ValidationError::BriefFormMissingRightOperand {\n 682│ line: line_number,\n 683│ operator: bf.operator.clone(),\n 684│ });\n 685│ }\n 686│ }\n 687│\n 688│ // Check for unclosed evolution markers in original text\n 689│ if claim.text.contains(\"[<=\") && !claim.text.contains(']') {\n 690│ errors.push(ValidationError::UnclosedEvolutionMarker { line: line_number });\n 691│ }\n 692│\n 693│ // Validate evolution marker content if present\n 694│ if let Some(ref evo) = claim.evolution {\n 695│ if evo.prior_belief.is_empty() {\n 696│ errors.push(ValidationError::EmptyEvolutionMarker { line: line_number });\n 697│ }\n 698│ }\n 699│\n 700│ // Check for standalone modifiers that appear at the start (warning, not error)\n 701│ // Space-separated modifiers that follow a term are valid (e.g., \"fast !\")\n 702│ let tokens: Vec<&str> = claim.text.split_whitespace().collect();\n 703│ for (i, token) in tokens.iter().enumerate() {\n 704│ if *token == \"^\" || *token == \"!\" || *token == \"?\" || *token == \"*\" {\n 705│ // Only warn if it's at the start (no preceding term) or follows an operator\n 706│ if i == 0 {\n 707│ warnings.push(ValidationError::StandaloneModifier {\n 708│ line: line_number,\n 709│ modifier: token.to_string(),\n 710│ });\n 711│ } else {\n 712│ let prev = tokens[i - 1];\n 713│ let is_after_operator = BRIEF_FORMS.iter().any(|(op, _)| prev.ends_with(op));\n 714│ if is_after_operator {\n 715│ warnings.push(ValidationError::StandaloneModifier {\n 716│ line: line_number,\n 717│ modifier: token.to_string(),\n 718│ });\n 719│ }\n 720│ }\n 721│ }\n 722│ }\n 723│}\n 724│\n 725│/// Validate a file by path\n 726│pub fn validate_file(path: &std::path::Path) -> Result {\n 727│ let content = std::fs::read_to_string(path)?;\n 728│ Ok(validate(&content))\n 729│}\n 730│\n", - "background": false - }, - { - "type": "ThinkingBlock", - "text": "Now I need to:\n1. Add the `valid_refs` parameter to `validate_claim_syntax`\n2. Add the check for undefined references after the format check\n3. Add the `collect_valid_references` function before `validate_claim_syntax`\n\nLet me do this in a couple of edits.", - "status": "complete" - }, - { - "type": "TextBlock", - "text": "Let me add the collection function and update validate_claim_syntax:", - "status": "complete" - }, - { - "type": "EditFileBlock", - "call_id": "toolu_014EvBDKRLhsa2MRpj5vpWhM", - "tool_name": "mcp_edit_file", - "params": { - "edits": [ - { - "new_string": " }\n}\n\n/// Collect all valid Concept.facet reference targets from the document\nfn collect_valid_references(lines: &[ParsedLine]) -> HashSet {\n let mut valid_refs = HashSet::new();\n let mut current_concept: Option = None;\n\n for line in lines {\n match &line.line_type {\n LineType::Concept(name) => {\n current_concept = Some(name.clone());\n }\n LineType::Facet(name) => {\n if let Some(ref concept) = current_concept {\n valid_refs.insert(format!(\"{}.{}\", concept, name));\n }\n }\n _ => {}\n }\n }\n\n valid_refs\n}\n\n/// Validate claim syntax including brief forms, modifiers, and evolution markers\nfn validate_claim_syntax(\n line_number: usize,\n claim: &ClaimData,\n valid_refs: &HashSet,\n errors: &mut Vec,\n warnings: &mut Vec,\n) {", - "old_string": " }\n}\n\n/// Validate claim syntax including brief forms, modifiers, and evolution markers\nfn validate_claim_syntax(\n line_number: usize,\n claim: &ClaimData,\n errors: &mut Vec,\n warnings: &mut Vec,\n) {" - } - ], - "path": "validator/src/lib.rs" - }, - "status": "complete", - "text": "Successfully applied 1 edit(s) to validator/src/lib.rs", - "background": false - }, - { - "type": "ThinkingBlock", - "text": "Now I need to add the actual check for undefined references after the format check. Let me find that section again.", - "status": "complete" - }, - { - "type": "TextBlock", - "text": "Now add the undefined reference check after the format validation:", - "status": "complete" - }, - { - "type": "ReadFileBlock", - "call_id": "toolu_015oA9mV81JDwxQjdyVjBXQ8", - "tool_name": "mcp_read_file", - "params": { - "end_line": 700, - "path": "validator/src/lib.rs", - "start_line": 680 - }, - "status": "complete", - "text": " 680│ if reference.is_empty() {\n 681│ errors.push(ValidationError::EmptyReference { line: line_number });\n 682│ }\n 683│ }\n 684│\n 685│ // Validate reference format (should be Concept.facet)\n 686│ for reference in &claim.references {\n 687│ if !reference.is_empty() && !reference.contains('.') {\n 688│ errors.push(ValidationError::InvalidReferenceFormat {\n 689│ line: line_number,\n 690│ reference: reference.clone(),\n 691│ });\n 692│ }\n 693│ }\n 694│\n 695│ // Validate brief forms have operands\n 696│ for bf in &claim.brief_forms {\n 697│ if bf.left_operand.is_empty() {\n 698│ errors.push(ValidationError::BriefFormMissingLeftOperand {\n 699│ line: line_number,\n 700│ operator: bf.operator.clone(),\n", - "background": false - }, - { - "type": "ThinkingBlock", - "text": "I need to add the undefined reference check after the format check. Let me add it.", - "status": "complete" - }, - { - "type": "EditFileBlock", - "call_id": "toolu_011LJekvSmtT9e8Ybvfe2XtE", - "tool_name": "mcp_edit_file", - "params": { - "edits": [ - { - "new_string": " // Validate reference format (should be Concept.facet)\n for reference in &claim.references {\n if !reference.is_empty() && !reference.contains('.') {\n errors.push(ValidationError::InvalidReferenceFormat {\n line: line_number,\n reference: reference.clone(),\n });\n }\n }\n\n // Validate references point to existing concept.facet pairs\n for reference in &claim.references {\n if !reference.is_empty() && reference.contains('.') && !valid_refs.contains(reference) {\n errors.push(ValidationError::UndefinedReference {\n line: line_number,\n reference: reference.clone(),\n });\n }\n }\n\n // Validate brief forms have operands", - "old_string": " // Validate reference format (should be Concept.facet)\n for reference in &claim.references {\n if !reference.is_empty() && !reference.contains('.') {\n errors.push(ValidationError::InvalidReferenceFormat {\n line: line_number,\n reference: reference.clone(),\n });\n }\n }\n\n // Validate brief forms have operands" - } - ], - "path": "validator/src/lib.rs" - }, - "status": "complete", - "text": "Successfully applied 1 edit(s) to validator/src/lib.rs", - "background": false - }, - { - "type": "ThinkingBlock", - "text": "Now let's run the tests to make sure everything compiles and existing tests pass, then we need to add a test for the new undefined reference validation.", - "status": "complete" - }, - { - "type": "TextBlock", - "text": "Let's test it:", - "status": "complete" - }, - { - "type": "ShellBlock", - "call_id": "toolu_014Fuh2hGp1y47Kuc2tvPt8R", - "tool_name": "mcp_shell", - "params": { - "command": "cd validator && cargo test 2>&1 | tail -25" - }, - "status": "complete", - "text": "test tests::test_modifier_notable ... ok\ntest tests::test_modifier_uncertain ... ok\ntest tests::test_orphan_facet ... ok\ntest tests::test_orphan_claim ... ok\ntest tests::test_space_separated_modifier_valid ... ok\ntest tests::test_unclosed_evolution_marker ... ok\ntest tests::test_standalone_modifier_warning ... ok\ntest tests::test_valid_minimal_document ... ok\ntest tests::test_expanded_document ... ok\n\nfailures:\n\n---- tests::test_claim_with_reference stdout ----\n\nthread 'tests::test_claim_with_reference' (422038389) panicked at src/lib.rs:884:9:\nExpected valid: [UndefinedReference { line: 3, reference: \"Trust.formation\" }]\nnote: run with `RUST_BACKTRACE=1` environment variable to display a backtrace\n\n\nfailures:\n tests::test_claim_with_reference\n\ntest result: FAILED. 32 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s\n\nerror: test failed, to rerun pass `--lib`\n", - "background": false - }, - { - "type": "ThinkingBlock", - "text": "The test is failing because it has a reference to `Trust.formation` but the Trust concept isn't defined in the test document. Let me look at that test case and fix it.", - "status": "complete" - }, - { - "type": "TextBlock", - "text": "Ha! The test caught a real issue - the test was using an undefined reference. Let me look at it:", - "status": "complete" - }, - { - "type": "ShellBlock", - "call_id": "toolu_016XDDxqyFSNspCnQKDfxAcn", - "tool_name": "mcp_shell", - "params": { - "command": "grep -n -A 15 \"test_claim_with_reference\" validator/src/lib.rs | head -20" - }, - "status": "complete", - "text": "878: fn test_claim_with_reference() {\n879- let input = r#\"Trust\n880- .erosion\n881- - asymmetric vs formation &Trust.formation\"#;\n882-\n883- let result = validate(input);\n884- assert!(result.is_valid(), \"Expected valid: {:?}\", result.errors);\n885-\n886- if let Some(line) = result.lines.iter().find(|l| matches!(l.line_type, LineType::Claim(_))) {\n887- if let LineType::Claim(claim) = &line.line_type {\n888- assert!(claim.references.contains(&\"Trust.formation\".to_string()));\n889- }\n890- }\n891- }\n892-\n893- #[test]\n", - "background": false - }, - { - "type": "ThinkingBlock", - "text": "The test references `Trust.formation` but the document only has `Trust.erosion`. I need to update the test to include the `.formation` facet so the reference is valid.", - "status": "complete" - }, - { - "type": "TextBlock", - "text": "The test references `Trust.formation` but only defines `Trust.erosion`. Need to add the facet:", - "status": "complete" - }, - { - "type": "EditFileBlock", - "call_id": "toolu_01NF2572KUapiy5bY5uazPZf", - "tool_name": "mcp_edit_file", - "params": { - "edits": [ - { - "new_string": " fn test_claim_with_reference() {\n let input = r#\"Trust\n .formation\n - slow\n .erosion\n - asymmetric vs formation &Trust.formation\"#;\n\n let result = validate(input);\n assert!(result.is_valid(), \"Expected valid: {:?}\", result.errors);", - "old_string": " fn test_claim_with_reference() {\n let input = r#\"Trust\n .erosion\n - asymmetric vs formation &Trust.formation\"#;\n\n let result = validate(input);\n assert!(result.is_valid(), \"Expected valid: {:?}\", result.errors);" - } - ], - "path": "validator/src/lib.rs" - }, - "status": "complete", - "text": "Successfully applied 1 edit(s) to validator/src/lib.rs", - "background": false - }, - { - "type": "ThinkingBlock", - "text": "Now let's also add a proper test for the undefined reference error.", - "status": "complete" - }, - { - "type": "TextBlock", - "text": "Now let's add a test for the undefined reference case and run tests:", - "status": "complete" - }, - { - "type": "ShellBlock", - "call_id": "toolu_01DshpFxngULHc1wdV5NvSDT", - "tool_name": "mcp_shell", - "params": { - "command": "grep -n \"test_invalid_reference_format\" validator/src/lib.rs" - }, - "status": "complete", - "text": "896: fn test_invalid_reference_format() {\n", - "background": false - }, - { - "type": "ThinkingBlock", - "text": "Let me add a test for undefined references after the invalid_reference_format test.", - "status": "complete" - }, - { - "type": "ReadFileBlock", - "call_id": "toolu_01RG3DJTxsKMSm4PFV1rrKkU", - "tool_name": "mcp_read_file", - "params": { - "end_line": 910, - "path": "validator/src/lib.rs", - "start_line": 896 - }, - "status": "complete", - "text": " 896│ fn test_invalid_reference_format() {\n 897│ let input = r#\"Power\n 898│ .core\n 899│ - corrupts &InvalidReference\"#;\n 900│ let result = validate(input);\n 901│ assert!(!result.is_valid());\n 902│ assert!(result.errors.iter().any(|e| matches!(e, ValidationError::InvalidReferenceFormat { .. })));\n 903│ }\n 904│\n 905│ // ==================== Brief form tests ====================\n 906│\n 907│ #[test]\n 908│ fn test_brief_form_causes() {\n 909│ let input = r#\"Power\n 910│ .nature\n", - "background": false - }, - { - "type": "ThinkingBlock", - "text": "Let me add a test for undefined references right after the invalid_reference_format test.", - "status": "complete" - }, - { - "type": "EditFileBlock", - "call_id": "toolu_01MKyxD5gLhmiqCEtYaxKoW8", - "tool_name": "mcp_edit_file", - "params": { - "edits": [ - { - "new_string": " fn test_invalid_reference_format() {\n let input = r#\"Power\n .core\n - corrupts &InvalidReference\"#;\n let result = validate(input);\n assert!(!result.is_valid());\n assert!(result.errors.iter().any(|e| matches!(e, ValidationError::InvalidReferenceFormat { .. })));\n }\n\n #[test]\n fn test_undefined_reference() {\n let input = r#\"Power\n .core\n - corrupts &Trust.formation\"#;\n let result = validate(input);\n assert!(!result.is_valid());\n assert!(result.errors.iter().any(|e| matches!(e, ValidationError::UndefinedReference { .. })));\n }\n\n // ==================== Brief form tests ====================", - "old_string": " fn test_invalid_reference_format() {\n let input = r#\"Power\n .core\n - corrupts &InvalidReference\"#;\n let result = validate(input);\n assert!(!result.is_valid());\n assert!(result.errors.iter().any(|e| matches!(e, ValidationError::InvalidReferenceFormat { .. })));\n }\n\n // ==================== Brief form tests ====================" - } - ], - "path": "validator/src/lib.rs" - }, - "status": "complete", - "text": "Successfully applied 1 edit(s) to validator/src/lib.rs", - "background": false - }, - { - "type": "ThinkingBlock", - "text": "Now let's run all the tests to make sure everything passes.", - "status": "complete" - }, - { - "type": "ShellBlock", - "call_id": "toolu_01EMJWj65WxQijwXaLibCRGe", - "tool_name": "mcp_shell", - "params": { - "command": "cd validator && cargo test 2>&1 | tail -15" - }, - "status": "complete", - "text": "failures:\n\n---- tests::test_claim_with_reference stdout ----\n\nthread 'tests::test_claim_with_reference' (422039443) panicked at src/lib.rs:890:17:\nassertion failed: claim.references.contains(&\"Trust.formation\".to_string())\nnote: run with `RUST_BACKTRACE=1` environment variable to display a backtrace\n\n\nfailures:\n tests::test_claim_with_reference\n\ntest result: FAILED. 33 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s\n\nerror: test failed, to rerun pass `--lib`\n", - "background": false - }, - { - "type": "ThinkingBlock", - "text": "Hmm the test is looking for the reference on a line but now it's on a different line (the 5th line instead of the 3rd). Let me check what's happening.", - "status": "complete" - }, - { - "type": "TextBlock", - "text": "The assertion is finding the wrong claim line now. Let me fix the test to look at the right line:", - "status": "complete" - }, - { - "type": "ReadFileBlock", - "call_id": "toolu_01Pnj22NgWExfsCDCnFe1PDE", - "tool_name": "mcp_read_file", - "params": { - "end_line": 905, - "path": "validator/src/lib.rs", - "start_line": 878 - }, - "status": "complete", - "text": " 878│ fn test_claim_with_reference() {\n 879│ let input = r#\"Trust\n 880│ .formation\n 881│ - slow\n 882│ .erosion\n 883│ - asymmetric vs formation &Trust.formation\"#;\n 884│\n 885│ let result = validate(input);\n 886│ assert!(result.is_valid(), \"Expected valid: {:?}\", result.errors);\n 887│\n 888│ if let Some(line) = result.lines.iter().find(|l| matches!(l.line_type, LineType::Claim(_))) {\n 889│ if let LineType::Claim(claim) = &line.line_type {\n 890│ assert!(claim.references.contains(&\"Trust.formation\".to_string()));\n 891│ }\n 892│ }\n 893│ }\n 894│\n 895│ #[test]\n 896│ fn test_invalid_reference_format() {\n 897│ let input = r#\"Power\n 898│ .core\n 899│ - corrupts &InvalidReference\"#;\n 900│ let result = validate(input);\n 901│ assert!(!result.is_valid());\n 902│ assert!(result.errors.iter().any(|e| matches!(e, ValidationError::InvalidReferenceFormat { .. })));\n 903│ }\n 904│\n 905│ #[test]\n", - "background": false - }, - { - "type": "ThinkingBlock", - "text": "The test finds the first claim line which is \"slow\" and doesn't have any references. I need to find the claim that actually has the reference. Let me fix the test.", - "status": "complete" - }, - { - "type": "EditFileBlock", - "call_id": "toolu_01KJux7h7JbLghzvcQmRyWQC", - "tool_name": "mcp_edit_file", - "params": { - "edits": [ - { - "new_string": " fn test_claim_with_reference() {\n let input = r#\"Trust\n .formation\n - slow\n .erosion\n - asymmetric vs formation &Trust.formation\"#;\n\n let result = validate(input);\n assert!(result.is_valid(), \"Expected valid: {:?}\", result.errors);\n\n // Find the claim with the reference (second claim)\n let claims: Vec<_> = result.lines.iter()\n .filter_map(|l| match &l.line_type {\n LineType::Claim(c) => Some(c),\n _ => None,\n })\n .collect();\n \n assert!(claims.len() >= 2);\n assert!(claims[1].references.contains(&\"Trust.formation\".to_string()));\n }", - "old_string": " fn test_claim_with_reference() {\n let input = r#\"Trust\n .formation\n - slow\n .erosion\n - asymmetric vs formation &Trust.formation\"#;\n\n let result = validate(input);\n assert!(result.is_valid(), \"Expected valid: {:?}\", result.errors);\n\n if let Some(line) = result.lines.iter().find(|l| matches!(l.line_type, LineType::Claim(_))) {\n if let LineType::Claim(claim) = &line.line_type {\n assert!(claim.references.contains(&\"Trust.formation\".to_string()));\n }\n }\n }" - } - ], - "path": "validator/src/lib.rs" - }, - "status": "complete", - "text": "Successfully applied 1 edit(s) to validator/src/lib.rs", - "background": false - }, - { - "type": "ThinkingBlock", - "text": "Now let's run the tests again.", - "status": "complete" - }, - { - "type": "ShellBlock", - "call_id": "toolu_01BYhyqXZzEVzRWmZmBXEq8S", - "tool_name": "mcp_shell", - "params": { - "command": "cd validator && cargo test 2>&1 | tail -10" - }, - "status": "complete", - "text": "running 0 tests\n\ntest result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s\n\n Doc-tests worldview_validator\n\nrunning 0 tests\n\ntest result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s\n\n", - "background": false - }, - { - "type": "ThinkingBlock", - "text": "Hmm, that only shows doc-tests. Let me run the full test output.", - "status": "complete" - }, - { - "type": "ShellBlock", - "call_id": "toolu_01Mb1fXFgWgDSLNkvntha2HD", - "tool_name": "mcp_shell", - "params": { - "command": "cd validator && cargo test 2>&1" - }, - "status": "complete", - "text": " Finished `test` profile [unoptimized + debuginfo] target(s) in 0.01s\n Running unittests src/lib.rs (target/debug/deps/worldview_validator-83762e759e1950e7)\n\nrunning 34 tests\ntest tests::test_brief_form_caused_by ... ok\ntest tests::test_blank_lines_allowed ... ok\ntest tests::test_brief_form_causes ... ok\ntest tests::test_brief_form_contrast ... ok\ntest tests::test_brief_form_missing_left_operand ... ok\ntest tests::test_brief_form_regardless ... ok\ntest tests::test_brief_form_mutual ... ok\ntest tests::test_brief_form_missing_right_operand ... ok\ntest tests::test_brief_form_similar ... ok\ntest tests::test_brief_form_tension ... ok\ntest tests::test_claim_with_conditions_and_sources ... ok\ntest tests::test_claim_with_reference ... ok\ntest tests::test_concept_without_facet ... ok\ntest tests::test_empty_evolution_marker ... ok\ntest tests::test_evolution_marker ... ok\ntest tests::test_evolution_marker_rationalizes ... ok\ntest tests::test_facet_without_claim ... ok\ntest tests::test_invalid_indentation ... ok\ntest tests::test_invalid_reference_format ... ok\ntest tests::test_missing_claim_prefix ... ok\ntest tests::test_missing_facet_prefix ... ok\ntest tests::test_modifier_emphatic ... ok\ntest tests::test_modifier_notable ... ok\ntest tests::test_modifier_uncertain ... ok\ntest tests::test_orphan_claim ... ok\ntest tests::test_modifier_increasing ... ok\ntest tests::test_orphan_facet ... ok\ntest tests::test_space_separated_modifier_valid ... ok\ntest tests::test_unclosed_evolution_marker ... ok\ntest tests::test_standalone_modifier_warning ... ok\ntest tests::test_modifier_decreasing ... ok\ntest tests::test_expanded_document ... ok\ntest tests::test_valid_minimal_document ... ok\ntest tests::test_undefined_reference ... ok\n\ntest result: ok. 34 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s\n\n Running unittests src/main.rs (target/debug/deps/worldview_validate-56487114d7d41bd3)\n\nrunning 0 tests\n\ntest result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s\n\n Doc-tests worldview_validator\n\nrunning 0 tests\n\ntest result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s\n\n", - "background": false - }, - { - "type": "ThinkingBlock", - "text": "All 34 tests pass, including the new `test_undefined_reference` test. Let me also verify the example.wvf file still validates correctly since it has references.", - "status": "complete" - }, - { - "type": "TextBlock", - "text": "All 34 tests pass. Let's also make sure the example.wvf file still validates (it has cross-references):", - "status": "complete" - }, - { - "type": "ShellBlock", - "call_id": "toolu_013PhWH648Shi7EEU8aBTWqT", - "tool_name": "mcp_shell", - "params": { - "command": "cd validator && cargo run -- ../example.wvf 2>&1" - }, - "status": "complete", - "text": " Compiling worldview-validator v0.1.0 (/Users/tcdent/Work/wvf/validator)\n Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.11s\n Running `target/debug/worldview-validate ../example.wvf`\nValid Worldview document\n", - "background": false - }, - { - "type": "ThinkingBlock", - "text": "The example file validates correctly. All the references in it are valid (like `&Trust.institutional`, `&Trust.formation`, `&Human-nature.cognition`).\n\nThis was only about 30 lines of actual logic - not much work at all. Let me commit this.", - "status": "complete" - }, - { - "type": "TextBlock", - "text": "All references in example.wvf check out. Let's commit:", - "status": "complete" - }, - { - "type": "ShellBlock", - "call_id": "toolu_017TEpJiQ7mFA2dyHRpTxxNi", - "tool_name": "mcp_shell", - "params": { - "command": "git add -A && git commit -m \"Add validation for undefined references\n\nReferences like &Concept.facet now validated to ensure the target\nconcept and facet actually exist in the document.\n\n- Added UndefinedReference error variant\n- collect_valid_references() builds set of valid targets\n- validate_claim_syntax() checks references against the set\n- Added test_undefined_reference test case\n- Fixed test_claim_with_reference to include referenced facet\"" - }, - "status": "complete", - "text": "[main 5113067] Add validation for undefined references\n 2 files changed, 640 insertions(+), 8 deletions(-)\n", - "background": false - }, - { - "type": "ThinkingBlock", - "text": "Done. That was about 30 lines of actual code as I predicted. The tests went from 33 to 34.\n\nWhat else should we look at? Some options:\n1. Python evals - missing pyproject.toml \n2. The claim parsing in lib.rs is a bit complex\n3. The agent could have better error handling\n\nLet me ask if they want to continue or have something specific in mind.", - "status": "complete" - }, - { - "type": "TextBlock", - "text": "Done! ~30 lines of logic as predicted. The validator now catches dangling references.\n\nWhat's next? I noticed:\n1. **Python evals** - no `pyproject.toml`, still using `requirements.txt`\n2. **Claim parsing** - `parse_claim()` is doing a lot in one function \n3. **Agent error handling** - no timeouts or graceful API failure handling\n\nOr is there something else you're curious about?", - "status": "complete" - } - ], - "timestamp": "2026-01-21T23:12:03.802806Z" - }, - { - "id": 9, - "role": "user", - "content": [ - { - "type": "TextBlock", - "text": "haha awesome let's add a case to validate it in the other direction, too", - "status": "complete" - } - ], - "timestamp": "2026-01-21T23:14:00.508949Z" - }, - { - "id": 10, - "role": "assistant", - "content": [], - "timestamp": "2026-01-21T23:15:03.592849Z" - }, - { - "id": 11, - "role": "user", - "content": [ - { - "type": "TextBlock", - "text": "yeah i like the idea of moving to pyproject.toml", - "status": "complete" - } - ], - "timestamp": "2026-01-21T23:15:44.439396Z" - }, - { - "id": 12, - "role": "assistant", - "content": [ - { - "type": "ThinkingBlock", - "text": "The user wants to:\n1. Add a test case for valid references (the \"other direction\" - making sure valid references pass)\n2. Move the Python evals from requirements.txt to pyproject.toml\n\nLet me do both.", - "status": "complete" - }, - { - "type": "TextBlock", - "text": "Good call - we should test that valid cross-references pass, not just that invalid ones fail. Let me add that test and then modernize the Python packaging:", - "status": "complete" - }, - { - "type": "ReadFileBlock", - "call_id": "toolu_01M9xnn2vQZcbB1d7opRTDX3", - "tool_name": "mcp_read_file", - "params": { - "end_line": 925, - "path": "validator/src/lib.rs", - "start_line": 905 - }, - "status": "complete", - "text": " 905│ let result = validate(input);\n 906│ assert!(!result.is_valid());\n 907│ assert!(result.errors.iter().any(|e| matches!(e, ValidationError::InvalidReferenceFormat { .. })));\n 908│ }\n 909│\n 910│ #[test]\n 911│ fn test_undefined_reference() {\n 912│ let input = r#\"Power\n 913│ .core\n 914│ - corrupts &Trust.formation\"#;\n 915│ let result = validate(input);\n 916│ assert!(!result.is_valid());\n 917│ assert!(result.errors.iter().any(|e| matches!(e, ValidationError::UndefinedReference { .. })));\n 918│ }\n 919│\n 920│ // ==================== Brief form tests ====================\n 921│\n 922│ #[test]\n 923│ fn test_brief_form_causes() {\n 924│ let input = r#\"Power\n 925│ .nature\n", - "background": false - }, - { - "type": "ThinkingBlock", - "text": "Let me add a test for valid cross-references between concepts.", - "status": "complete" - }, - { - "type": "EditFileBlock", - "call_id": "toolu_01NksEJi2rirAR78n4Tz5e46", - "tool_name": "mcp_edit_file", - "params": { - "edits": [ - { - "new_string": " #[test]\n fn test_undefined_reference() {\n let input = r#\"Power\n .core\n - corrupts &Trust.formation\"#;\n let result = validate(input);\n assert!(!result.is_valid());\n assert!(result.errors.iter().any(|e| matches!(e, ValidationError::UndefinedReference { .. })));\n }\n\n #[test]\n fn test_valid_cross_reference() {\n // References between different concepts should work\n let input = r#\"Power\n .institutional\n - accountability <> trust &Trust.institutional\n\nTrust\n .institutional\n - possible | high transparency\"#;\n let result = validate(input);\n assert!(result.is_valid(), \"Expected valid: {:?}\", result.errors);\n }\n\n // ==================== Brief form tests ====================", - "old_string": " #[test]\n fn test_undefined_reference() {\n let input = r#\"Power\n .core\n - corrupts &Trust.formation\"#;\n let result = validate(input);\n assert!(!result.is_valid());\n assert!(result.errors.iter().any(|e| matches!(e, ValidationError::UndefinedReference { .. })));\n }\n\n // ==================== Brief form tests ====================" - } - ], - "path": "validator/src/lib.rs" - }, - "status": "complete", - "text": "Successfully applied 1 edit(s) to validator/src/lib.rs", - "background": false - }, - { - "type": "ThinkingBlock", - "text": "Now let me run the tests to make sure it passes.", - "status": "complete" - }, - { - "type": "ShellBlock", - "call_id": "toolu_01KnPbJ8X2BExcpDsPuewNuT", - "tool_name": "mcp_shell", - "params": { - "command": "cd validator && cargo test test_valid_cross_reference 2>&1" - }, - "status": "complete", - "text": " Compiling worldview-validator v0.1.0 (/Users/tcdent/Work/wvf/validator)\n Finished `test` profile [unoptimized + debuginfo] target(s) in 0.21s\n Running unittests src/lib.rs (target/debug/deps/worldview_validator-83762e759e1950e7)\n\nrunning 1 test\ntest tests::test_valid_cross_reference ... ok\n\ntest result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 34 filtered out; finished in 0.00s\n\n Running unittests src/main.rs (target/debug/deps/worldview_validate-56487114d7d41bd3)\n\nrunning 0 tests\n\ntest result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s\n\n", - "background": false - }, - { - "type": "ThinkingBlock", - "text": "Now let me look at the requirements.txt and create a pyproject.toml.", - "status": "complete" - }, - { - "type": "TextBlock", - "text": "Now let's check the Python setup and create a proper pyproject.toml:", - "status": "complete" - }, - { - "type": "ReadFileBlock", - "call_id": "toolu_01Agx9124yKfGCjuoHnvAJxz", - "tool_name": "mcp_read_file", - "params": { - "path": "evals/requirements.txt" - }, - "status": "complete", - "text": " 1│# WSL Evaluation Framework Dependencies\n 2│\n 3│# LiteLLM for unified LLM access\n 4│litellm>=1.30.0\n 5│\n 6│# These are optional but recommended for direct provider access\n 7│# (LiteLLM will use them if installed)\n 8│anthropic>=0.25.0\n 9│openai>=1.12.0\n", - "background": false - }, - { - "type": "ShellBlock", - "call_id": "toolu_01MoCbYZS3Vs5eLQhRj8y9jn", - "tool_name": "mcp_shell", - "params": { - "command": "ls -la evals/" - }, - "status": "complete", - "text": "total 48\ndrwxr-xr-x@ 9 tcdent staff 288 Jan 21 10:11 .\ndrwxr-xr-x@ 14 tcdent staff 448 Jan 21 15:02 ..\n-rw-r--r--@ 1 tcdent staff 360 Jan 21 10:11 __init__.py\n-rw-r--r--@ 1 tcdent staff 101 Jan 21 10:11 __main__.py\n-rw-r--r--@ 1 tcdent staff 11372 Jan 21 10:11 cli.py\ndrwxr-xr-x@ 5 tcdent staff 160 Jan 21 10:11 common\ndrwxr-xr-x@ 7 tcdent staff 224 Jan 21 10:11 read_eval\n-rw-r--r--@ 1 tcdent staff 227 Jan 21 10:11 requirements.txt\ndrwxr-xr-x@ 6 tcdent staff 192 Jan 21 10:11 write_eval\n", - "background": false - } - ], - "timestamp": "2026-01-21T23:15:44.442151Z" - }, - { - "id": 13, - "role": "user", - "content": [ - { - "type": "TextBlock", - "text": "actuall --amend that last test to the previous commit first, then do the py stuff", - "status": "complete" - } - ], - "timestamp": "2026-01-21T23:16:25.755930Z" - }, - { - "id": 14, - "role": "assistant", - "content": [ - { - "type": "ThinkingBlock", - "text": "Good call - the test should be part of the reference validation commit since it's testing that feature.", - "status": "complete" - }, - { - "type": "ShellBlock", - "call_id": "toolu_0145Wu46Z7LbMss6NB49E9ky", - "tool_name": "mcp_shell", - "params": { - "command": "git add -A && git commit --amend -m \"Add validation for undefined references\n\nReferences like &Concept.facet now validated to ensure the target\nconcept and facet actually exist in the document.\n\n- Added UndefinedReference error variant\n- collect_valid_references() builds set of valid targets\n- validate_claim_syntax() checks references against the set\n- Added test_undefined_reference test case\n- Added test_valid_cross_reference test case\n- Fixed test_claim_with_reference to include referenced facet\"" - }, - "status": "complete", - "text": "[main 952b32a] Add validation for undefined references\n Date: Wed Jan 21 15:14:56 2026 -0800\n 2 files changed, 817 insertions(+), 8 deletions(-)\n", - "background": false - }, - { - "type": "ThinkingBlock", - "text": "Good, now let's create the pyproject.toml for the evals package.", - "status": "complete" - }, - { - "type": "TextBlock", - "text": "Now the pyproject.toml:", - "status": "complete" - }, - { - "type": "WriteFileBlock", - "call_id": "toolu_01Wbs2QtJb1NVnc8R6VJGoov", - "tool_name": "mcp_write_file", - "params": { - "content": "[project]\nname = \"worldview-evals\"\nversion = \"0.1.0\"\ndescription = \"Evaluation framework for Worldview format LLM integration\"\nreadme = \"../README.md\"\nrequires-python = \">=3.11\"\nlicense = \"MIT\"\ndependencies = [\n \"litellm>=1.30.0\",\n \"anthropic>=0.25.0\",\n \"openai>=1.12.0\",\n]\n\n[project.scripts]\nworldview-evals = \"evals.cli:main\"\n\n[build-system]\nrequires = [\"hatchling\"]\nbuild-backend = \"hatchling.build\"\n\n[tool.hatch.build.targets.wheel]\npackages = [\".\"]\n\n[tool.uv]\ndev-dependencies = [\n \"pytest>=8.0\",\n]\n", - "path": "evals/pyproject.toml" - }, - "status": "complete", - "text": "Created file: evals/pyproject.toml (27 lines, 516 bytes)", - "background": false - }, - { - "type": "ThinkingBlock", - "text": "Now let's test that it works with uv, then remove the old requirements.txt.", - "status": "complete" - }, - { - "type": "ShellBlock", - "call_id": "toolu_01Lj9sZyQ6syKq9hEm34FWsM", - "tool_name": "mcp_shell", - "params": { - "command": "cd evals && uv sync 2>&1 | tail -20" - }, - "status": "complete", - "text": " + pygments==2.19.2\n + pytest==9.0.2\n + python-dotenv==1.2.1\n + pyyaml==6.0.3\n + referencing==0.37.0\n + regex==2026.1.15\n + requests==2.32.5\n + rpds-py==0.30.0\n + shellingham==1.5.4\n + sniffio==1.3.1\n + tiktoken==0.12.0\n + tokenizers==0.22.2\n + tqdm==4.67.1\n + typer-slim==0.21.1\n + typing-extensions==4.15.0\n + typing-inspection==0.4.2\n + urllib3==2.6.3\n + worldview-evals==0.1.0 (from file:///Users/tcdent/Work/wvf/evals)\n + yarl==1.22.0\n + zipp==3.23.0\n", - "background": false - }, - { - "type": "ThinkingBlock", - "text": "Looks like uv successfully synced. Let's test running the module.", - "status": "complete" - }, - { - "type": "ShellBlock", - "call_id": "toolu_01Q5Er2hFuxD1vxpL3NGkeUs", - "tool_name": "mcp_shell", - "params": { - "command": "cd evals && uv run python -m evals list-models 2>&1 | head -20" - }, - "status": "complete", - "text": "/Users/tcdent/Work/wvf/evals/.venv/bin/python3: No module named evals\n", - "background": false - }, - { - "type": "ThinkingBlock", - "text": "The pyproject.toml is in the evals directory, so the package structure is a bit odd. The issue is that we're in evals/ trying to import evals as a module. Let me check the package structure.\n\nThe issue is that pyproject.toml is inside evals/, but the package name is \"worldview-evals\" and it's trying to build from that directory. The standard approach would be to either:\n1. Put pyproject.toml at the repo root and reference evals/ as a package\n2. Or restructure to have evals/src/worldview_evals/...\n\nActually looking at the structure, the simpler fix is to put pyproject.toml at the project root and just include evals as a package. That's cleaner anyway.", - "status": "complete" - }, - { - "type": "TextBlock", - "text": "Ah, the package structure is awkward with pyproject.toml inside `evals/`. Let me put it at the project root instead - cleaner:", - "status": "complete" - }, - { - "type": "ShellBlock", - "call_id": "toolu_01Tfk44mafnwbQBbnutYVenU", - "tool_name": "mcp_shell", - "params": { - "command": "rm evals/pyproject.toml" - }, - "status": "complete", - "text": "(no output)", - "background": false - }, - { - "type": "WriteFileBlock", - "call_id": "toolu_01A1sE2oDoxZMyQKiy1Jaea8", - "tool_name": "mcp_write_file", - "params": { - "content": "[project]\nname = \"worldview-evals\"\nversion = \"0.1.0\"\ndescription = \"Evaluation framework for Worldview format LLM integration\"\nreadme = \"README.md\"\nrequires-python = \">=3.11\"\nlicense = \"MIT\"\ndependencies = [\n \"litellm>=1.30.0\",\n \"anthropic>=0.25.0\",\n \"openai>=1.12.0\",\n]\n\n[project.scripts]\nworldview-evals = \"evals.cli:main\"\n\n[build-system]\nrequires = [\"hatchling\"]\nbuild-backend = \"hatchling.build\"\n\n[tool.hatch.build.targets.wheel]\npackages = [\"evals\"]\n\n[tool.uv]\ndev-dependencies = [\n \"pytest>=8.0\",\n]\n", - "path": "pyproject.toml" - }, - "status": "complete", - "text": "Created file: pyproject.toml (27 lines, 517 bytes)", - "background": false - }, - { - "type": "ThinkingBlock", - "text": "Now let me remove the old venv and try again from the project root.", - "status": "complete" - }, - { - "type": "ShellBlock", - "call_id": "toolu_01WakJpk4obvhCjTcZ8iA1Cq", - "tool_name": "mcp_shell", - "params": { - "command": "rm -rf evals/.venv && uv sync 2>&1 | tail -10" - }, - "status": "complete", - "text": " + tiktoken==0.12.0\n + tokenizers==0.22.2\n + tqdm==4.67.1\n + typer-slim==0.21.1\n + typing-extensions==4.15.0\n + typing-inspection==0.4.2\n + urllib3==2.6.3\n + worldview-evals==0.1.0 (from file:///Users/tcdent/Work/wvf)\n + yarl==1.22.0\n + zipp==3.23.0\n", - "background": false - }, - { - "type": "ThinkingBlock", - "text": "Now let's test running the module.", - "status": "complete" - }, - { - "type": "ShellBlock", - "call_id": "toolu_01F36pyKjYs98oHZcjj8RPcn", - "tool_name": "mcp_shell", - "params": { - "command": "uv run python -m evals list-models 2>&1 | head -15" - }, - "status": "complete", - "text": "Read Evaluation Models (for testing LLM response to Worldview context):\n\nName Provider Model ID Default\n--------------------------------------------------------------------------------\nClaude Sonnet 4 anthropic claude-sonnet-4-20250514 Yes\nClaude Opus 4.5 anthropic claude-opus-4-5-20251101 \nClaude Haiku 4.5 anthropic claude-haiku-4-5-20251001 \nGPT-5.2 openai gpt-5.2 Yes\nGPT-5 Mini openai gpt-5-mini \n\n\nWrite Evaluation Models (for testing Worldview document generation):\n\nName Model ID Default\n----------------------------------------------------------------------\n", - "background": false - }, - { - "type": "ThinkingBlock", - "text": "Now let's remove the old requirements.txt and update .gitignore to ignore .venv and uv.lock.", - "status": "complete" - }, - { - "type": "TextBlock", - "text": "Now let's remove the old requirements.txt and update the README:", - "status": "complete" - }, - { - "type": "ShellBlock", - "call_id": "toolu_015pxMC3BVZjL1S59iDBiqcA", - "tool_name": "mcp_shell", - "params": { - "command": "rm evals/requirements.txt" - }, - "status": "complete", - "text": "(no output)", - "background": false - }, - { - "type": "ReadFileBlock", - "call_id": "toolu_015uicEp1WYBWVi8D8G5BEHM", - "tool_name": "mcp_read_file", - "params": { - "end_line": 160, - "path": "README.md", - "start_line": 138 - }, - "status": "complete", - "text": " 138│### Evaluation Framework\n 139│\n 140│A Python framework for testing how well LLMs can leverage Worldview-encoded beliefs.\n 141│\n 142│```bash\n 143│# Install dependencies\n 144│pip install -r evals/requirements.txt\n 145│\n 146│# Run evaluations\n 147│python -m evals run --models claude-sonnet gpt-5.2\n 148│\n 149│# Run specific difficulty\n 150│python -m evals run --difficulty extreme\n 151│\n 152│# List available models and test cases\n 153│python -m evals list-models\n 154│python -m evals list-cases\n 155│```\n 156│\n 157│## Project Structure\n 158│\n 159│```\n 160│wvf/\n", - "background": false - }, - { - "type": "ThinkingBlock", - "text": "Let me update the README to use uv instead of pip.", - "status": "complete" - }, - { - "type": "EditFileBlock", - "call_id": "toolu_015ZFnmEgeCc9dXH6dbptbyZ", - "tool_name": "mcp_edit_file", - "params": { - "edits": [ - { - "new_string": "### Evaluation Framework\n\nA Python framework for testing how well LLMs can leverage Worldview-encoded beliefs.\n\n```bash\n# Install dependencies\nuv sync\n\n# Run evaluations\nuv run python -m evals run --models claude-sonnet gpt-5.2\n\n# Run specific difficulty\nuv run python -m evals run --difficulty extreme\n\n# List available models and test cases\nuv run python -m evals list-models\nuv run python -m evals list-cases\n```", - "old_string": "### Evaluation Framework\n\nA Python framework for testing how well LLMs can leverage Worldview-encoded beliefs.\n\n```bash\n# Install dependencies\npip install -r evals/requirements.txt\n\n# Run evaluations\npython -m evals run --models claude-sonnet gpt-5.2\n\n# Run specific difficulty\npython -m evals run --difficulty extreme\n\n# List available models and test cases\npython -m evals list-models\npython -m evals list-cases\n```" - } - ], - "path": "README.md" - }, - "status": "complete", - "text": "Successfully applied 1 edit(s) to README.md", - "background": false - }, - { - "type": "ThinkingBlock", - "text": "Let me also check if there's a .gitignore and if we need to add entries for uv.", - "status": "complete" - }, - { - "type": "ReadFileBlock", - "call_id": "toolu_015q4jTTUPafPrCPyiwJNA33", - "tool_name": "mcp_read_file", - "params": { - "path": ".gitignore" - }, - "status": "complete", - "text": " 1│# Python\n 2│__pycache__/\n 3│*.py[cod]\n 4│*$py.class\n 5│*.so\n 6│.Python\n 7│build/\n 8│develop-eggs/\n 9│dist/\n 10│downloads/\n 11│eggs/\n 12│.eggs/\n 13│lib/\n 14│lib64/\n 15│parts/\n 16│sdist/\n 17│var/\n 18│wheels/\n 19│*.egg-info/\n 20│.installed.cfg\n 21│*.egg\n 22│\n 23│# Virtual environments\n 24│.env\n 25│.venv\n 26│env/\n 27│venv/\n 28│ENV/\n 29│\n 30│# IDE\n 31│.idea/\n 32│.vscode/\n 33│*.swp\n 34│*.swo\n 35│*~\n 36│\n 37│# Testing\n 38│.pytest_cache/\n 39│.coverage\n 40│htmlcov/\n 41│\n 42│# Evaluation results (local)\n 43│results/\n 44│*.log\n 45│\n 46│# Generated files (created at compile-time)\n 47│validator/src/tokens_generated.rs\n", - "background": false - }, - { - "type": "ThinkingBlock", - "text": ".gitignore already covers .venv. I should add uv.lock since that's good to commit (lockfiles ensure reproducible builds). Actually, let me double check - uv.lock should probably be committed for reproducibility. Let me check if it's there.", - "status": "complete" - }, - { - "type": "ShellBlock", - "call_id": "toolu_01DiS8LewDxMiaZAN91oSXAT", - "tool_name": "mcp_shell", - "params": { - "command": "ls -la *.lock 2>/dev/null || echo \"no lock files\"" - }, - "status": "complete", - "text": "-rw-r--r--@ 1 tcdent staff 364653 Jan 21 15:17 uv.lock\n", - "background": false - }, - { - "type": "ThinkingBlock", - "text": "Good, uv.lock is there. Lock files should be committed for reproducibility. Let me commit everything.", - "status": "complete" - }, - { - "type": "TextBlock", - "text": "Good - uv.lock should be committed for reproducibility. Let's commit:", - "status": "complete" - }, - { - "type": "ShellBlock", - "call_id": "toolu_01NUwNj5PZrgcGBJ3unkGDdW", - "tool_name": "mcp_shell", - "params": { - "command": "git add -A && git status" - }, - "status": "complete", - "text": "On branch main\nYour branch is ahead of 'origin/main' by 2 commits.\n (use \"git push\" to publish your local commits)\n\nChanges to be committed:\n (use \"git restore --staged ...\" to unstage)\n\tmodified: .codey/transcripts/000000.json\n\tmodified: README.md\n\tdeleted: evals/requirements.txt\n\tnew file: evals/uv.lock\n\tnew file: pyproject.toml\n\tnew file: uv.lock\n\n", - "background": false - }, - { - "type": "ThinkingBlock", - "text": "Wait, there's an `evals/uv.lock` file - that's from when I accidentally ran `uv sync` in the evals dir with the first pyproject.toml. Let me remove that.", - "status": "complete" - }, - { - "type": "ShellBlock", - "call_id": "toolu_01JH9PRbMC266PGHrhRuqB4t", - "tool_name": "mcp_shell", - "params": { - "command": "rm evals/uv.lock && git add -A && git status" - }, - "status": "complete", - "text": "On branch main\nYour branch is ahead of 'origin/main' by 2 commits.\n (use \"git push\" to publish your local commits)\n\nChanges to be committed:\n (use \"git restore --staged ...\" to unstage)\n\tmodified: .codey/transcripts/000000.json\n\tmodified: README.md\n\tdeleted: evals/requirements.txt\n\tnew file: pyproject.toml\n\tnew file: uv.lock\n\n", - "background": false - }, - { - "type": "ThinkingBlock", - "text": "Good, now let me commit this.", - "status": "complete" - }, - { - "type": "ShellBlock", - "call_id": "toolu_01AHBzWMadT3qW5dejwCotyM", - "tool_name": "mcp_shell", - "params": { - "command": "git commit -m \"Migrate Python evals from requirements.txt to pyproject.toml\n\n- Added pyproject.toml at project root with uv support\n- Updated README to use uv commands\n- Removed evals/requirements.txt\n- Added uv.lock for reproducible builds\"" - }, - "status": "complete", - "text": "[main b877337] Migrate Python evals from requirements.txt to pyproject.toml\n 5 files changed, 2119 insertions(+), 14 deletions(-)\n delete mode 100644 evals/requirements.txt\n create mode 100644 pyproject.toml\n create mode 100644 uv.lock\n", - "background": false - }, - { - "type": "ThinkingBlock", - "text": "Done. Now let me summarize what we've done and ask what's next.", - "status": "complete" - }, - { - "type": "TextBlock", - "text": "Done! Three commits so far:\n\n1. **Replace hand-rolled YAML parser with serde_yaml** - build.rs went from 129 → 67 lines\n2. **Add validation for undefined references** - `&Concept.facet` now checked against actual document\n3. **Migrate Python evals to pyproject.toml** - modern uv-based tooling\n\nWhat's next? Still on my radar:\n- **Claim parsing** - `parse_claim()` is a chunky function doing several things\n- **Agent error handling** - no timeouts or retry on API failures \n- **Duplicate detection** - no check for duplicate concept/facet names\n\nOr something else catching your eye?", - "status": "complete" - } - ], - "timestamp": "2026-01-21T23:16:25.759134Z" - }, - { - "id": 15, - "role": "user", - "content": [ - { - "type": "TextBlock", - "text": "i want to be able to release binaries for a few systems of these tools. can uyou look at ../code which has some github actions and build setup", - "status": "complete" - } - ], - "timestamp": "2026-01-21T23:18:27.250816Z" - }, - { - "id": 16, - "role": "assistant", - "content": [ - { - "type": "ThinkingBlock", - "text": "The user wants to look at a sibling directory `../codey` which likely has GitHub Actions workflows for building and releasing binaries. Let me explore that.", - "status": "complete" - }, - { - "type": "ShellBlock", - "call_id": "toolu_01QQ3LXDVrSnTufYtWrXGvQn", - "tool_name": "mcp_shell", - "params": { - "command": "ls -la ../codey/" - }, - "status": "complete", - "text": "[stderr]\nls: ../codey/: No such file or directory\n\n[exit code: 1]", - "background": false - }, - { - "type": "ShellBlock", - "call_id": "toolu_01DuU25CfEwYiSoBaa3gtFTr", - "tool_name": "mcp_shell", - "params": { - "command": "ls -la ../codey/.github/workflows/" - }, - "status": "complete", - "text": "[stderr]\nls: ../codey/.github/workflows/: No such file or directory\n\n[exit code: 1]", - "background": false - }, - { - "type": "ThinkingBlock", - "text": "The user said `../code` not `../codey`. Let me try that.", - "status": "complete" - }, - { - "type": "ShellBlock", - "call_id": "toolu_019Bd2PhH7HLdUExZAshYdqS", - "tool_name": "mcp_shell", - "params": { - "command": "ls -la ../code/" - }, - "status": "complete", - "text": "total 736\ndrwxr-xr-x@ 24 tcdent staff 768 Jan 21 08:31 .\ndrwxr-xr-x 180 tcdent staff 5760 Jan 21 10:11 ..\n-rw-r--r--@ 1 tcdent staff 8196 Jan 16 09:25 .DS_Store\ndrwxr-xr-x@ 3 tcdent staff 96 Jan 14 18:46 .cargo\ndrwxr-xr-x@ 4 tcdent staff 128 Jan 17 18:26 .codey\ndrwxr-xr-x@ 17 tcdent staff 544 Jan 21 09:56 .git\ndrwxr-xr-x@ 3 tcdent staff 96 Jan 6 15:02 .github\n-rw-r--r--@ 1 tcdent staff 336 Jan 6 20:13 .gitignore\n-rw-r--r--@ 1 tcdent staff 122322 Jan 21 09:56 Cargo.lock\n-rw-r--r--@ 1 tcdent staff 2614 Jan 21 09:56 Cargo.toml\n-rw-r--r--@ 1 tcdent staff 7287 Jan 21 08:31 LIBRARY.md\n-rw-r--r--@ 1 tcdent staff 1068 Dec 14 10:31 LICENSE\n-rw-r--r--@ 1 tcdent staff 1148 Jan 6 15:02 Makefile\n-rw-r--r--@ 1 tcdent staff 7327 Jan 21 09:46 README.md\n-rw-r--r--@ 1 tcdent staff 1231 Jan 7 15:54 RELEASE.md\n-rw-r--r--@ 1 tcdent staff 3688 Jan 6 20:13 config.example.toml\ndrwxr-xr-x@ 7 tcdent staff 224 Jan 21 09:13 lib\n-rw-r--r--@ 1 tcdent staff 778 Jan 6 20:16 profile.json\n-rw-r--r--@ 1 tcdent staff 192854 Dec 15 08:19 profile.json.gz\ndrwxr-xr-x@ 11 tcdent staff 352 Jan 17 12:43 research\ndrwxr-xr-x@ 7 tcdent staff 224 Jan 9 10:15 sandbox\ndrwxr-xr-x@ 17 tcdent staff 544 Jan 21 09:48 src\ndrwxr-xr-x@ 8 tcdent staff 256 Jan 16 09:25 target\ndrwxr-xr-x@ 3 tcdent staff 96 Jan 6 20:13 tools\n", - "background": false - }, - { - "type": "ThinkingBlock", - "text": "Found it. Let me look at the GitHub workflows directory.", - "status": "complete" - }, - { - "type": "ShellBlock", - "call_id": "toolu_01749yJPwCN4nDZAhVQqrQPz", - "tool_name": "mcp_shell", - "params": { - "command": "ls -la ../code/.github/workflows/ 2>/dev/null || ls -la ../code/.github/" - }, - "status": "complete", - "text": "total 8\ndrwxr-xr-x@ 3 tcdent staff 96 Jan 6 15:02 .\ndrwxr-xr-x@ 3 tcdent staff 96 Jan 6 15:02 ..\n-rw-r--r--@ 1 tcdent staff 1772 Jan 7 15:43 release.yml\n", - "background": false - }, - { - "type": "ThinkingBlock", - "text": "There's a release.yml. Let me read that along with the Makefile and RELEASE.md to understand the build/release process.", - "status": "complete" - }, - { - "type": "ReadFileBlock", - "call_id": "toolu_01XofhVdQGiWoEqpVFNckGor", - "tool_name": "mcp_read_file", - "params": { - "path": "../code/RELEASE.md" - }, - "status": "complete", - "text": " 1│# Release Process\n 2│\n 3│## 1. Bump version\n 4│\n 5│```bash\n 6│# Edit Cargo.toml version\n 7│vim Cargo.toml\n 8│\n 9│git add Cargo.toml Cargo.lock\n 10│git commit -m \"Bump version to X.Y.Z\"\n 11│git push origin main\n 12│```\n 13│\n 14│## 2. Tag and push\n 15│\n 16│```bash\n 17│git tag vX.Y.Z\n 18│git push origin vX.Y.Z\n 19│```\n 20│\n 21│This triggers the [Release workflow](https://github.com/tcdent/codey/actions/workflows/release.yml) which builds for:\n 22│- `codey-darwin-arm64` (macOS ARM)\n 23│- `codey-linux-x86_64` (Linux x86)\n 24│- `codey-linux-arm64` (Linux ARM)\n 25│\n 26│## 3. Update Homebrew tap\n 27│\n 28│Once the release workflow completes, get SHA256 hashes:\n 29│\n 30│```bash\n 31│VERSION=X.Y.Z\n 32│curl -sL https://github.com/tcdent/codey/releases/download/v$VERSION/codey-darwin-arm64.tar.gz | shasum -a 256\n 33│curl -sL https://github.com/tcdent/codey/releases/download/v$VERSION/codey-linux-x86_64.tar.gz | shasum -a 256\n 34│curl -sL https://github.com/tcdent/codey/releases/download/v$VERSION/codey-linux-arm64.tar.gz | shasum -a 256\n 35│```\n 36│\n 37│Update `~/Work/tap/Formula/codey.rb`:\n 38│- `version \"X.Y.Z\"`\n 39│- SHA256 hashes for each platform\n 40│\n 41│```bash\n 42│cd ~/Work/tap\n 43│git add -A\n 44│git commit -m \"Update codey to vX.Y.Z\"\n 45│git push\n 46│```\n 47│\n 48│## Version scheme\n 49│\n 50│```\n 51│0.1.0-alpha.1 # Early development\n 52│0.1.0-rc.1 # Release candidate\n 53│0.1.0 # Stable release\n 54│```\n", - "background": false - }, - { - "type": "ReadFileBlock", - "call_id": "toolu_01WNPvqe3wANWSaiTsZFvjDd", - "tool_name": "mcp_read_file", - "params": { - "path": "../code/Makefile" - }, - "status": "complete", - "text": " 1│.PHONY: build run release profile clean patch\n 2│\n 3│# Set to 0 to use upstream crates: make SIMD=0 build\n 4│SIMD ?= 1\n 5│\n 6│ifeq ($(SIMD),1)\n 7│PATCH_DEPS := lib/ratatui-core/.patched lib/genai/.patched .cargo/config.toml\n 8│else\n 9│PATCH_DEPS := lib/genai/.patched .cargo/config.toml\n 10│endif\n 11│\n 12│build: $(PATCH_DEPS)\n 13│\tcargo build\n 14│\n 15│run: $(PATCH_DEPS)\n 16│\tcargo run\n 17│\n 18│release: $(PATCH_DEPS)\n 19│ifdef CARGO_BUILD_TARGET\n 20│\tcargo build --release --target $(CARGO_BUILD_TARGET)\n 21│else\n 22│\tcargo build --release\n 23│endif\n 24│\n 25│profile: release\n 26│\tsamply record ./target/release/codey\n 27│\n 28│clean: clean-config\n 29│\tcargo clean\n 30│\trm -rf lib/ratatui-core lib/genai\n 31│\n 32│clean-config:\n 33│\trm -f .cargo/config.toml\n 34│\trmdir .cargo 2>/dev/null || true\n 35│\n 36│patch: lib/ratatui-core/.patched lib/genai/.patched .cargo/config.toml\n 37│\n 38│lib/ratatui-core/.patched: lib/patches/ratatui-core/*\n 39│\t./lib/patches/ratatui-core/apply.sh\n 40│\n 41│lib/genai/.patched: lib/patches/genai/*\n 42│\t./lib/patches/genai/apply.sh\n 43│\n 44│.cargo/config.toml: lib/ratatui-core/.patched lib/genai/.patched\n 45│\tmkdir -p .cargo\n 46│\techo '[patch.crates-io]' > $@\n 47│ifeq ($(SIMD),1)\n 48│\techo 'ratatui-core = { path = \"lib/ratatui-core\" }' >> $@\n 49│endif\n 50│\techo 'genai = { path = \"lib/genai\" }' >> $@\n", - "background": false - }, - { - "type": "ThinkingBlock", - "text": "The release.yml file exists but it's probably in the workflows subdirectory. Let me look again.", - "status": "complete" - }, - { - "type": "ShellBlock", - "call_id": "toolu_01KZrsj5YkN6whAM9kGEhN46", - "tool_name": "mcp_shell", - "params": { - "command": "find ../code/.github -name \"*.yml\" -o -name \"*.yaml\" 2>/dev/null" - }, - "status": "complete", - "text": "../code/.github/workflows/release.yml\n", - "background": false - }, - { - "type": "ThinkingBlock", - "text": "Oh, there's a workflows directory. Let me read the release.yml file.", - "status": "complete" - }, - { - "type": "ShellBlock", - "call_id": "toolu_01AXfRV274ifRtqggGeGuSsA", - "tool_name": "mcp_shell", - "params": { - "command": "cat ../code/.github/workflows/release.yml" - }, - "status": "complete", - "text": "name: Release\n\non:\n push:\n tags:\n - 'v*'\n\nenv:\n CARGO_TERM_COLOR: always\n\njobs:\n build:\n strategy:\n matrix:\n include:\n - target: aarch64-apple-darwin\n os: macos-14\n name: codey-darwin-arm64\n - target: x86_64-unknown-linux-gnu\n os: ubuntu-latest\n name: codey-linux-x86_64\n - target: aarch64-unknown-linux-gnu\n os: ubuntu-24.04-arm\n name: codey-linux-arm64\n\n runs-on: ${{ matrix.os }}\n \n steps:\n - uses: actions/checkout@v4\n \n - name: Install Rust\n uses: dtolnay/rust-toolchain@stable\n with:\n targets: ${{ matrix.target }}\n \n - name: Build release\n run: make release\n env:\n CARGO_BUILD_TARGET: ${{ matrix.target }}\n \n - name: Ad-hoc sign (macOS)\n if: contains(matrix.os, 'macos')\n run: codesign --force --sign - target/${{ matrix.target }}/release/codey\n \n - name: Package\n run: |\n mkdir -p dist\n cp target/${{ matrix.target }}/release/codey dist/${{ matrix.name }}\n cd dist && tar -czvf ${{ matrix.name }}.tar.gz ${{ matrix.name }}\n \n - name: Upload artifact\n uses: actions/upload-artifact@v4\n with:\n name: ${{ matrix.name }}\n path: dist/${{ matrix.name }}.tar.gz\n\n release:\n needs: build\n runs-on: ubuntu-latest\n permissions:\n contents: write\n \n steps:\n - name: Download artifacts\n uses: actions/download-artifact@v4\n with:\n path: artifacts\n \n - name: Create Release\n uses: softprops/action-gh-release@v2\n with:\n files: artifacts/**/*.tar.gz\n generate_release_notes: true\n", - "background": false - }, - { - "type": "ThinkingBlock", - "text": "This is a good release workflow. It:\n1. Triggers on version tags (v*)\n2. Builds for 3 targets: macOS ARM, Linux x86_64, Linux ARM\n3. Uses a matrix to parallelize builds\n4. Signs macOS binaries (ad-hoc)\n5. Packages into tar.gz\n6. Creates a GitHub release with all artifacts\n\nFor the worldview project, we have two binaries:\n- `worldview-validate` (validator)\n- `worldview` (agent)\n\nLet me look at the current GitHub workflows in the wvf project first.", - "status": "complete" - }, - { - "type": "TextBlock", - "text": "Nice setup. Let me see what's already in the wvf project:", - "status": "complete" - }, - { - "type": "ShellBlock", - "call_id": "toolu_01V2NCfJnzosVeDT6vipUFXP", - "tool_name": "mcp_shell", - "params": { - "command": "ls -la .github/workflows/" - }, - "status": "complete", - "text": "total 32\ndrwxr-xr-x@ 4 tcdent staff 128 Jan 21 10:11 .\ndrwxr-xr-x@ 3 tcdent staff 96 Jan 21 10:11 ..\n-rw-r--r--@ 1 tcdent staff 3150 Jan 21 10:11 build.yml\n-rw-r--r--@ 1 tcdent staff 9340 Jan 21 10:11 evals.yml\n", - "background": false - }, - { - "type": "ReadFileBlock", - "call_id": "toolu_01M5R5ZpmhgV38eLYHdEaNEd", - "tool_name": "mcp_read_file", - "params": { - "path": ".github/workflows/build.yml" - }, - "status": "complete", - "text": " 1│name: Build Worldview Tools\n 2│\n 3│on:\n 4│ push:\n 5│ branches: [main, master]\n 6│ paths:\n 7│ - 'agent/**'\n 8│ - 'validator/**'\n 9│ - 'spec/**'\n 10│ - 'evals/**'\n 11│ - 'system.md'\n 12│ - 'SPEC.md'\n 13│ - '.github/workflows/build.yml'\n 14│ - '.github/workflows/evals.yml'\n 15│ pull_request:\n 16│ branches: [main, master]\n 17│ paths:\n 18│ - 'agent/**'\n 19│ - 'validator/**'\n 20│ - 'spec/**'\n 21│ - 'evals/**'\n 22│ - 'system.md'\n 23│ - 'SPEC.md'\n 24│ - '.github/workflows/evals.yml'\n 25│ workflow_dispatch:\n 26│ workflow_call:\n 27│ outputs:\n 28│ artifact-name:\n 29│ description: \"Name of the uploaded artifact\"\n 30│ value: worldview-tools\n 31│\n 32│jobs:\n 33│ # Verify generated files are in sync with spec/tokens.yaml\n 34│ spec-check:\n 35│ runs-on: ubuntu-latest\n 36│ steps:\n 37│ - name: Checkout repository\n 38│ uses: actions/checkout@v4\n 39│\n 40│ - name: Setup Python\n 41│ uses: actions/setup-python@v5\n 42│ with:\n 43│ python-version: '3.11'\n 44│\n 45│ - name: Install PyYAML\n 46│ run: pip install pyyaml\n 47│\n 48│ - name: Regenerate all spec-derived files\n 49│ run: |\n 50│ # Generate system.md, README language spec, etc.\n 51│ python spec/generate.py all\n 52│\n 53│ # Note: validator tokens are generated at compile-time via build.rs\n 54│ # so we don't need tokens_generated.rs committed anymore\n 55│\n 56│ - name: Check for uncommitted changes\n 57│ run: |\n 58│ # Check if any tracked files changed (excluding tokens_generated.rs)\n 59│ CHANGED=$(git diff --name-only -- ':!validator/src/tokens_generated.rs')\n 60│ if [ -n \"$CHANGED\" ]; then\n 61│ echo \"::error::Generated files are out of sync with spec/tokens.yaml\"\n 62│ echo \"\"\n 63│ echo \"The following files need to be regenerated:\"\n 64│ echo \"$CHANGED\"\n 65│ echo \"\"\n 66│ echo \"Run 'python spec/generate.py all' and commit the changes.\"\n 67│ exit 1\n 68│ fi\n 69│\n 70│ echo \"✓ All generated files are in sync\"\n 71│\n 72│ build:\n 73│ needs: spec-check\n 74│ runs-on: ubuntu-latest\n 75│\n 76│ steps:\n 77│ - name: Checkout repository\n 78│ uses: actions/checkout@v4\n 79│\n 80│ - name: Setup Rust\n 81│ uses: dtolnay/rust-toolchain@stable\n 82│\n 83│ - name: Cache cargo registry\n 84│ uses: actions/cache@v4\n 85│ with:\n 86│ path: |\n 87│ ~/.cargo/registry\n 88│ ~/.cargo/git\n 89│ target\n 90│ key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}\n 91│ restore-keys: |\n 92│ ${{ runner.os }}-cargo-\n 93│\n 94│ - name: Build validator\n 95│ run: |\n 96│ cd validator\n 97│ cargo build --release\n 98│\n 99│ - name: Build agent\n 100│ run: |\n 101│ cd agent\n 102│ ./setup.sh\n 103│ cargo build --release\n 104│\n 105│ - name: Run validator tests\n 106│ run: |\n 107│ cd validator\n 108│ cargo test\n 109│\n 110│ - name: Collect binaries\n 111│ run: |\n 112│ mkdir -p dist\n 113│ cp validator/target/release/worldview-validate dist/\n 114│ cp agent/target/release/worldview dist/\n 115│ chmod +x dist/*\n 116│\n 117│ - name: Upload artifacts\n 118│ uses: actions/upload-artifact@v4\n 119│ with:\n 120│ name: worldview-tools\n 121│ path: dist/\n 122│ retention-days: 7\n", - "background": false - }, - { - "type": "ThinkingBlock", - "text": "So there's already a build.yml that builds on ubuntu, but it's not doing multi-platform release builds. Let me look at what agent/setup.sh does and then create a release workflow.", - "status": "complete" - }, - { - "type": "ShellBlock", - "call_id": "toolu_01FFoLqFQwEhboarn63uh1BM", - "tool_name": "mcp_shell", - "params": { - "command": "cat agent/setup.sh 2>/dev/null || echo \"no setup.sh\"" - }, - "status": "complete", - "text": "#!/bin/bash\n# Setup script for wsl-agent\n# This script clones and patches codey dependencies\n\nset -euo pipefail\n\nSCRIPT_DIR=\"$(cd \"$(dirname \"${BASH_SOURCE[0]}\")\" && pwd)\"\nVENDOR_DIR=\"$SCRIPT_DIR/vendor\"\n\necho \"Setting up wsl-agent dependencies...\"\n\n# Clone codey if not present\nif [ ! -d \"$VENDOR_DIR/codey\" ]; then\n echo \"Cloning codey...\"\n mkdir -p \"$VENDOR_DIR\"\n git clone --depth 1 https://github.com/tcdent/codey.git \"$VENDOR_DIR/codey\"\nelse\n echo \"Codey already cloned, updating...\"\n cd \"$VENDOR_DIR/codey\"\n git pull --ff-only || true\n cd \"$SCRIPT_DIR\"\nfi\n\n# Apply patches in codey\necho \"Applying codey patches...\"\ncd \"$VENDOR_DIR/codey\"\n\n# Run the genai patch\nif [ ! -f \"lib/genai/.patched\" ]; then\n echo \"Patching genai...\"\n bash lib/patches/genai/apply.sh\nfi\n\necho \"Setup complete!\"\necho \"\"\necho \"You can now build with: cargo build\"\n", - "background": false - }, - { - "type": "ReadFileBlock", - "call_id": "toolu_018oWMxNT82Tyjp16Hoq34o8", - "tool_name": "mcp_read_file", - "params": { - "path": "agent/Cargo.toml" - }, - "status": "complete", - "text": " 1│[package]\n 2│name = \"worldview-agent\"\n 3│version = \"0.1.0\"\n 4│edition = \"2024\"\n 5│description = \"CLI tool for adding facts to Worldview files using an AI agent\"\n 6│license = \"MIT\"\n 7│\n 8│[[bin]]\n 9│name = \"worldview\"\n 10│path = \"src/main.rs\"\n 11│\n 12│[dependencies]\n 13│# Core agent functionality (vendored, minimal features for library usage)\n 14│codey = { path = \"vendor/codey\", default-features = false }\n 15│\n 16│# Worldview validation\n 17│worldview-validator = { path = \"../validator\" }\n 18│\n 19│# Async runtime\n 20│tokio = { version = \"1\", features = [\"full\"] }\n 21│\n 22│# CLI parsing\n 23│clap = { version = \"4\", features = [\"derive\"] }\n 24│\n 25│# Serialization for tool schemas\n 26│serde = { version = \"1\", features = [\"derive\"] }\n 27│serde_json = \"1\"\n 28│\n 29│# Error handling\n 30│anyhow = \"1\"\n 31│\n 32│[patch.crates-io]\n 33│# Use codey's patched genai with thinking block support\n 34│genai = { path = \"vendor/codey/lib/genai\" }\n", - "background": false - }, - { - "type": "ThinkingBlock", - "text": "I see - the agent depends on a vendored codey with patches. The setup.sh clones codey and applies patches. This complicates cross-platform builds a bit since each runner needs to run setup.sh.\n\nLet me create a release workflow based on the codey one, but adapted for the two binaries (worldview-validate and worldview).", - "status": "complete" - }, - { - "type": "TextBlock", - "text": "Got it. The agent vendors codey with patches. Let me create a release workflow:", - "status": "complete" - }, - { - "type": "WriteFileBlock", - "call_id": "toolu_014QviJBcRYkURuf5wJjhtaA", - "tool_name": "mcp_write_file", - "params": { - "content": "name: Release\n\non:\n push:\n tags:\n - 'v*'\n\nenv:\n CARGO_TERM_COLOR: always\n\njobs:\n build:\n strategy:\n matrix:\n include:\n - target: aarch64-apple-darwin\n os: macos-14\n suffix: darwin-arm64\n - target: x86_64-unknown-linux-gnu\n os: ubuntu-latest\n suffix: linux-x86_64\n - target: aarch64-unknown-linux-gnu\n os: ubuntu-24.04-arm\n suffix: linux-arm64\n\n runs-on: ${{ matrix.os }}\n \n steps:\n - uses: actions/checkout@v4\n \n - name: Install Rust\n uses: dtolnay/rust-toolchain@stable\n with:\n targets: ${{ matrix.target }}\n \n - name: Build validator\n run: |\n cd validator\n cargo build --release --target ${{ matrix.target }}\n \n - name: Setup agent dependencies\n run: |\n cd agent\n ./setup.sh\n \n - name: Build agent\n run: |\n cd agent\n cargo build --release --target ${{ matrix.target }}\n \n - name: Ad-hoc sign (macOS)\n if: contains(matrix.os, 'macos')\n run: |\n codesign --force --sign - validator/target/${{ matrix.target }}/release/worldview-validate\n codesign --force --sign - agent/target/${{ matrix.target }}/release/worldview\n \n - name: Package\n run: |\n mkdir -p dist\n cp validator/target/${{ matrix.target }}/release/worldview-validate dist/worldview-validate-${{ matrix.suffix }}\n cp agent/target/${{ matrix.target }}/release/worldview dist/worldview-${{ matrix.suffix }}\n cd dist\n tar -czvf worldview-validate-${{ matrix.suffix }}.tar.gz worldview-validate-${{ matrix.suffix }}\n tar -czvf worldview-${{ matrix.suffix }}.tar.gz worldview-${{ matrix.suffix }}\n \n - name: Upload artifacts\n uses: actions/upload-artifact@v4\n with:\n name: binaries-${{ matrix.suffix }}\n path: dist/*.tar.gz\n\n release:\n needs: build\n runs-on: ubuntu-latest\n permissions:\n contents: write\n \n steps:\n - name: Download artifacts\n uses: actions/download-artifact@v4\n with:\n path: artifacts\n \n - name: Create Release\n uses: softprops/action-gh-release@v2\n with:\n files: artifacts/**/*.tar.gz\n generate_release_notes: true\n", - "path": ".github/workflows/release.yml" - }, - "status": "complete", - "text": "Created file: .github/workflows/release.yml (88 lines, 2403 bytes)", - "background": false - }, - { - "type": "ThinkingBlock", - "text": "Now let me also add a RELEASE.md with instructions like codey has.", - "status": "complete" - }, - { - "type": "WriteFileBlock", - "call_id": "toolu_01P6sUw8CvRwjjgaS42MuPgh", - "tool_name": "mcp_write_file", - "params": { - "content": "# Release Process\n\n## 1. Bump versions\n\n```bash\n# Edit version in both Cargo.toml files\nvim validator/Cargo.toml agent/Cargo.toml\n\ngit add validator/Cargo.toml agent/Cargo.toml\ngit commit -m \"Bump version to X.Y.Z\"\ngit push origin main\n```\n\n## 2. Tag and push\n\n```bash\ngit tag vX.Y.Z\ngit push origin vX.Y.Z\n```\n\nThis triggers the [Release workflow](.github/workflows/release.yml) which builds:\n\n| Binary | Description |\n|--------|-------------|\n| `worldview-validate-darwin-arm64` | macOS ARM validator |\n| `worldview-validate-linux-x86_64` | Linux x86 validator |\n| `worldview-validate-linux-arm64` | Linux ARM validator |\n| `worldview-darwin-arm64` | macOS ARM agent CLI |\n| `worldview-linux-x86_64` | Linux x86 agent CLI |\n| `worldview-linux-arm64` | Linux ARM agent CLI |\n\n## 3. Verify release\n\nOnce the workflow completes, check the [Releases page](../../releases) for the new version with all artifacts attached.\n\n## Version scheme\n\n```\n0.1.0-alpha.1 # Early development\n0.1.0-rc.1 # Release candidate\n0.1.0 # Stable release\n```\n", - "path": "RELEASE.md" - }, - "status": "complete", - "text": "Created file: RELEASE.md (42 lines, 1052 bytes)", - "background": false - }, - { - "type": "ThinkingBlock", - "text": "Let me also run a quick check on the workflow to make sure the paths are correct - I need to verify the validator and agent have different target directories.", - "status": "complete" - }, - { - "type": "TextBlock", - "text": "Let me verify the target paths are correct:", - "status": "complete" - }, - { - "type": "ShellBlock", - "call_id": "toolu_01SUsqQbh8KPG3cqrGEA9eSB", - "tool_name": "mcp_shell", - "params": { - "command": "cd validator && cargo build --release 2>&1 | tail -5" - }, - "status": "complete", - "text": " Compiling serde_derive v1.0.228\n Compiling thiserror-impl v1.0.69\n Compiling serde_yaml v0.9.34+deprecated\n Compiling worldview-validator v0.1.0 (/Users/tcdent/Work/wvf/validator)\n Finished `release` profile [optimized] target(s) in 4.44s\n", - "background": false - }, - { - "type": "ShellBlock", - "call_id": "toolu_01K8GVaczxwTiDAGHdnV2L7G", - "tool_name": "mcp_shell", - "params": { - "command": "ls -la validator/target/release/worldview-validate 2>/dev/null && ls -la agent/target/release/worldview 2>/dev/null || echo \"need to build agent\"" - }, - "status": "complete", - "text": "-rwxr-xr-x@ 1 tcdent staff 487360 Jan 21 15:19 validator/target/release/worldview-validate\nneed to build agent\n", - "background": false - }, - { - "type": "ThinkingBlock", - "text": "Good, the validator builds. The agent would need the setup.sh run first. Let me commit the release workflow and document.", - "status": "complete" - }, - { - "type": "ShellBlock", - "call_id": "toolu_01LMSyd7n3DmXrMHc8LjykUf", - "tool_name": "mcp_shell", - "params": { - "command": "git add -A && git commit -m \"Add release workflow for multi-platform binaries\n\nBuilds for macOS ARM, Linux x86_64, and Linux ARM on tag push.\nCreates GitHub release with:\n- worldview-validate-{platform}.tar.gz\n- worldview-{platform}.tar.gz\n\nAdded RELEASE.md with release process documentation.\"" - }, - "status": "pending", - "text": "", - "background": false - } - ], - "timestamp": "2026-01-21T23:18:27.258771Z" - } - ], - "next_id": 17 -} \ No newline at end of file diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 5186339..ec11162 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -4,7 +4,7 @@ on: push: branches: [main, master] paths: - - 'agent/**' + - 'cli/**' - 'validator/**' - 'spec/**' - 'evals/**' @@ -15,7 +15,7 @@ on: pull_request: branches: [main, master] paths: - - 'agent/**' + - 'cli/**' - 'validator/**' - 'spec/**' - 'evals/**' @@ -37,21 +37,11 @@ jobs: - name: Checkout repository uses: actions/checkout@v4 - - name: Setup Python - uses: actions/setup-python@v5 - with: - python-version: '3.11' - - - name: Install PyYAML - run: pip install pyyaml + - name: Install uv + uses: astral-sh/setup-uv@v5 - name: Regenerate all spec-derived files - run: | - # Generate system.md, README language spec, etc. - python spec/generate.py all - - # Note: validator tokens are generated at compile-time via build.rs - # so we don't need tokens_generated.rs committed anymore + run: uv run spec/generate.py all - name: Check for uncommitted changes run: | @@ -91,27 +81,22 @@ jobs: restore-keys: | ${{ runner.os }}-cargo- - - name: Build validator - run: | - cd validator - cargo build --release + - name: Build validator library + run: cargo build --release -p worldview-validator - - name: Build agent + - name: Build CLI run: | - cd agent + cd cli ./setup.sh cargo build --release - name: Run validator tests - run: | - cd validator - cargo test + run: cargo test -p worldview-validator - name: Collect binaries run: | mkdir -p dist - cp validator/target/release/worldview-validate dist/ - cp agent/target/release/worldview dist/ + cp cli/target/release/worldview dist/ chmod +x dist/* - name: Upload artifacts diff --git a/.github/workflows/evals.yml b/.github/workflows/evals.yml index 1b43f9d..de09fdd 100644 --- a/.github/workflows/evals.yml +++ b/.github/workflows/evals.yml @@ -47,16 +47,11 @@ jobs: # For workflow_run, checkout the commit that triggered the build ref: ${{ github.event.workflow_run.head_sha || github.sha }} - - name: Setup Python - uses: actions/setup-python@v5 - with: - python-version: '3.11' - cache: 'pip' - cache-dependency-path: 'evals/requirements.txt' + - name: Install uv + uses: astral-sh/setup-uv@v5 - name: Install dependencies - run: | - pip install -r evals/requirements.txt + run: uv sync - name: Download Worldview tools from build if: ${{ github.event_name == 'workflow_run' }} @@ -108,7 +103,7 @@ jobs: mkdir -p results # Build command - CMD="python -m evals run --verbose --output results" + CMD="uv run python -m evals run --verbose --output results" # Add models if [ "${{ steps.models.outputs.use_all }}" == "true" ]; then @@ -147,7 +142,7 @@ jobs: - name: Check for failures run: | # Extract success rate from JSON and fail if below threshold - python -c " + uv run python -c " import json with open('results/results.json') as f: data = json.load(f) @@ -182,16 +177,11 @@ jobs: with: ref: ${{ github.event.workflow_run.head_sha || github.sha }} - - name: Setup Python - uses: actions/setup-python@v5 - with: - python-version: '3.11' - cache: 'pip' - cache-dependency-path: 'evals/requirements.txt' + - name: Install uv + uses: astral-sh/setup-uv@v5 - name: Install dependencies - run: | - pip install -r evals/requirements.txt + run: uv sync - name: Download Worldview tools from build if: ${{ github.event_name == 'workflow_run' }} @@ -231,7 +221,7 @@ jobs: mkdir -p results # Build command - CMD="python -m evals write-eval --verbose --output results --agent-cli ./bin/worldview" + CMD="uv run python -m evals write-eval --verbose --output results --agent-cli ./bin/worldview" # Add models (write eval only supports Claude models) CMD="$CMD --all-models" @@ -263,7 +253,7 @@ jobs: - name: Check write eval results if: steps.cli-check.outputs.available == 'true' run: | - python -c " + uv run python -c " import json with open('results/write_results.json') as f: data = json.load(f) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 8252f48..36cd776 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -33,34 +33,31 @@ jobs: with: targets: ${{ matrix.target }} - - name: Build validator - run: | - cd validator - cargo build --release --target ${{ matrix.target }} + - name: Install uv + uses: astral-sh/setup-uv@v5 - - name: Setup agent dependencies + - name: Generate spec files + run: uv run spec/generate.py system + + - name: Setup CLI dependencies run: | - cd agent + cd cli ./setup.sh - - name: Build agent + - name: Build CLI run: | - cd agent + cd cli cargo build --release --target ${{ matrix.target }} - name: Ad-hoc sign (macOS) if: contains(matrix.os, 'macos') - run: | - codesign --force --sign - validator/target/${{ matrix.target }}/release/worldview-validate - codesign --force --sign - agent/target/${{ matrix.target }}/release/worldview + run: codesign --force --sign - cli/target/${{ matrix.target }}/release/worldview - name: Package run: | mkdir -p dist - cp validator/target/${{ matrix.target }}/release/worldview-validate dist/worldview-validate-${{ matrix.suffix }} - cp agent/target/${{ matrix.target }}/release/worldview dist/worldview-${{ matrix.suffix }} + cp cli/target/${{ matrix.target }}/release/worldview dist/worldview-${{ matrix.suffix }} cd dist - tar -czvf worldview-validate-${{ matrix.suffix }}.tar.gz worldview-validate-${{ matrix.suffix }} tar -czvf worldview-${{ matrix.suffix }}.tar.gz worldview-${{ matrix.suffix }} - name: Upload artifacts diff --git a/.gitignore b/.gitignore index a91b374..b35dede 100644 --- a/.gitignore +++ b/.gitignore @@ -45,3 +45,4 @@ results/ # Generated files (created at compile-time) validator/src/tokens_generated.rs +.codey/ diff --git a/README.md b/README.md index d09029c..26284d7 100644 --- a/README.md +++ b/README.md @@ -105,35 +105,32 @@ Claims follow positional grammar—position implies role: This repository includes a complete toolchain for working with Worldview documents: -### Validator (`worldview-validate`) +### CLI (`worldview`) -Validates `.wvf` files for syntactic correctness. +A unified command-line tool for working with Worldview files. ```bash # Validate a file -worldview-validate example.wvf +worldview validate example.wvf # Validate from stdin -cat example.wvf | worldview-validate --stdin -``` - -### Agent CLI (`worldview`) +cat example.wvf | worldview validate --stdin -An AI-powered tool that converts plain-text statements into proper Worldview notation. - -```bash -# Add a fact to a Worldview file -worldview "Trust is built slowly through consistent actions" --file worldview.wvf +# Add a fact using AI agent +worldview add "Trust is built slowly through consistent actions" --file worldview.wvf # Use a specific model -worldview "Power corrupts when unchecked" --model claude-opus-4-5-20251101 +worldview add "Power corrupts when unchecked" --model claude-opus-4-5-20251101 + +# View format specification +worldview --help ``` -The agent understands the full Worldview specification and will: -1. Read existing content to understand structure -2. Determine appropriate concept/facet placement -3. Format statements using proper notation -4. Validate before writing +The `add` command uses an AI agent that: +1. Reads existing content to understand structure +2. Determines appropriate concept/facet placement +3. Formats statements using proper notation +4. Validates before writing (validation runs automatically) ### Evaluation Framework @@ -162,18 +159,17 @@ wvf/ │ ├── tokens.yaml # Token definitions (source of truth) │ ├── grammar.pest # PEG grammar │ └── generate.py # Generates docs and code from tokens.yaml -├── validator/ # Rust validator -│ ├── src/lib.rs # Validation library -│ ├── src/main.rs # CLI tool +├── validator/ # Rust validation library +│ ├── src/lib.rs # Validation logic │ └── build.rs # Generates tokens from spec at compile time -├── agent/ # Rust agent CLI -│ └── src/main.rs # LLM-powered notation converter +├── cli/ # Rust CLI (unified binary) +│ ├── src/main.rs # Subcommand dispatch +│ ├── src/validate.rs # Validate subcommand +│ └── src/add.rs # Add subcommand (AI agent) ├── evals/ # Python evaluation framework │ ├── cli.py # Evaluation CLI -│ ├── runner.py # Test orchestration -│ ├── evaluator.py # Response scoring -│ ├── test_cases.py # Test case definitions -│ └── llm_clients.py # LLM provider clients +│ ├── read_eval/ # Read comprehension tests +│ └── write_eval/ # Write/generation tests ├── SPEC.md # Full format specification ├── system.md # Condensed system prompt (generated) └── example.wvf # Example document @@ -184,16 +180,23 @@ wvf/ ### Prerequisites - Rust 1.70+ -- Python 3.11+ +- Python 3.11+ and [uv](https://docs.astral.sh/uv/) -### Build Tools +### Build CLI ```bash -# Build validator -cd validator && cargo build --release +# Setup vendored dependencies and build +cd cli && ./setup.sh && cargo build --release -# Build agent (requires setup for vendored dependencies) -cd agent && ./setup.sh && cargo build --release +# Binary will be at cli/target/release/worldview +``` + +### Run Evaluations + +```bash +# From project root +uv sync +uv run python -m evals --help ``` ## Design Principles diff --git a/RELEASE.md b/RELEASE.md index d2f3ee7..42ffd24 100644 --- a/RELEASE.md +++ b/RELEASE.md @@ -3,10 +3,10 @@ ## 1. Bump versions ```bash -# Edit version in both Cargo.toml files -vim validator/Cargo.toml agent/Cargo.toml +# Edit version in Cargo.toml files +vim validator/Cargo.toml cli/Cargo.toml -git add validator/Cargo.toml agent/Cargo.toml +git add validator/Cargo.toml cli/Cargo.toml git commit -m "Bump version to X.Y.Z" git push origin main ``` @@ -22,12 +22,11 @@ This triggers the [Release workflow](.github/workflows/release.yml) which builds | Binary | Description | |--------|-------------| -| `worldview-validate-darwin-arm64` | macOS ARM validator | -| `worldview-validate-linux-x86_64` | Linux x86 validator | -| `worldview-validate-linux-arm64` | Linux ARM validator | -| `worldview-darwin-arm64` | macOS ARM agent CLI | -| `worldview-linux-x86_64` | Linux x86 agent CLI | -| `worldview-linux-arm64` | Linux ARM agent CLI | +| `worldview-darwin-arm64` | macOS ARM CLI | +| `worldview-linux-x86_64` | Linux x86 CLI | +| `worldview-linux-arm64` | Linux ARM CLI | + +The `worldview` binary includes both validation (`worldview validate`) and agent (`worldview add`) functionality. ## 3. Verify release diff --git a/agent/.gitignore b/cli/.gitignore similarity index 100% rename from agent/.gitignore rename to cli/.gitignore diff --git a/agent/Cargo.lock b/cli/Cargo.lock similarity index 99% rename from agent/Cargo.lock rename to cli/Cargo.lock index 8d2ff6f..122c39d 100644 --- a/agent/Cargo.lock +++ b/cli/Cargo.lock @@ -247,7 +247,7 @@ checksum = "c3e64b0cc0439b12df2fa678eae89a1c56a529fd067a9115f7827f1fffd22b32" [[package]] name = "codey" -version = "0.1.0-rc.5" +version = "0.1.0-rc.6" dependencies = [ "anyhow", "async-stream", @@ -1670,6 +1670,19 @@ dependencies = [ "syn", ] +[[package]] +name = "serde_yaml" +version = "0.9.34+deprecated" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47" +dependencies = [ + "indexmap 2.13.0", + "itoa", + "ryu", + "serde", + "unsafe-libyaml", +] + [[package]] name = "sha2" version = "0.10.9" @@ -2157,6 +2170,12 @@ version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" +[[package]] +name = "unsafe-libyaml" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861" + [[package]] name = "untrusted" version = "0.9.0" @@ -2649,7 +2668,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" [[package]] -name = "worldview-agent" +name = "worldview-cli" version = "0.1.0" dependencies = [ "anyhow", @@ -2665,6 +2684,8 @@ dependencies = [ name = "worldview-validator" version = "0.1.0" dependencies = [ + "serde", + "serde_yaml", "thiserror 1.0.69", ] diff --git a/agent/Cargo.toml b/cli/Cargo.toml similarity index 87% rename from agent/Cargo.toml rename to cli/Cargo.toml index 0f0e8b2..34792dd 100644 --- a/agent/Cargo.toml +++ b/cli/Cargo.toml @@ -1,8 +1,8 @@ [package] -name = "worldview-agent" +name = "worldview-cli" version = "0.1.0" edition = "2024" -description = "CLI tool for adding facts to Worldview files using an AI agent" +description = "CLI tool for working with Worldview format files" license = "MIT" [[bin]] diff --git a/agent/setup.sh b/cli/setup.sh similarity index 100% rename from agent/setup.sh rename to cli/setup.sh diff --git a/agent/src/main.rs b/cli/src/add.rs similarity index 89% rename from agent/src/main.rs rename to cli/src/add.rs index 91fa969..c9349d1 100644 --- a/agent/src/main.rs +++ b/cli/src/add.rs @@ -1,10 +1,6 @@ -//! Worldview Agent CLI - A tool for adding facts to Worldview files using an AI agent -//! -//! This CLI accepts plain-text facts and uses an AI agent to properly format -//! and incorporate them into a Worldview format (.wvf) file. +//! Add subcommand - adds facts to Worldview files using an AI agent use anyhow::Result; -use clap::Parser; use codey::{Agent, AgentRuntimeConfig, AgentStep, RequestMode, SimpleTool, ToolRegistry}; use serde_json::json; use std::path::PathBuf; @@ -61,29 +57,6 @@ fn build_system_prompt() -> String { ) } -/// CLI for adding facts to Worldview files using an AI agent -#[derive(Parser, Debug)] -#[command(name = "worldview")] -#[command(about = "Add facts to Worldview files using an AI agent")] -#[command(version)] -struct Cli { - /// The fact or statement to add to the Worldview file - #[arg(required = true)] - fact: String, - - /// Path to the Worldview file to modify - #[arg(short, long, default_value = "worldview.wvf")] - file: PathBuf, - - /// Model to use (claude-sonnet-4-20250514 or claude-opus-4-5-20251101) - #[arg(short, long, default_value = "claude-sonnet-4-20250514")] - model: String, - - /// Enable verbose output - #[arg(short, long)] - verbose: bool, -} - /// Create the read_worldview tool definition fn create_read_tool() -> SimpleTool { SimpleTool::new( @@ -274,9 +247,7 @@ fn handle_tool_call(file_path: &PathBuf, tool_name: &str, params: &serde_json::V } } -#[tokio::main] -async fn main() -> Result<()> { - let cli = Cli::parse(); +pub async fn run(fact: String, file: PathBuf, model: String, verbose: bool) -> Result<()> { let start_time = std::time::Instant::now(); // Check for API key @@ -286,16 +257,16 @@ async fn main() -> Result<()> { } // Resolve the file path - let file_path = if cli.file.is_absolute() { - cli.file.clone() + let file_path = if file.is_absolute() { + file.clone() } else { - std::env::current_dir()?.join(&cli.file) + std::env::current_dir()?.join(&file) }; - if cli.verbose { + if verbose { eprintln!("[config] Worldview file: {:?}", file_path); - eprintln!("[config] Model: {}", cli.model); - eprintln!("[config] Fact: {}", cli.fact); + eprintln!("[config] Model: {}", model); + eprintln!("[config] Fact: {}", fact); eprintln!("[start] Beginning agent execution..."); } @@ -306,7 +277,7 @@ async fn main() -> Result<()> { // Configure the agent let config = AgentRuntimeConfig { - model: cli.model.clone(), + model: model.clone(), max_tokens: 4096, thinking_budget: 1024, // Minimum required max_retries: 3, @@ -325,7 +296,7 @@ async fn main() -> Result<()> { // Format the user message let user_message = format!( "Please add this fact to the Worldview file at {:?}:\n\n{}", - file_path, cli.fact + file_path, fact ); // Send the request @@ -338,12 +309,12 @@ async fn main() -> Result<()> { while let Some(step) = agent.next().await { match step { AgentStep::TextDelta(text) => { - if cli.verbose { + if verbose { print!("{}", text); } } AgentStep::ThinkingDelta(thinking) => { - if cli.verbose { + if verbose { if !thinking_started { thinking_started = true; eprint!("\n[thinking] "); @@ -355,7 +326,7 @@ async fn main() -> Result<()> { // Not used in our simple case } AgentStep::ToolRequest(tool_calls) => { - if cli.verbose && thinking_started { + if verbose && thinking_started { eprintln!(); // End thinking block thinking_started = false; } @@ -364,7 +335,7 @@ async fn main() -> Result<()> { tool_call_count += 1; let tool_start = std::time::Instant::now(); - if cli.verbose { + if verbose { // Format params nicely for readability let params_str = if call.params.is_object() { serde_json::to_string_pretty(&call.params).unwrap_or_else(|_| format!("{:?}", call.params)) @@ -377,7 +348,7 @@ async fn main() -> Result<()> { let result = handle_tool_call(&file_path, &call.name, &call.params); - if cli.verbose { + if verbose { let tool_elapsed = tool_start.elapsed(); eprintln!("[result:{}ms] {}", tool_elapsed.as_millis(), result); } @@ -386,13 +357,13 @@ async fn main() -> Result<()> { } } AgentStep::Retrying { attempt, error } => { - if cli.verbose { + if verbose { eprintln!("[retry] Attempt {} after error: {}", attempt, error); } } AgentStep::Finished { usage } => { let total_elapsed = start_time.elapsed(); - if cli.verbose { + if verbose { eprintln!("\n[done] Output: {}, Context: {}", usage.output_tokens, usage.context_tokens); eprintln!("[timing] Total: {}ms, Tool calls: {}", @@ -402,7 +373,7 @@ async fn main() -> Result<()> { } AgentStep::Error(e) => { let total_elapsed = start_time.elapsed(); - if cli.verbose { + if verbose { eprintln!("[error:{}ms] {}", total_elapsed.as_millis(), e); } eprintln!("Error: {}", e); diff --git a/cli/src/main.rs b/cli/src/main.rs new file mode 100644 index 0000000..7b44e14 --- /dev/null +++ b/cli/src/main.rs @@ -0,0 +1,66 @@ +//! Worldview CLI - Tools for working with Worldview format files +//! +//! Commands: +//! validate - Validate .wvf files for syntax errors +//! add - Add facts to a Worldview file using an AI agent + +use anyhow::Result; +use clap::{Parser, Subcommand}; +use std::path::PathBuf; + +mod add; +mod validate; + +/// CLI for working with Worldview format files +#[derive(Parser, Debug)] +#[command(name = "worldview")] +#[command(about = "Tools for working with Worldview format files")] +#[command(version)] +#[command(after_long_help = include_str!("../../system.md"))] +struct Cli { + #[command(subcommand)] + command: Commands, +} + +#[derive(Subcommand, Debug)] +enum Commands { + /// Validate Worldview files for syntax errors (also runs automatically on `add`) + Validate { + /// Files to validate + #[arg(required_unless_present = "stdin")] + files: Vec, + + /// Read from stdin instead of files + #[arg(long)] + stdin: bool, + }, + + /// Add a fact to a Worldview file using an AI agent + Add { + /// The fact or statement to add + #[arg(required = true)] + fact: String, + + /// Path to the Worldview file to modify + #[arg(short, long, default_value = "worldview.wvf")] + file: PathBuf, + + /// Model to use + #[arg(short, long, default_value = "claude-sonnet-4-20250514")] + model: String, + + /// Enable verbose output + #[arg(short, long)] + verbose: bool, + }, +} + +#[tokio::main] +async fn main() -> Result<()> { + let cli = Cli::parse(); + + match cli.command { + Commands::Validate { files, stdin } => validate::run(files, stdin), + Commands::Add { fact, file, model, verbose } => add::run(fact, file, model, verbose).await, + } +} diff --git a/cli/src/validate.rs b/cli/src/validate.rs new file mode 100644 index 0000000..51cc1f3 --- /dev/null +++ b/cli/src/validate.rs @@ -0,0 +1,50 @@ +//! Validate subcommand - validates .wvf files for syntax errors + +use anyhow::Result; +use std::io::{self, Read}; +use std::path::PathBuf; + +pub fn run(files: Vec, stdin: bool) -> Result<()> { + let mut all_valid = true; + + if stdin { + // Read from stdin + let mut content = String::new(); + io::stdin().read_to_string(&mut content)?; + let result = worldview_validator::validate(&content); + print!("{}", result); + if !result.is_valid() { + all_valid = false; + } + } else { + // Validate each file + for path in &files { + if files.len() > 1 { + println!("{}:", path.display()); + } + + match worldview_validator::validate_file(path) { + Ok(result) => { + println!("{}", result); + if !result.is_valid() { + all_valid = false; + } + } + Err(e) => { + eprintln!("Error reading {}: {}", path.display(), e); + all_valid = false; + } + } + + if files.len() > 1 { + println!(); + } + } + } + + if all_valid { + Ok(()) + } else { + std::process::exit(1); + } +} diff --git a/validator/Cargo.toml b/validator/Cargo.toml index 94f5168..03cb979 100644 --- a/validator/Cargo.toml +++ b/validator/Cargo.toml @@ -9,10 +9,6 @@ license = "MIT" name = "worldview_validator" path = "src/lib.rs" -[[bin]] -name = "worldview-validate" -path = "src/main.rs" - [dependencies] thiserror = "1.0" diff --git a/validator/src/main.rs b/validator/src/main.rs deleted file mode 100644 index 5ca9dc2..0000000 --- a/validator/src/main.rs +++ /dev/null @@ -1,103 +0,0 @@ -//! Worldview Validator CLI -//! -//! A command-line tool for validating Worldview format (.wvf) files. - -use std::env; -use std::path::Path; -use std::process::ExitCode; -use worldview_validator::{validate, validate_file}; - -fn print_usage(program: &str) { - eprintln!("Usage: {} ", program); - eprintln!(" {} --stdin", program); - eprintln!(); - eprintln!("Validates a Worldview format file for syntactic correctness."); - eprintln!(); - eprintln!("Options:"); - eprintln!(" --stdin Read from standard input instead of a file"); - eprintln!(" --help Show this help message"); - eprintln!(" --version Show version information"); -} - -fn print_version() { - eprintln!("worldview-validate {}", env!("CARGO_PKG_VERSION")); -} - -fn main() -> ExitCode { - let args: Vec = env::args().collect(); - let program = &args[0]; - - if args.len() < 2 { - print_usage(program); - return ExitCode::from(1); - } - - let arg = &args[1]; - - if arg == "--help" || arg == "-h" { - print_usage(program); - return ExitCode::SUCCESS; - } - - if arg == "--version" || arg == "-V" { - print_version(); - return ExitCode::SUCCESS; - } - - let result = if arg == "--stdin" { - // Read from stdin - let mut input = String::new(); - if let Err(e) = std::io::Read::read_to_string(&mut std::io::stdin(), &mut input) { - eprintln!("Error reading from stdin: {}", e); - return ExitCode::from(1); - } - validate(&input) - } else { - // Read from file - let path = Path::new(arg); - - if !path.exists() { - eprintln!("Error: File '{}' not found", arg); - return ExitCode::from(1); - } - - // Check file extension - if let Some(ext) = path.extension() { - if ext.to_ascii_lowercase() != "wvf" { - eprintln!("Warning: File does not have .wvf extension"); - } - } - - match validate_file(path) { - Ok(r) => r, - Err(e) => { - eprintln!("Error reading file '{}': {}", arg, e); - return ExitCode::from(1); - } - } - }; - - if result.is_valid() { - if result.has_warnings() { - println!("Valid Worldview document with {} warning(s):", result.warnings.len()); - for warning in &result.warnings { - println!(" {}", warning); - } - } else { - println!("Valid Worldview document"); - } - ExitCode::SUCCESS - } else { - eprintln!("Invalid Worldview document ({} error(s)):", result.errors.len()); - for error in &result.errors { - eprintln!(" {}", error); - } - if result.has_warnings() { - eprintln!("Additionally, {} warning(s):", result.warnings.len()); - for warning in &result.warnings { - eprintln!(" {}", warning); - } - } - ExitCode::from(1) - } -} From 8954184140c3dcd977b5306b313658dc42b3f063 Mon Sep 17 00:00:00 2001 From: Travis Dent Date: Wed, 21 Jan 2026 15:49:22 -0800 Subject: [PATCH 6/8] Fix build workflow: use cd instead of -p flag --- .github/workflows/build.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index ec11162..45cc17d 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -82,7 +82,7 @@ jobs: ${{ runner.os }}-cargo- - name: Build validator library - run: cargo build --release -p worldview-validator + run: cd validator && cargo build --release - name: Build CLI run: | @@ -91,7 +91,7 @@ jobs: cargo build --release - name: Run validator tests - run: cargo test -p worldview-validator + run: cd validator && cargo test - name: Collect binaries run: | From c5ecbe4a18de50314fc4ed78d8cb7190e61a1d47 Mon Sep 17 00:00:00 2001 From: Travis Dent Date: Wed, 21 Jan 2026 15:54:02 -0800 Subject: [PATCH 7/8] Fix evals: add 'add' subcommand to CLI invocation --- evals/write_eval/runner.py | 1 + 1 file changed, 1 insertion(+) diff --git a/evals/write_eval/runner.py b/evals/write_eval/runner.py index 05fecb5..6d50af5 100644 --- a/evals/write_eval/runner.py +++ b/evals/write_eval/runner.py @@ -129,6 +129,7 @@ def _run_agent( # Build command with verbose flag cmd = [ self.agent_cli_path, + "add", fact_statement, "--file", temp_path, "--model", model_id, From ecd09224feaef421638d60b5bca420a5429a4877 Mon Sep 17 00:00:00 2001 From: Travis Dent Date: Wed, 21 Jan 2026 16:06:36 -0800 Subject: [PATCH 8/8] Fix read eval: add 'add' subcommand to CLI invocation --- evals/read_eval/runner.py | 1 + 1 file changed, 1 insertion(+) diff --git a/evals/read_eval/runner.py b/evals/read_eval/runner.py index c8dbb1f..565b778 100644 --- a/evals/read_eval/runner.py +++ b/evals/read_eval/runner.py @@ -87,6 +87,7 @@ def _generate_worldview_with_cli( try: cmd = [ self.worldview_cli_path, + "add", fact_statement, "--file", worldview_path,