hyperweave 0.3.5__tar.gz → 0.3.6__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {hyperweave-0.3.5 → hyperweave-0.3.6}/CHANGELOG.md +23 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/PKG-INFO +1 -1
- {hyperweave-0.3.5 → hyperweave-0.3.6}/justfile +10 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/_version.py +2 -2
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/cli.py +178 -44
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/compose/context.py +1 -1
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/compose/resolver.py +47 -19
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/telemetry/codex_parser.py +5 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/telemetry/contract.py +5 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/telemetry/models.py +12 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/telemetry/parser.py +6 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/tests/test_doctor.py +53 -4
- {hyperweave-0.3.5 → hyperweave-0.3.6}/tests/test_install_hook.py +122 -7
- {hyperweave-0.3.5 → hyperweave-0.3.6}/tests/test_telemetry_integration.py +70 -1
- {hyperweave-0.3.5 → hyperweave-0.3.6}/.dockerignore +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/.github/workflows/ci.yml +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/.github/workflows/deploy.yml +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/.github/workflows/publish.yml +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/.gitignore +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/.pre-commit-config.yaml +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/Dockerfile +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/LICENSE +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/README.md +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/assets/buttons/button-liquid.svg +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/assets/cards/card-butterfly.svg +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/assets/cards/card-python.svg +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/assets/cards/card-sunflower.svg +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/assets/cards/card-waves.svg +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/assets/examples/brutalist-emerald/badge_critical.svg +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/assets/examples/brutalist-emerald/badge_passing.svg +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/assets/examples/brutalist-emerald/badge_warning.svg +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/assets/examples/brutalist-emerald/banner.svg +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/assets/examples/brutalist-emerald/icons/discord.svg +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/assets/examples/brutalist-emerald/icons/notion.svg +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/assets/examples/brutalist-emerald/icons/reddit.svg +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/assets/examples/brutalist-emerald/icons/spotify.svg +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/assets/examples/brutalist-emerald/icons/youtube.svg +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/assets/examples/brutalist-emerald/marquee_counter.svg +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/assets/examples/brutalist-emerald/marquee_horizontal.svg +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/assets/examples/brutalist-emerald/marquee_vertical.svg +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/assets/examples/brutalist-emerald/profile-cards/chart_stars_full.svg +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/assets/examples/brutalist-emerald/profile-cards/stats.svg +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/assets/examples/brutalist-emerald/strip.svg +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/assets/examples/chrome-horizon/badge_critical.svg +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/assets/examples/chrome-horizon/badge_passing.svg +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/assets/examples/chrome-horizon/badge_warning.svg +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/assets/examples/chrome-horizon/banner.svg +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/assets/examples/chrome-horizon/icons/bluesky.svg +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/assets/examples/chrome-horizon/icons/github.svg +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/assets/examples/chrome-horizon/icons/instagram.svg +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/assets/examples/chrome-horizon/icons/mastodon.svg +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/assets/examples/chrome-horizon/icons/x.svg +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/assets/examples/chrome-horizon/marquee_counter.svg +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/assets/examples/chrome-horizon/marquee_horizontal.svg +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/assets/examples/chrome-horizon/marquee_vertical.svg +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/assets/examples/chrome-horizon/profile-cards/chart_stars_full.svg +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/assets/examples/chrome-horizon/profile-cards/stats.svg +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/assets/examples/chrome-horizon/strip.svg +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/assets/examples/telemetry/master_card.svg +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/assets/examples/telemetry/receipt.svg +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/assets/examples/telemetry/receipt_claude-code_medium.svg +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/assets/examples/telemetry/receipt_codex_large.svg +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/assets/examples/telemetry/receipt_voltage_xlarge.svg +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/assets/examples/telemetry/rhythm_strip.svg +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/assets/examples/telemetry/rhythm_strip_voltage_xlarge.svg +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/assets/footers/inneraura-footer-liquid.svg +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/assets/footers/inneraura-footer-purple.svg +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/assets/footers/inneraura-footer.svg +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/assets/hyperweave-banner.svg +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/assets/icons/cobalt-sapphire-discord.svg +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/assets/icons/cobalt-sapphire-docs.svg +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/assets/icons/cobalt-sapphire-github.svg +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/assets/icons/cobalt-sapphire-instagram.svg +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/assets/icons/cobalt-sapphire-linkedin.svg +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/assets/icons/cobalt-sapphire-tiktok.svg +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/assets/icons/cobalt-sapphire-x.svg +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/assets/icons/cobalt-sapphire-youtube.svg +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/assets/marquees/badge-showcase-triple.svg +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/assets/marquees/genome-marquee-triple.svg +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/assets/marquees/sample-badges.svg +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/assets/mintlify-assets/404.svg +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/assets/mintlify-assets/callout-icons.svg +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/assets/mintlify-assets/divider.svg +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/assets/mintlify-assets/favicon.svg +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/assets/mintlify-assets/hero-banner.svg +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/assets/mintlify-assets/hyperweave-banner-v2.svg +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/assets/mintlify-assets/hyperweave-navbar-logo.svg +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/assets/mintlify-assets/loader.svg +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/assets/mintlify-assets/og-image.svg +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/assets/timelines/hyperweave-roadmap.svg +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/docs/assets/favicon.svg +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/docs/assets/hyperweave-banner.svg +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/docs/assets/hyperweave-navbar-logo.svg +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/docs/assets/og-image.svg +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/docs/explanation/architecture.mdx +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/docs/explanation/camo-compatibility.mdx +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/docs/explanation/cim-compliance.mdx +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/docs/explanation/genome-profile-system.mdx +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/docs/explanation/metadata-tiers.mdx +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/docs/how-to/add-motion-to-badges.mdx +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/docs/how-to/create-session-receipts.mdx +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/docs/how-to/set-up-claude-code-hooks.mdx +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/docs/how-to/use-live-data-badges.mdx +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/docs/introduction.mdx +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/docs/mint.json +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/docs/quickstart.mdx +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/docs/reference/cli.mdx +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/docs/reference/compose-spec.mdx +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/docs/reference/genomes.mdx +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/docs/reference/http-api.mdx +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/docs/reference/mcp-tools.mdx +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/docs/reference/motions.mdx +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/docs/reference/telemetry-contract.mdx +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/docs/spec/hyperweave-protocol.mdx +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/docs/tutorials/build-an-artifact-kit.mdx +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/docs/tutorials/compose-your-first-badge.mdx +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/docs/tutorials/install-session-telemetry.mdx +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/fly.toml +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/hooks/install.py +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/hooks/session_end.sh +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/pyproject.toml +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/scripts/extract_font_metrics.py +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/scripts/extract_glyphs.py +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/scripts/generate_proofset.py +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/scripts/probe_live_stats.py +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/scripts/probe_star_history.py +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/scripts/stress_test.py +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/__init__.py +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/__main__.py +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/compose/__init__.py +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/compose/assembler.py +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/compose/bar_chart.py +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/compose/engine.py +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/compose/lanes.py +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/compose/layout.py +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/compose/palette.py +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/compose/reasoning.py +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/compose/resolvers/__init__.py +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/compose/resolvers/chart.py +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/compose/resolvers/stats.py +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/compose/rhythm.py +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/compose/rhythm_strip.py +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/compose/treemap.py +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/compose/validate_paradigms.py +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/config/__init__.py +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/config/genome_validator.py +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/config/loader.py +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/config/registry.py +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/config/settings.py +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/connectors/__init__.py +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/connectors/arxiv.py +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/connectors/base.py +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/connectors/cache.py +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/connectors/github.py +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/connectors/rest.py +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/core/__init__.py +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/core/cell_layout.py +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/core/color.py +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/core/contracts.py +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/core/enums.py +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/core/font_metrics.py +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/core/models.py +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/core/paradigm.py +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/core/schema.py +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/core/state.py +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/core/text.py +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/core/thresholds.py +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/data/badge_modes.yaml +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/data/css/accessibility.css +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/data/css/bridge.css +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/data/css/expression.css +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/data/css/status.css +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/data/font-metrics/chakra-petch.json +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/data/font-metrics/inter.json +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/data/font-metrics/jetbrains-mono.json +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/data/font-metrics/orbitron.json +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/data/fonts/barlow-condensed-700.b64 +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/data/fonts/barlow-condensed-700.meta.json +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/data/fonts/barlow-condensed-900.b64 +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/data/fonts/barlow-condensed-900.meta.json +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/data/fonts/chakra-petch.b64 +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/data/fonts/chakra-petch.meta.json +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/data/fonts/jetbrains-mono.b64 +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/data/fonts/jetbrains-mono.meta.json +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/data/fonts/orbitron.b64 +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/data/fonts/orbitron.meta.json +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/data/genomes/automata.json +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/data/genomes/brutalist.json +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/data/genomes/chrome.json +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/data/genomes/telemetry-claude-code.json +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/data/genomes/telemetry-codex.json +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/data/genomes/telemetry-cream.json +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/data/genomes/telemetry-voltage.json +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/data/glyphs.json +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/data/motions/border/chromatic-pulse.yaml +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/data/motions/border/corner-trace.yaml +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/data/motions/border/dual-orbit.yaml +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/data/motions/border/entanglement.yaml +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/data/motions/border/rimrun.yaml +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/data/motions/static.yaml +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/data/paradigms/brutalist.yaml +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/data/paradigms/cellular.yaml +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/data/paradigms/chrome.yaml +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/data/paradigms/default.yaml +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/data/policies/normal.json +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/data/policies/permissive.json +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/data/policies/ungoverned.json +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/data/profiles/brutalist.contract.json +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/data/profiles/brutalist.yaml +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/data/profiles/chrome.contract.json +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/data/profiles/chrome.yaml +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/data/reasoning/__init__.py +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/data/reasoning/brutalist.yaml +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/data/specimens.yaml +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/data/telemetry/model-pricing.yaml +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/data/telemetry/runtimes/claude-code.yaml +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/data/telemetry/runtimes/codex.yaml +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/data/telemetry/stage-config.yaml +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/data/telemetry/stage-labels.yaml +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/data/telemetry/tool-colors.yaml +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/data/terminals/arrow.json +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/data/terminals/aurora.json +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/data/terminals/beacon.json +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/data/terminals/bracket3d.json +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/data/terminals/chevron.json +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/data/terminals/crosshair.json +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/data/terminals/diamond.json +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/data/terminals/rocket.json +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/data/terminals/rules/bar.json +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/data/terminals/rules/dashed.json +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/data/terminals/rules/five-wave.json +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/data/terminals/rules/spectral.json +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/data/terminals/rules/straight.json +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/data/terminals/rules/wave.json +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/data/terminals/stijl.json +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/kit.py +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/mcp/__init__.py +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/mcp/__main__.py +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/mcp/server.py +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/py.typed +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/render/__init__.py +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/render/chart_engine.py +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/render/fonts.py +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/render/glyphs.py +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/render/motion.py +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/render/templates.py +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/serve/__init__.py +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/serve/app.py +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/serve/data_tokens.py +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/telemetry/__init__.py +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/telemetry/capture.py +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/telemetry/corrections.py +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/telemetry/cost.py +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/telemetry/receipt_paths.py +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/telemetry/runtimes.py +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/telemetry/stages.py +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/components/chart-area.svg.j2 +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/components/chart-axes.svg.j2 +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/components/chart-empty-state.svg.j2 +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/components/chart-gridlines.svg.j2 +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/components/chart-markers/circle.svg.j2 +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/components/chart-markers/diamond.svg.j2 +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/components/chart-markers/endpoint-diamond.svg.j2 +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/components/chart-markers/endpoint-rect.svg.j2 +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/components/chart-markers/rect.svg.j2 +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/components/chart-milestone-brutalist.svg.j2 +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/components/chart-milestone.svg.j2 +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/components/chart-polyline.svg.j2 +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/components/filter.svg.j2 +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/components/glyph-inline.svg.j2 +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/components/glyph.svg.j2 +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/components/gradient.svg.j2 +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/components/metadata.svg.j2 +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/components/metric.svg.j2 +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/components/phase-segment.svg.j2 +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/components/rule.svg.j2 +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/components/status.svg.j2 +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/components/terminal.svg.j2 +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/document.svg.j2 +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/error-badge.svg.j2 +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/frames/badge/brutalist-content.j2 +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/frames/badge/brutalist-dark-content.j2 +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/frames/badge/brutalist-defs.j2 +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/frames/badge/brutalist-light-content.j2 +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/frames/badge/cellular-content.j2 +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/frames/badge/cellular-defs.j2 +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/frames/badge/chrome-content.j2 +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/frames/badge/chrome-defs.j2 +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/frames/badge/default-content.j2 +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/frames/badge/default-defs.j2 +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/frames/badge.svg.j2 +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/frames/catalog.svg.j2 +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/frames/chart/brutalist-content.j2 +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/frames/chart/brutalist-dark-content.j2 +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/frames/chart/brutalist-defs.j2 +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/frames/chart/brutalist-light-content.j2 +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/frames/chart/cellular-content.j2 +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/frames/chart/cellular-defs.j2 +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/frames/chart/chrome-content.j2 +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/frames/chart/chrome-defs.j2 +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/frames/chart.svg.j2 +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/frames/divider/brutalist-seam.svg.j2 +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/frames/divider/chrome-band.svg.j2 +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/frames/divider.svg.j2 +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/frames/icon/brutalist-content.j2 +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/frames/icon/brutalist-defs.j2 +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/frames/icon/cellular-content.j2 +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/frames/icon/cellular-defs.j2 +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/frames/icon/chrome-content.j2 +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/frames/icon/chrome-defs.j2 +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/frames/icon/default-content.j2 +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/frames/icon/default-defs.j2 +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/frames/icon.svg.j2 +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/frames/marquee-horizontal/brutalist-content.j2 +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/frames/marquee-horizontal/brutalist-defs.j2 +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/frames/marquee-horizontal/brutalist-overlay.j2 +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/frames/marquee-horizontal/cellular-content.j2 +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/frames/marquee-horizontal/cellular-defs.j2 +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/frames/marquee-horizontal/cellular-overlay.j2 +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/frames/marquee-horizontal/chrome-content.j2 +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/frames/marquee-horizontal/chrome-defs.j2 +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/frames/marquee-horizontal/chrome-overlay.j2 +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/frames/marquee-horizontal/default-content.j2 +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/frames/marquee-horizontal/default-defs.j2 +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/frames/marquee-horizontal.svg.j2 +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/frames/master-card.svg.j2 +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/frames/receipt.svg.j2 +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/frames/rhythm-strip.svg.j2 +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/frames/stats/brutalist-content.j2 +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/frames/stats/brutalist-dark-content.j2 +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/frames/stats/brutalist-defs.j2 +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/frames/stats/brutalist-light-content.j2 +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/frames/stats/cellular-content.j2 +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/frames/stats/cellular-defs.j2 +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/frames/stats/chrome-content.j2 +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/frames/stats/chrome-defs.j2 +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/frames/stats.svg.j2 +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/frames/strip/brutalist-content.j2 +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/frames/strip/brutalist-dark-content.j2 +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/frames/strip/brutalist-defs.j2 +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/frames/strip/brutalist-light-content.j2 +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/frames/strip/brutalist-status.j2 +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/frames/strip/cellular-content.j2 +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/frames/strip/cellular-defs.j2 +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/frames/strip/cellular-status.j2 +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/frames/strip/chrome-content.j2 +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/frames/strip/chrome-defs.j2 +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/frames/strip/chrome-status.j2 +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/frames/strip/default-content.j2 +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/frames/strip/default-defs.j2 +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/frames/strip/default-status.j2 +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/frames/strip.svg.j2 +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/motions/border/chromatic-pulse.svg.j2 +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/motions/border/corner-trace.svg.j2 +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/motions/border/dual-orbit.svg.j2 +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/motions/border/entanglement.svg.j2 +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/motions/border/rimrun.svg.j2 +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/partials/provider-glyphs.svg.j2 +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/src/hyperweave/templates/partials/state-signal-cascade.j2 +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/tests/__init__.py +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/tests/conftest.py +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/tests/fixtures/codex_session.jsonl +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/tests/fixtures/codex_session_patches.jsonl +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/tests/fixtures/github_contributions/synthetic.html +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/tests/fixtures/session.json +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/tests/fixtures/session.jsonl +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/tests/fixtures/synthetic_session.jsonl +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/tests/helpers.py +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/tests/snapshots/url_stability/divider_band_chrome_static.svg +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/tests/snapshots/url_stability/icon_github_chrome_static.svg +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/tests/test_assembler.py +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/tests/test_badge_brutalist_dark.py +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/tests/test_badge_cellular.py +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/tests/test_badge_layout.py +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/tests/test_badge_mode.py +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/tests/test_chart_engine.py +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/tests/test_chart_frame.py +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/tests/test_chromatic_coverage_validator.py +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/tests/test_chrome_variants.py +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/tests/test_codex_parser.py +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/tests/test_compose_bar_chart.py +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/tests/test_compose_rhythm_strip.py +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/tests/test_compose_treemap.py +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/tests/test_compose_variant_field.py +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/tests/test_connectors.py +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/tests/test_core.py +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/tests/test_data_tokens.py +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/tests/test_divider_cellular.py +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/tests/test_divider_genome_specific.py +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/tests/test_font_gating.py +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/tests/test_font_metrics_chakra.py +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/tests/test_genome_automata.py +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/tests/test_github_scrape.py +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/tests/test_kit.py +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/tests/test_marquee_v0_2_16.py +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/tests/test_mcp.py +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/tests/test_paradigm_cellular.py +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/tests/test_paradigm_dispatch.py +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/tests/test_paradigm_extensibility.py +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/tests/test_parse_transcript_auto.py +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/tests/test_proofset.py +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/tests/test_receipt_paths.py +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/tests/test_receipt_token_compliance.py +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/tests/test_render.py +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/tests/test_resolver_rhythm.py +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/tests/test_runtime_registries.py +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/tests/test_serve.py +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/tests/test_serve_live_state.py +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/tests/test_slot_metric_state.py +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/tests/test_state_inference.py +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/tests/test_stats_brutalist.py +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/tests/test_stats_card.py +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/tests/test_strip_brutalist_grammar.py +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/tests/test_strip_cellular.py +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/tests/test_strip_status_toggle.py +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/tests/test_telemetry.py +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/tests/test_telemetry_contract_per_stage.py +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/tests/test_telemetry_skin_dispatch.py +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/tests/test_tool_class_completeness.py +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/tests/test_url_stability.py +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/tests/test_variant_default_resolution.py +0 -0
- {hyperweave-0.3.5 → hyperweave-0.3.6}/uv.lock +0 -0
|
@@ -5,6 +5,29 @@ All notable changes to HyperWeave are documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [0.3.6] - 2026-05-18
|
|
9
|
+
|
|
10
|
+
Receipt active-duration sources from per-turn compute when the runtime emits it; long-running sessions no longer inflate via stage-span summation. Codex hooks migrate to the wrapped-matcher GA layout shipped in the Codex CLI v0.129 release. The `data-hw-genome` root attribute reflects the resolved genome ID instead of raw spec input.
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- **`just tag VERSION MESSAGE`** — annotated tag plus automatic `_version.py` refresh so the `doctor` banner and metadata version stay in sync after release.
|
|
15
|
+
- **`just version-refresh`** — standalone recipe to rebuild `_version.py` from the current git tag.
|
|
16
|
+
|
|
17
|
+
### Changed
|
|
18
|
+
|
|
19
|
+
- **Receipt active-duration** — Claude Code sessions source from `system.turn_duration` events; Codex and mocked fixtures fall back to the prior `min(stage-span sum, wall-clock span)` formula.
|
|
20
|
+
- **Session contract** — new `turn_duration_minutes` field surfaces the parser's per-turn compute sum; null when the runtime emits no per-turn signal.
|
|
21
|
+
- **Codex hooks file** — `install-hook` writes the wrapped-matcher GA format (Codex CLI v0.129+). Existing flat installs are migrated on next install.
|
|
22
|
+
- **Hooks feature flag** — `codex_hooks` renames to `hooks` to match Codex's GA terminology; the legacy name continues to work for one release.
|
|
23
|
+
- **`hyperweave doctor`** — traverses both the new wrapped format and the legacy flat layout, surfacing a migration prompt when a flat config is detected.
|
|
24
|
+
- **Genome root attribute** — `data-hw-genome` reflects the resolved genome ID instead of raw spec input, so variants and auto-resolved genomes never emit empty.
|
|
25
|
+
|
|
26
|
+
### Notes
|
|
27
|
+
|
|
28
|
+
- Codex sessions hit the active-duration fallback because the runtime emits no per-turn duration signal.
|
|
29
|
+
- `_version.py` refreshes automatically when tagging through `just tag`.
|
|
30
|
+
|
|
8
31
|
## [0.3.5] - 2026-05-17
|
|
9
32
|
|
|
10
33
|
Receipt hero reads `token volume · $cost` with a four-cell decomposition strip below (IN / OUT / CACHED / WRITTEN), making the asymmetric cache-token math legible at a glance. Codex receipts now price tokens against the OpenAI rate card; figures previously inflated ~2× drop to actual GPT rates.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: hyperweave
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.6
|
|
4
4
|
Summary: Headless visual output layer for AI agents. One API call → self-contained SVG.
|
|
5
5
|
Project-URL: Repository, https://github.com/InnerAura/hyperweave
|
|
6
6
|
Project-URL: Issues, https://github.com/InnerAura/hyperweave/issues
|
|
@@ -48,3 +48,13 @@ docs:
|
|
|
48
48
|
|
|
49
49
|
build:
|
|
50
50
|
uv build
|
|
51
|
+
|
|
52
|
+
version-refresh:
|
|
53
|
+
uv pip install -e . --force-reinstall --no-deps --quiet
|
|
54
|
+
@uv run python -c "import hyperweave; print(f'_version.py refreshed to {hyperweave.__version__}')"
|
|
55
|
+
|
|
56
|
+
tag VERSION MESSAGE:
|
|
57
|
+
git tag -a {{VERSION}} -m "{{MESSAGE}}"
|
|
58
|
+
just version-refresh
|
|
59
|
+
@echo ""
|
|
60
|
+
@echo "Tagged {{VERSION}}. Push with: git push --follow-tags"
|
|
@@ -18,7 +18,7 @@ version_tuple: tuple[int | str, ...]
|
|
|
18
18
|
commit_id: str | None
|
|
19
19
|
__commit_id__: str | None
|
|
20
20
|
|
|
21
|
-
__version__ = version = '0.3.
|
|
22
|
-
__version_tuple__ = version_tuple = (0, 3,
|
|
21
|
+
__version__ = version = '0.3.6'
|
|
22
|
+
__version_tuple__ = version_tuple = (0, 3, 6)
|
|
23
23
|
|
|
24
24
|
__commit_id__ = commit_id = None
|
|
@@ -567,8 +567,45 @@ def _install_claude_code_hook(hook_command: str, full_slug: str) -> None:
|
|
|
567
567
|
typer.echo(f"Installed SessionEnd hook in {settings_path}{pinned}")
|
|
568
568
|
|
|
569
569
|
|
|
570
|
+
# Codex hook event names (v0.129.0 GA). Used by ``_install_codex_hook`` and
|
|
571
|
+
# ``_doctor_runtime_status`` to detect pre-GA flat-format event keys sitting
|
|
572
|
+
# at the top of ``hooks.json`` and lift them under the canonical ``hooks``
|
|
573
|
+
# wrapper introduced when hooks went GA.
|
|
574
|
+
_CODEX_HOOK_EVENTS = frozenset(
|
|
575
|
+
{
|
|
576
|
+
"SessionStart",
|
|
577
|
+
"PreToolUse",
|
|
578
|
+
"PermissionRequest",
|
|
579
|
+
"PostToolUse",
|
|
580
|
+
"UserPromptSubmit",
|
|
581
|
+
"Stop",
|
|
582
|
+
}
|
|
583
|
+
)
|
|
584
|
+
|
|
585
|
+
|
|
586
|
+
def _wrap_legacy_codex_hook_entry(entry: object) -> object:
|
|
587
|
+
"""Lift a pre-GA bare-command hook entry into the GA matcher+hooks group.
|
|
588
|
+
|
|
589
|
+
Codex v0.129 (hooks GA) changed each event-array entry from a bare
|
|
590
|
+
``{type, command, timeout}`` to a matcher group
|
|
591
|
+
``{matcher, hooks: [{type, command, timeout}]}``. Idempotent: an entry
|
|
592
|
+
already carrying a list-valued ``hooks`` key is returned unchanged so
|
|
593
|
+
repeated migrations stay stable. Bare-command entries are wrapped under
|
|
594
|
+
a universal ``"*"`` matcher; the matcher is parsed-but-ignored for Stop
|
|
595
|
+
today per the spec, but we use ``"*"`` for forward-compat against any
|
|
596
|
+
future Codex release that begins to honor it on Stop.
|
|
597
|
+
"""
|
|
598
|
+
if not isinstance(entry, dict):
|
|
599
|
+
return entry
|
|
600
|
+
if isinstance(entry.get("hooks"), list):
|
|
601
|
+
return entry # already in GA shape
|
|
602
|
+
if entry.get("type") == "command":
|
|
603
|
+
return {"matcher": "*", "hooks": [entry]}
|
|
604
|
+
return entry # unknown shape — preserve as-is
|
|
605
|
+
|
|
606
|
+
|
|
570
607
|
def _install_codex_hook(hook_command: str, full_slug: str) -> None:
|
|
571
|
-
"""Write a Stop hook to ``~/.codex/hooks.json`` + enable
|
|
608
|
+
"""Write a Stop hook to ``~/.codex/hooks.json`` + enable hooks feature.
|
|
572
609
|
|
|
573
610
|
Per developers.openai.com/codex/hooks, Codex CLI fires Stop hooks
|
|
574
611
|
PER-TURN (after every assistant response). The receipt rewrites the
|
|
@@ -579,9 +616,21 @@ def _install_codex_hook(hook_command: str, full_slug: str) -> None:
|
|
|
579
616
|
will lean into live-receipt consumers (file watchers, dashboards)
|
|
580
617
|
rather than collapse it back to a single session-end event.
|
|
581
618
|
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
619
|
+
Codex v0.129.0 (2026-05-07) took hooks GA with two shape changes the
|
|
620
|
+
installer handles via migration-on-write:
|
|
621
|
+
|
|
622
|
+
* ``hooks.json`` moved from flat ``{Stop: [{type, command, timeout}]}``
|
|
623
|
+
to wrapped ``{hooks: {Stop: [{matcher, hooks: [{type, command,
|
|
624
|
+
timeout}]}]}}``. Any pre-GA event keys at the top level are lifted
|
|
625
|
+
under the new wrapper; each bare-command entry is wrapped under a
|
|
626
|
+
universal ``"*"`` matcher group.
|
|
627
|
+
* ``[features].codex_hooks`` was aliased as ``[features].hooks``. The
|
|
628
|
+
installer strips any legacy ``codex_hooks`` entry and writes the
|
|
629
|
+
canonical ``hooks = true``.
|
|
630
|
+
|
|
631
|
+
Both migrations are idempotent — re-running install-hook over any
|
|
632
|
+
combination of pre-GA, partially-migrated, or GA configs converges to
|
|
633
|
+
the canonical GA shape with exactly one hyperweave entry.
|
|
585
634
|
"""
|
|
586
635
|
import json
|
|
587
636
|
|
|
@@ -590,48 +639,99 @@ def _install_codex_hook(hook_command: str, full_slug: str) -> None:
|
|
|
590
639
|
config_path = codex_dir / "config.toml"
|
|
591
640
|
codex_dir.mkdir(parents=True, exist_ok=True)
|
|
592
641
|
|
|
593
|
-
# ── Update hooks.json:
|
|
642
|
+
# ── Update hooks.json: lift any pre-GA flat keys into the wrapper, then
|
|
643
|
+
# operate on hooks.Stop as the canonical GA structure ──
|
|
594
644
|
config: dict[str, object] = {}
|
|
595
645
|
if hooks_path.exists():
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
646
|
+
loaded = json.loads(hooks_path.read_text())
|
|
647
|
+
if isinstance(loaded, dict):
|
|
648
|
+
config = loaded
|
|
649
|
+
|
|
650
|
+
# GA wrapper: hooks lives under config["hooks"]. Pre-GA configs may not
|
|
651
|
+
# have it yet; create it (or reset if it's the wrong shape).
|
|
652
|
+
wrapper_raw = config.setdefault("hooks", {})
|
|
653
|
+
hooks_wrapper: dict[str, object]
|
|
654
|
+
if isinstance(wrapper_raw, dict):
|
|
655
|
+
hooks_wrapper = wrapper_raw
|
|
656
|
+
else:
|
|
657
|
+
hooks_wrapper = {}
|
|
658
|
+
config["hooks"] = hooks_wrapper
|
|
659
|
+
|
|
660
|
+
# Lift any pre-GA top-level event keys (Stop, PreToolUse, etc.) into the
|
|
661
|
+
# wrapper, wrapping bare-command entries with a universal matcher group.
|
|
662
|
+
# Iterate over a snapshot of keys since we mutate during traversal.
|
|
663
|
+
for legacy_event in list(config.keys()):
|
|
664
|
+
if legacy_event == "hooks" or legacy_event not in _CODEX_HOOK_EVENTS:
|
|
665
|
+
continue
|
|
666
|
+
legacy_value = config.pop(legacy_event)
|
|
667
|
+
if not isinstance(legacy_value, list):
|
|
668
|
+
continue
|
|
669
|
+
target_raw = hooks_wrapper.setdefault(legacy_event, [])
|
|
670
|
+
target: list[object]
|
|
671
|
+
if isinstance(target_raw, list):
|
|
672
|
+
target = target_raw
|
|
673
|
+
else:
|
|
674
|
+
target = []
|
|
675
|
+
hooks_wrapper[legacy_event] = target
|
|
676
|
+
for entry in legacy_value:
|
|
677
|
+
target.append(_wrap_legacy_codex_hook_entry(entry))
|
|
678
|
+
|
|
679
|
+
# Now drop any prior hyperweave matcher groups under hooks.Stop and
|
|
680
|
+
# append the fresh one. A hyperweave group is identified by ANY inner
|
|
681
|
+
# handler whose command mentions "hyperweave session".
|
|
682
|
+
raw_stop = hooks_wrapper.setdefault("Stop", [])
|
|
683
|
+
stop_groups: list[object]
|
|
684
|
+
if isinstance(raw_stop, list):
|
|
685
|
+
stop_groups = raw_stop
|
|
686
|
+
else:
|
|
687
|
+
stop_groups = []
|
|
688
|
+
hooks_wrapper["Stop"] = stop_groups
|
|
602
689
|
|
|
603
|
-
cleaned = []
|
|
604
|
-
for
|
|
605
|
-
if not isinstance(
|
|
606
|
-
cleaned.append(
|
|
690
|
+
cleaned: list[object] = []
|
|
691
|
+
for group in stop_groups:
|
|
692
|
+
if not isinstance(group, dict):
|
|
693
|
+
cleaned.append(group)
|
|
607
694
|
continue
|
|
608
|
-
|
|
609
|
-
if
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
695
|
+
inner_hooks = group.get("hooks", [])
|
|
696
|
+
if isinstance(inner_hooks, list) and any(
|
|
697
|
+
isinstance(h, dict) and "hyperweave session" in str(h.get("command", "")) for h in inner_hooks
|
|
698
|
+
):
|
|
699
|
+
continue # drop the whole matcher group — owned by hyperweave
|
|
700
|
+
cleaned.append(group)
|
|
701
|
+
cleaned.append(
|
|
702
|
+
{
|
|
703
|
+
"matcher": "*",
|
|
704
|
+
"hooks": [{"type": "command", "command": hook_command, "timeout": 10}],
|
|
705
|
+
}
|
|
706
|
+
)
|
|
707
|
+
hooks_wrapper["Stop"] = cleaned
|
|
614
708
|
hooks_path.write_text(json.dumps(config, indent=2) + "\n")
|
|
615
709
|
|
|
616
|
-
# ── Update config.toml: ensure [features]
|
|
710
|
+
# ── Update config.toml: ensure [features] hooks = true (preserve other keys) ──
|
|
711
|
+
# Codex v0.130.0 renamed the gate from `codex_hooks` to `hooks`; strip any
|
|
712
|
+
# legacy key on the way in so re-running install-hook over an older config
|
|
713
|
+
# upgrades cleanly instead of leaving a dead key. Key detection is exact-
|
|
714
|
+
# match (split-on-=, strip), not prefix, so `hooks_*` lookalikes can't
|
|
715
|
+
# spoof a hit.
|
|
617
716
|
config_lines: list[str] = []
|
|
618
717
|
if config_path.exists():
|
|
619
718
|
config_lines = config_path.read_text().splitlines()
|
|
719
|
+
config_lines = [line for line in config_lines if line.split("=", 1)[0].strip() != "codex_hooks"]
|
|
620
720
|
has_features_section = any(line.strip() == "[features]" for line in config_lines)
|
|
621
|
-
|
|
721
|
+
has_hooks_key = any("=" in line and line.split("=", 1)[0].strip() == "hooks" for line in config_lines)
|
|
622
722
|
if not has_features_section:
|
|
623
|
-
config_lines.extend(["", "[features]", "
|
|
624
|
-
elif not
|
|
625
|
-
# Insert
|
|
723
|
+
config_lines.extend(["", "[features]", "hooks = true"])
|
|
724
|
+
elif not has_hooks_key:
|
|
725
|
+
# Insert hooks = true right after [features] header
|
|
626
726
|
for i, line in enumerate(config_lines):
|
|
627
727
|
if line.strip() == "[features]":
|
|
628
|
-
config_lines.insert(i + 1, "
|
|
728
|
+
config_lines.insert(i + 1, "hooks = true")
|
|
629
729
|
break
|
|
630
730
|
config_path.write_text("\n".join(config_lines) + "\n")
|
|
631
731
|
|
|
632
732
|
pinned = f" (pinned to {full_slug})" if full_slug else " (auto-detect skin)"
|
|
633
733
|
typer.echo(f"Installed Stop hook in {hooks_path}{pinned}")
|
|
634
|
-
typer.echo(f"Set [features]
|
|
734
|
+
typer.echo(f"Set [features] hooks = true in {config_path}")
|
|
635
735
|
typer.echo(
|
|
636
736
|
"Note: Codex Stop fires per-turn — the receipt file refreshes live as the "
|
|
637
737
|
"session progresses, always reflecting current cumulative state.",
|
|
@@ -702,7 +802,7 @@ def install_hook(
|
|
|
702
802
|
"auto-detects installed runtimes (~/.claude, ~/.codex, or 'claude'/"
|
|
703
803
|
"'codex' on PATH) and registers for each present. 'claude-code' "
|
|
704
804
|
"writes a SessionEnd hook to ~/.claude/settings.json. 'codex' writes "
|
|
705
|
-
"a Stop hook to ~/.codex/hooks.json plus [features]
|
|
805
|
+
"a Stop hook to ~/.codex/hooks.json plus [features] hooks in "
|
|
706
806
|
"~/.codex/config.toml. 'all' forces both regardless of detection."
|
|
707
807
|
),
|
|
708
808
|
),
|
|
@@ -771,8 +871,8 @@ def _doctor_runtime_status(runtime: str, home_dir: Path) -> str:
|
|
|
771
871
|
Returns ``✓`` when the hyperweave hook is wired correctly, ``✗`` when
|
|
772
872
|
the runtime is initialized but no hyperweave hook is registered, or
|
|
773
873
|
``⚠`` when the wiring is partial (malformed config; codex missing the
|
|
774
|
-
[features]
|
|
775
|
-
``doctor``.
|
|
874
|
+
[features] hooks flag — renamed from ``codex_hooks`` in Codex v0.130.0).
|
|
875
|
+
The string is rendered verbatim by ``doctor``.
|
|
776
876
|
"""
|
|
777
877
|
import json
|
|
778
878
|
|
|
@@ -802,24 +902,56 @@ def _doctor_runtime_status(runtime: str, home_dir: Path) -> str:
|
|
|
802
902
|
hooks_path = home_dir / "hooks.json"
|
|
803
903
|
config_path = home_dir / "config.toml"
|
|
804
904
|
registered_cmd: str | None = None
|
|
905
|
+
legacy_flat_cmd: str | None = None
|
|
805
906
|
if hooks_path.exists():
|
|
806
907
|
try:
|
|
807
908
|
hooks = json.loads(hooks_path.read_text())
|
|
808
909
|
except json.JSONDecodeError:
|
|
809
910
|
return f"⚠ {runtime}: ~/.codex/hooks.json is malformed"
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
911
|
+
# GA traversal (Codex v0.129+):
|
|
912
|
+
# hooks["hooks"]["Stop"][group].hooks[handler].command
|
|
913
|
+
wrapper = hooks.get("hooks") if isinstance(hooks, dict) else None
|
|
914
|
+
if isinstance(wrapper, dict):
|
|
915
|
+
for group in wrapper.get("Stop") or []:
|
|
916
|
+
if not isinstance(group, dict):
|
|
917
|
+
continue
|
|
918
|
+
for handler in group.get("hooks") or []:
|
|
919
|
+
if not isinstance(handler, dict):
|
|
920
|
+
continue
|
|
921
|
+
cmd = str(handler.get("command", ""))
|
|
922
|
+
if "hyperweave session" in cmd:
|
|
923
|
+
registered_cmd = cmd
|
|
924
|
+
break
|
|
925
|
+
if registered_cmd:
|
|
926
|
+
break
|
|
927
|
+
# Pre-GA flat fallback (kept for one release): hooks["Stop"][entry]
|
|
928
|
+
# with command directly on the entry. Detected separately so a
|
|
929
|
+
# legacy install surfaces as ⚠ with an upgrade pointer instead of
|
|
930
|
+
# silently misreporting the hook as missing.
|
|
931
|
+
if not registered_cmd and isinstance(hooks, dict):
|
|
932
|
+
for entry in hooks.get("Stop") or []:
|
|
933
|
+
if not isinstance(entry, dict):
|
|
934
|
+
continue
|
|
935
|
+
cmd = str(entry.get("command", ""))
|
|
936
|
+
if "hyperweave session" in cmd:
|
|
937
|
+
legacy_flat_cmd = cmd
|
|
938
|
+
break
|
|
939
|
+
if legacy_flat_cmd and not registered_cmd:
|
|
940
|
+
return (
|
|
941
|
+
f"⚠ {runtime}: hook registered in legacy pre-GA flat format — "
|
|
942
|
+
f"re-run 'hyperweave install-hook --runtime {runtime}' to lift "
|
|
943
|
+
f"it to the GA wrapped structure (Codex v0.129+)"
|
|
944
|
+
)
|
|
817
945
|
if not registered_cmd:
|
|
818
946
|
return (
|
|
819
947
|
f"✗ {runtime}: initialized but no hyperweave hook — run 'hyperweave install-hook --runtime {runtime}'"
|
|
820
948
|
)
|
|
821
|
-
# [features]
|
|
822
|
-
#
|
|
949
|
+
# [features] hooks = true must be present for the hook to fire; the
|
|
950
|
+
# install command writes it, but a hand-edited config could miss it.
|
|
951
|
+
# Codex v0.130.0 renamed the gate from `codex_hooks` to `hooks`; key
|
|
952
|
+
# detection is exact-match within the [features] section so a stale
|
|
953
|
+
# `codex_hooks = true` left over from older installs is not mistaken
|
|
954
|
+
# for the live gate (and so `hooks_*` lookalikes can't spoof a hit).
|
|
823
955
|
feature_ok = False
|
|
824
956
|
if config_path.exists():
|
|
825
957
|
in_features = False
|
|
@@ -831,12 +963,14 @@ def _doctor_runtime_status(runtime: str, home_dir: Path) -> str:
|
|
|
831
963
|
if in_features and stripped.startswith("["):
|
|
832
964
|
in_features = False
|
|
833
965
|
continue
|
|
834
|
-
if in_features and
|
|
835
|
-
|
|
836
|
-
|
|
966
|
+
if in_features and "=" in stripped:
|
|
967
|
+
key, _, value = stripped.partition("=")
|
|
968
|
+
if key.strip() == "hooks" and "true" in value:
|
|
969
|
+
feature_ok = True
|
|
970
|
+
break
|
|
837
971
|
if not feature_ok:
|
|
838
972
|
return (
|
|
839
|
-
f"⚠ {runtime}: hook registered but [features]
|
|
973
|
+
f"⚠ {runtime}: hook registered but [features] hooks = true "
|
|
840
974
|
f"is missing — re-run 'hyperweave install-hook --runtime {runtime}'"
|
|
841
975
|
)
|
|
842
976
|
return f"✓ {runtime}: hook registered — {registered_cmd}"
|
|
@@ -181,7 +181,7 @@ def _base_context(
|
|
|
181
181
|
"artifact_id": artifact_id,
|
|
182
182
|
"contract_id": artifact_id,
|
|
183
183
|
"frame_type": spec.type,
|
|
184
|
-
"genome_id": spec.genome_id,
|
|
184
|
+
"genome_id": resolved.genome.get("id", spec.genome_id),
|
|
185
185
|
"genome_category": resolved.genome.get("category", "dark"),
|
|
186
186
|
# v0.3.2: variant + substrate_kind plumbed through to templates.
|
|
187
187
|
# substrate_kind drives the brutalist split-template dispatcher
|
|
@@ -1872,44 +1872,65 @@ def _format_model_label(model: str) -> str:
|
|
|
1872
1872
|
return label
|
|
1873
1873
|
|
|
1874
1874
|
|
|
1875
|
-
def _active_window_minutes(
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
|
|
1875
|
+
def _active_window_minutes(
|
|
1876
|
+
stages: list[dict[str, Any]],
|
|
1877
|
+
fallback_m: float,
|
|
1878
|
+
turn_duration_m: float | None = None,
|
|
1879
|
+
) -> float:
|
|
1880
|
+
"""Active work duration in minutes.
|
|
1881
|
+
|
|
1882
|
+
Primary source is ``turn_duration_m`` — the parser's sum of per-turn
|
|
1883
|
+
compute durations from ``system.turn_duration`` events (Claude Code).
|
|
1884
|
+
This measures agent compute time directly and never absorbs idle gaps
|
|
1885
|
+
that the stage detector failed to break on (a multi-hour break inside
|
|
1886
|
+
a same-class working stretch silently inflates a stage's span).
|
|
1887
|
+
|
|
1888
|
+
Fallback is ``min(sum_of_stage_durations, wall_clock_first_to_last)``
|
|
1889
|
+
when the runtime emits no per-turn duration events (Codex) or when
|
|
1890
|
+
stages lack ISO timestamps (mock fixtures):
|
|
1891
|
+
* Sum_of_stages collapses inter-stage idle in sessions left open
|
|
1892
|
+
across multiple bursts over days.
|
|
1893
|
+
* Wall-clock cap defends against any future stage detector that
|
|
1894
|
+
produces overlapping spans (today's partition algorithm cannot,
|
|
1895
|
+
but the cap costs nothing).
|
|
1896
|
+
|
|
1897
|
+
The wall-clock cap applies to the turn-duration source too — defensive
|
|
1898
|
+
against malformed turn_duration values exceeding the session span.
|
|
1889
1899
|
|
|
1890
1900
|
Returns ``fallback_m`` when stages lack ISO timestamps (mock data) or
|
|
1891
1901
|
when parsing fails.
|
|
1892
1902
|
"""
|
|
1893
1903
|
if not stages:
|
|
1904
|
+
if turn_duration_m is not None and turn_duration_m > 0:
|
|
1905
|
+
return max(turn_duration_m, 1.0)
|
|
1894
1906
|
return fallback_m
|
|
1895
|
-
durations: list[float] = []
|
|
1896
1907
|
starts: list[datetime] = []
|
|
1897
1908
|
ends: list[datetime] = []
|
|
1909
|
+
durations: list[float] = []
|
|
1898
1910
|
for s in stages:
|
|
1899
1911
|
start = s.get("start")
|
|
1900
1912
|
end = s.get("end")
|
|
1901
1913
|
if not start or not end:
|
|
1914
|
+
if turn_duration_m is not None and turn_duration_m > 0:
|
|
1915
|
+
return max(turn_duration_m, 1.0)
|
|
1902
1916
|
return fallback_m
|
|
1903
1917
|
try:
|
|
1904
1918
|
t0 = datetime.fromisoformat(start)
|
|
1905
1919
|
t_end = datetime.fromisoformat(end)
|
|
1906
1920
|
except (ValueError, TypeError):
|
|
1921
|
+
if turn_duration_m is not None and turn_duration_m > 0:
|
|
1922
|
+
return max(turn_duration_m, 1.0)
|
|
1907
1923
|
return fallback_m
|
|
1908
1924
|
starts.append(t0)
|
|
1909
1925
|
ends.append(t_end)
|
|
1910
1926
|
durations.append((t_end - t0).total_seconds() / 60.0)
|
|
1911
|
-
sum_m = sum(durations)
|
|
1912
1927
|
wall_clock_m = (max(ends) - min(starts)).total_seconds() / 60.0
|
|
1928
|
+
if turn_duration_m is not None and turn_duration_m > 0:
|
|
1929
|
+
# Cap defensively at wall-clock so a runtime emitting impossibly
|
|
1930
|
+
# large turn_duration values can't make the chart disagree with
|
|
1931
|
+
# itself; in practice turn_duration_m << wall_clock_m always.
|
|
1932
|
+
return max(min(turn_duration_m, wall_clock_m), 1.0)
|
|
1933
|
+
sum_m = sum(durations)
|
|
1913
1934
|
return max(min(sum_m, wall_clock_m), 1.0)
|
|
1914
1935
|
|
|
1915
1936
|
|
|
@@ -2009,10 +2030,17 @@ def resolve_receipt(
|
|
|
2009
2030
|
)
|
|
2010
2031
|
]
|
|
2011
2032
|
duration_m = session.get("duration_minutes", 0)
|
|
2012
|
-
|
|
2013
|
-
#
|
|
2033
|
+
turn_duration_m = session.get("turn_duration_minutes")
|
|
2034
|
+
# Active window: prefers the parser's per-turn compute sum (Claude
|
|
2035
|
+
# Code's `system.turn_duration` events) and falls back to
|
|
2036
|
+
# min(stage-span sum, wall-clock) when the runtime emits no per-turn
|
|
2037
|
+
# signal (Codex) or stages lack timestamps. Same value drives the
|
|
2014
2038
|
# chart geometry AND the hero subline, so they always agree.
|
|
2015
|
-
active_duration_m = _active_window_minutes(
|
|
2039
|
+
active_duration_m = _active_window_minutes(
|
|
2040
|
+
stages,
|
|
2041
|
+
float(duration_m),
|
|
2042
|
+
float(turn_duration_m) if turn_duration_m is not None else None,
|
|
2043
|
+
)
|
|
2016
2044
|
wall_clock_m = _wall_clock_minutes(stages, float(duration_m))
|
|
2017
2045
|
# ``total`` for the divergence flag: max of parser-reported duration and
|
|
2018
2046
|
# the stage-derived wall-clock. The parser may overstate (idle tail) or
|
|
@@ -373,6 +373,10 @@ def parse_transcript(transcript_path: str | Path) -> SessionTelemetry:
|
|
|
373
373
|
n_assistant_msgs += 1
|
|
374
374
|
|
|
375
375
|
# ── Compute timestamps + duration ──
|
|
376
|
+
# Codex emits no per-turn duration events analogous to Claude Code's
|
|
377
|
+
# `system.turn_duration`, so `turn_duration_minutes` stays None and the
|
|
378
|
+
# receipt's active-duration line falls back to min(stage-span sum,
|
|
379
|
+
# wall-clock span). Wall-clock is the only signal available here.
|
|
376
380
|
timestamps = [_parse_timestamp(line.get("timestamp")) for line in raw_lines if line.get("timestamp")]
|
|
377
381
|
session_start = min(timestamps) if timestamps else datetime.now()
|
|
378
382
|
session_end = max(timestamps) if timestamps else session_start
|
|
@@ -431,6 +435,7 @@ def parse_transcript(transcript_path: str | Path) -> SessionTelemetry:
|
|
|
431
435
|
runtime=_REGISTRY.runtime,
|
|
432
436
|
timestamp=session_start,
|
|
433
437
|
duration_minutes=round(duration_minutes, 2),
|
|
438
|
+
turn_duration_minutes=None, # Codex has no per-turn duration events
|
|
434
439
|
tool_calls=all_tool_calls,
|
|
435
440
|
stages=[], # populated by stages.detect_stages
|
|
436
441
|
agents=[], # Codex has no Task-style subagent dispatch
|
|
@@ -108,6 +108,11 @@ def _assemble(t: SessionTelemetry, cost: float) -> dict[str, Any]:
|
|
|
108
108
|
"start": t.timestamp.isoformat(),
|
|
109
109
|
"end": end,
|
|
110
110
|
"duration_minutes": t.duration_minutes,
|
|
111
|
+
# Per-turn compute sum when the runtime emits it (Claude Code's
|
|
112
|
+
# `system.turn_duration` events). None for Codex and any runtime
|
|
113
|
+
# without per-turn duration signal; the resolver falls back to
|
|
114
|
+
# min(stage-span sum, wall-clock) in that case.
|
|
115
|
+
"turn_duration_minutes": t.turn_duration_minutes,
|
|
111
116
|
"model": t.model or "",
|
|
112
117
|
"git_branch": t.git_branch or "",
|
|
113
118
|
"project_path": t.project_path or "",
|
|
@@ -271,6 +271,18 @@ class SessionTelemetry(BaseModel):
|
|
|
271
271
|
)
|
|
272
272
|
timestamp: datetime = Field(description="Session start time")
|
|
273
273
|
duration_minutes: float = Field(default=0.0, description="Wall-clock duration")
|
|
274
|
+
turn_duration_minutes: float | None = Field(
|
|
275
|
+
default=None,
|
|
276
|
+
description=(
|
|
277
|
+
"Sum of per-turn compute durations when the runtime emits them. "
|
|
278
|
+
"Claude Code emits `system.turn_duration` events and the parser "
|
|
279
|
+
"sums them here; Codex has no equivalent and leaves this None. "
|
|
280
|
+
"Receipts prefer this over stage-span sums because it counts "
|
|
281
|
+
"agent compute time directly and never absorbs idle gaps that "
|
|
282
|
+
"the stage detector failed to break on. None signals the "
|
|
283
|
+
"fallback path (min of stage-span sum and wall-clock span)."
|
|
284
|
+
),
|
|
285
|
+
)
|
|
274
286
|
tool_calls: list[ToolCall] = Field(
|
|
275
287
|
default_factory=list,
|
|
276
288
|
description="Flat ordered list of all tool calls",
|
|
@@ -368,10 +368,15 @@ def parse_transcript(transcript_path: str | Path) -> SessionTelemetry:
|
|
|
368
368
|
session_start = min(timestamps) if timestamps else datetime.now()
|
|
369
369
|
session_end = max(timestamps) if timestamps else session_start
|
|
370
370
|
|
|
371
|
+
# turn_duration_minutes is the receipt's primary "active" source when present —
|
|
372
|
+
# it measures per-turn compute time and ignores idle gaps the stage detector
|
|
373
|
+
# silently absorbs into a stage span. None signals the fallback path.
|
|
371
374
|
if total_duration_ms > 0:
|
|
372
375
|
duration_minutes = total_duration_ms / 60_000
|
|
376
|
+
turn_duration_minutes: float | None = duration_minutes
|
|
373
377
|
else:
|
|
374
378
|
duration_minutes = (session_end - session_start).total_seconds() / 60
|
|
379
|
+
turn_duration_minutes = None
|
|
375
380
|
|
|
376
381
|
# -- Extract unique file paths (ordered) --
|
|
377
382
|
files_accessed: list[str] = []
|
|
@@ -437,6 +442,7 @@ def parse_transcript(transcript_path: str | Path) -> SessionTelemetry:
|
|
|
437
442
|
runtime=_REGISTRY.runtime,
|
|
438
443
|
timestamp=session_start,
|
|
439
444
|
duration_minutes=round(duration_minutes, 2),
|
|
445
|
+
turn_duration_minutes=(round(turn_duration_minutes, 2) if turn_duration_minutes is not None else None),
|
|
440
446
|
tool_calls=all_tool_calls,
|
|
441
447
|
stages=[],
|
|
442
448
|
agents=agents,
|
|
@@ -45,14 +45,37 @@ def _write_codex_hook(
|
|
|
45
45
|
*,
|
|
46
46
|
with_feature_flag: bool = True,
|
|
47
47
|
) -> None:
|
|
48
|
+
"""Write a Codex hook in the GA wrapped+matcher format (Codex v0.129+)."""
|
|
48
49
|
codex_dir = home / ".codex"
|
|
49
50
|
codex_dir.mkdir(parents=True, exist_ok=True)
|
|
50
|
-
hooks = {
|
|
51
|
+
hooks = {
|
|
52
|
+
"hooks": {
|
|
53
|
+
"Stop": [
|
|
54
|
+
{
|
|
55
|
+
"matcher": "*",
|
|
56
|
+
"hooks": [{"type": "command", "command": command, "timeout": 10}],
|
|
57
|
+
}
|
|
58
|
+
]
|
|
59
|
+
}
|
|
60
|
+
}
|
|
51
61
|
(codex_dir / "hooks.json").write_text(json.dumps(hooks, indent=2))
|
|
52
|
-
config_lines = ["[features]", "
|
|
62
|
+
config_lines = ["[features]", "hooks = true"] if with_feature_flag else ["[other]", "key = 1"]
|
|
53
63
|
(codex_dir / "config.toml").write_text("\n".join(config_lines) + "\n")
|
|
54
64
|
|
|
55
65
|
|
|
66
|
+
def _write_codex_hook_legacy_flat(home: Path, command: str) -> None:
|
|
67
|
+
"""Write a Codex hook in the pre-GA flat shape (no wrapper, bare entries).
|
|
68
|
+
|
|
69
|
+
Used to drive the doctor's legacy-detection path so a stale install
|
|
70
|
+
surfaces as ⚠ with an upgrade pointer.
|
|
71
|
+
"""
|
|
72
|
+
codex_dir = home / ".codex"
|
|
73
|
+
codex_dir.mkdir(parents=True, exist_ok=True)
|
|
74
|
+
hooks = {"Stop": [{"type": "command", "command": command, "timeout": 10}]}
|
|
75
|
+
(codex_dir / "hooks.json").write_text(json.dumps(hooks, indent=2))
|
|
76
|
+
(codex_dir / "config.toml").write_text("[features]\nhooks = true\n")
|
|
77
|
+
|
|
78
|
+
|
|
56
79
|
# ─────────────────────────────────────────────────────────────────────────────
|
|
57
80
|
# Doctor — runtime detection states
|
|
58
81
|
# ─────────────────────────────────────────────────────────────────────────────
|
|
@@ -98,8 +121,34 @@ def test_doctor_codex_initialized_but_no_hook(monkeypatch: MonkeyPatch, tmp_path
|
|
|
98
121
|
assert "hyperweave install-hook --runtime codex" in result.stdout
|
|
99
122
|
|
|
100
123
|
|
|
124
|
+
def test_doctor_codex_hook_in_legacy_flat_format_surfaces_warning(monkeypatch: MonkeyPatch, tmp_path: Path) -> None:
|
|
125
|
+
"""Pre-GA flat hooks.json surfaces as ⚠ with an upgrade pointer, not ✗.
|
|
126
|
+
|
|
127
|
+
Codex v0.129.0 took hooks GA and changed hooks.json from flat
|
|
128
|
+
``{Stop: [{type, command, timeout}]}`` to wrapped
|
|
129
|
+
``{hooks: {Stop: [{matcher, hooks: [{type, command, timeout}]}]}}``.
|
|
130
|
+
A user with a stale install — hyperweave entry written in the flat
|
|
131
|
+
shape — would otherwise look like "no hyperweave hook" to doctor and
|
|
132
|
+
leave the silent symptom (Codex ignores the file, no receipt fires)
|
|
133
|
+
undiagnosed. The legacy-detection branch flips that to ⚠ with a
|
|
134
|
+
re-install pointer.
|
|
135
|
+
"""
|
|
136
|
+
_patch_home(monkeypatch, tmp_path)
|
|
137
|
+
_patch_which(monkeypatch, {"claude": None, "codex": None})
|
|
138
|
+
_write_codex_hook_legacy_flat(tmp_path, "hyperweave session receipt --genome telemetry-codex")
|
|
139
|
+
monkeypatch.chdir(tmp_path)
|
|
140
|
+
|
|
141
|
+
runner = CliRunner()
|
|
142
|
+
result = runner.invoke(app, ["doctor"])
|
|
143
|
+
assert result.exit_code == 0
|
|
144
|
+
assert "⚠ codex" in result.stdout
|
|
145
|
+
assert "legacy pre-GA flat format" in result.stdout
|
|
146
|
+
assert "hyperweave install-hook --runtime codex" in result.stdout
|
|
147
|
+
|
|
148
|
+
|
|
101
149
|
def test_doctor_codex_hook_registered_but_feature_flag_missing(monkeypatch: MonkeyPatch, tmp_path: Path) -> None:
|
|
102
|
-
"""Codex Stop hook only fires when [features]
|
|
150
|
+
"""Codex Stop hook only fires when [features] hooks = true is set (renamed
|
|
151
|
+
from codex_hooks in Codex v0.130.0)."""
|
|
103
152
|
_patch_home(monkeypatch, tmp_path)
|
|
104
153
|
_patch_which(monkeypatch, {"claude": None, "codex": None})
|
|
105
154
|
_write_codex_hook(tmp_path, "hyperweave session receipt", with_feature_flag=False)
|
|
@@ -109,7 +158,7 @@ def test_doctor_codex_hook_registered_but_feature_flag_missing(monkeypatch: Monk
|
|
|
109
158
|
result = runner.invoke(app, ["doctor"])
|
|
110
159
|
assert result.exit_code == 0
|
|
111
160
|
assert "⚠ codex" in result.stdout
|
|
112
|
-
assert "
|
|
161
|
+
assert "[features] hooks = true" in result.stdout
|
|
113
162
|
|
|
114
163
|
|
|
115
164
|
def test_doctor_codex_binary_only_state(monkeypatch: MonkeyPatch, tmp_path: Path) -> None:
|