hyperweave 0.2.6__tar.gz → 0.2.7__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.2.6 → hyperweave-0.2.7}/CHANGELOG.md +24 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/PKG-INFO +1 -1
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/_version.py +2 -2
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/compose/context.py +2 -2
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/compose/resolver.py +53 -61
- hyperweave-0.2.7/src/hyperweave/compose/rhythm.py +109 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/config/settings.py +5 -1
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/connectors/github.py +19 -8
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/core/state.py +6 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/mcp/server.py +22 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/render/chart_engine.py +60 -96
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/telemetry/contract.py +1 -1
- hyperweave-0.2.7/src/hyperweave/templates/components/chart-markers/circle.svg.j2 +2 -0
- hyperweave-0.2.7/src/hyperweave/templates/components/chart-markers/diamond.svg.j2 +7 -0
- hyperweave-0.2.7/src/hyperweave/templates/components/chart-markers/endpoint-diamond.svg.j2 +14 -0
- hyperweave-0.2.7/src/hyperweave/templates/components/chart-markers/endpoint-rect.svg.j2 +12 -0
- hyperweave-0.2.7/src/hyperweave/templates/components/chart-markers/rect.svg.j2 +12 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/templates/components/treemap.svg.j2 +2 -2
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/templates/frames/chart/brutalist-content.j2 +6 -1
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/templates/frames/chart/chrome-content.j2 +6 -1
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/templates/frames/receipt.svg.j2 +7 -1
- {hyperweave-0.2.6 → hyperweave-0.2.7}/tests/test_chart_engine.py +59 -30
- {hyperweave-0.2.6 → hyperweave-0.2.7}/tests/test_chart_frame.py +5 -5
- {hyperweave-0.2.6 → hyperweave-0.2.7}/tests/test_connectors.py +122 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/tests/test_mcp.py +23 -0
- hyperweave-0.2.7/tests/test_resolver_rhythm.py +158 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/tests/test_state_inference.py +22 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/tests/test_telemetry.py +3 -3
- {hyperweave-0.2.6 → hyperweave-0.2.7}/tests/test_telemetry_integration.py +166 -2
- {hyperweave-0.2.6 → hyperweave-0.2.7}/.dockerignore +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/.github/workflows/ci.yml +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/.github/workflows/deploy.yml +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/.github/workflows/publish.yml +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/.gitignore +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/.pre-commit-config.yaml +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/Dockerfile +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/LICENSE +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/README.md +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/assets/buttons/button-liquid.svg +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/assets/cards/card-butterfly.svg +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/assets/cards/card-python.svg +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/assets/cards/card-sunflower.svg +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/assets/cards/card-waves.svg +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/assets/examples/brutalist-emerald/badge_critical.svg +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/assets/examples/brutalist-emerald/badge_passing.svg +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/assets/examples/brutalist-emerald/badge_warning.svg +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/assets/examples/brutalist-emerald/banner.svg +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/assets/examples/brutalist-emerald/icons/discord.svg +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/assets/examples/brutalist-emerald/icons/notion.svg +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/assets/examples/brutalist-emerald/icons/reddit.svg +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/assets/examples/brutalist-emerald/icons/spotify.svg +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/assets/examples/brutalist-emerald/icons/youtube.svg +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/assets/examples/brutalist-emerald/marquee_counter.svg +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/assets/examples/brutalist-emerald/marquee_horizontal.svg +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/assets/examples/brutalist-emerald/marquee_vertical.svg +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/assets/examples/brutalist-emerald/profile-cards/chart_stars_full.svg +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/assets/examples/brutalist-emerald/profile-cards/stats.svg +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/assets/examples/brutalist-emerald/strip.svg +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/assets/examples/chrome-horizon/badge_critical.svg +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/assets/examples/chrome-horizon/badge_passing.svg +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/assets/examples/chrome-horizon/badge_warning.svg +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/assets/examples/chrome-horizon/banner.svg +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/assets/examples/chrome-horizon/icons/bluesky.svg +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/assets/examples/chrome-horizon/icons/github.svg +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/assets/examples/chrome-horizon/icons/instagram.svg +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/assets/examples/chrome-horizon/icons/mastodon.svg +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/assets/examples/chrome-horizon/icons/x.svg +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/assets/examples/chrome-horizon/marquee_counter.svg +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/assets/examples/chrome-horizon/marquee_horizontal.svg +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/assets/examples/chrome-horizon/marquee_vertical.svg +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/assets/examples/chrome-horizon/profile-cards/chart_stars_full.svg +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/assets/examples/chrome-horizon/profile-cards/stats.svg +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/assets/examples/chrome-horizon/strip.svg +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/assets/examples/telemetry/master_card.svg +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/assets/examples/telemetry/receipt.svg +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/assets/examples/telemetry/rhythm_strip.svg +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/assets/footers/inneraura-footer-liquid.svg +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/assets/footers/inneraura-footer-purple.svg +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/assets/footers/inneraura-footer.svg +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/assets/hyperweave-banner.svg +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/assets/icons/cobalt-sapphire-discord.svg +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/assets/icons/cobalt-sapphire-docs.svg +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/assets/icons/cobalt-sapphire-github.svg +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/assets/icons/cobalt-sapphire-instagram.svg +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/assets/icons/cobalt-sapphire-linkedin.svg +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/assets/icons/cobalt-sapphire-tiktok.svg +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/assets/icons/cobalt-sapphire-x.svg +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/assets/icons/cobalt-sapphire-youtube.svg +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/assets/marquees/badge-showcase-triple.svg +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/assets/marquees/genome-marquee-triple.svg +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/assets/marquees/sample-badges.svg +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/assets/mintlify-assets/404.svg +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/assets/mintlify-assets/callout-icons.svg +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/assets/mintlify-assets/divider.svg +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/assets/mintlify-assets/favicon.svg +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/assets/mintlify-assets/hero-banner.svg +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/assets/mintlify-assets/hyperweave-banner-v2.svg +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/assets/mintlify-assets/hyperweave-navbar-logo.svg +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/assets/mintlify-assets/loader.svg +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/assets/mintlify-assets/og-image.svg +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/assets/timelines/hyperweave-roadmap.svg +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/docs/assets/favicon.svg +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/docs/assets/hyperweave-banner.svg +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/docs/assets/hyperweave-navbar-logo.svg +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/docs/assets/og-image.svg +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/docs/explanation/architecture.mdx +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/docs/explanation/camo-compatibility.mdx +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/docs/explanation/cim-compliance.mdx +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/docs/explanation/genome-profile-system.mdx +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/docs/explanation/metadata-tiers.mdx +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/docs/how-to/add-motion-to-badges.mdx +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/docs/how-to/create-session-receipts.mdx +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/docs/how-to/set-up-claude-code-hooks.mdx +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/docs/how-to/use-live-data-badges.mdx +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/docs/introduction.mdx +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/docs/mint.json +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/docs/quickstart.mdx +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/docs/reference/cli.mdx +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/docs/reference/compose-spec.mdx +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/docs/reference/genomes.mdx +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/docs/reference/http-api.mdx +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/docs/reference/mcp-tools.mdx +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/docs/reference/motions.mdx +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/docs/reference/telemetry-contract.mdx +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/docs/spec/hyperweave-protocol.mdx +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/docs/tutorials/build-an-artifact-kit.mdx +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/docs/tutorials/compose-your-first-badge.mdx +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/docs/tutorials/install-session-telemetry.mdx +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/fly.toml +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/hooks/install.py +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/hooks/session_end.sh +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/justfile +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/pyproject.toml +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/scripts/extract_font_metrics.py +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/scripts/extract_glyphs.py +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/scripts/generate_hw_compliant.py +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/scripts/generate_proofset.py +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/scripts/stress_test.py +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/__init__.py +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/__main__.py +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/cli.py +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/compose/__init__.py +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/compose/assembler.py +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/compose/engine.py +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/compose/lanes.py +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/compose/resolvers/__init__.py +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/compose/resolvers/chart.py +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/compose/resolvers/stats.py +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/compose/resolvers/timeline.py +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/compose/validate_paradigms.py +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/config/__init__.py +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/config/genome_validator.py +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/config/loader.py +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/config/registry.py +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/connectors/__init__.py +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/connectors/arxiv.py +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/connectors/base.py +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/connectors/cache.py +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/connectors/rest.py +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/core/__init__.py +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/core/color.py +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/core/contracts.py +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/core/enums.py +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/core/font_metrics.py +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/core/models.py +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/core/paradigm.py +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/core/schema.py +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/core/text.py +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/core/thresholds.py +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/data/css/accessibility.css +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/data/css/bridge.css +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/data/css/expression.css +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/data/css/phase-colors.css +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/data/css/status.css +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/data/css/telemetry.css +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/data/font-metrics/inter.json +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/data/font-metrics/jetbrains-mono.json +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/data/font-metrics/orbitron.json +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/data/fonts/jetbrains-mono.b64 +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/data/fonts/jetbrains-mono.meta.json +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/data/fonts/orbitron.b64 +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/data/fonts/orbitron.meta.json +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/data/genomes/brutalist-emerald.json +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/data/genomes/chrome-horizon.json +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/data/genomes/telemetry-void.json +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/data/glyphs.json +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/data/motions/border/chromatic-pulse.yaml +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/data/motions/border/corner-trace.yaml +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/data/motions/border/dual-orbit.yaml +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/data/motions/border/entanglement.yaml +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/data/motions/border/rimrun.yaml +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/data/motions/kinetic/bars.yaml +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/data/motions/kinetic/breach.yaml +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/data/motions/kinetic/broadcast.yaml +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/data/motions/kinetic/cascade.yaml +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/data/motions/kinetic/collapse.yaml +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/data/motions/kinetic/converge.yaml +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/data/motions/kinetic/crash.yaml +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/data/motions/kinetic/drop.yaml +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/data/motions/kinetic/pulse.yaml +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/data/motions/static.yaml +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/data/paradigms/brutalist.yaml +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/data/paradigms/chrome.yaml +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/data/paradigms/default.yaml +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/data/policies/normal.json +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/data/policies/permissive.json +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/data/policies/ungoverned.json +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/data/profiles/brutalist.contract.json +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/data/profiles/brutalist.yaml +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/data/profiles/chrome.contract.json +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/data/profiles/chrome.yaml +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/data/specimens.yaml +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/data/telemetry/model-pricing.yaml +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/data/telemetry/stage-config.yaml +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/data/telemetry/stage-labels.yaml +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/data/telemetry/tool-classes.yaml +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/data/telemetry/tool-colors.yaml +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/data/terminals/arrow.json +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/data/terminals/aurora.json +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/data/terminals/beacon.json +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/data/terminals/bracket3d.json +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/data/terminals/chevron.json +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/data/terminals/crosshair.json +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/data/terminals/diamond.json +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/data/terminals/rocket.json +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/data/terminals/rules/bar.json +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/data/terminals/rules/dashed.json +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/data/terminals/rules/five-wave.json +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/data/terminals/rules/spectral.json +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/data/terminals/rules/straight.json +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/data/terminals/rules/wave.json +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/data/terminals/stijl.json +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/kit.py +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/mcp/__init__.py +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/mcp/__main__.py +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/py.typed +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/render/__init__.py +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/render/fonts.py +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/render/glyphs.py +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/render/motion.py +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/render/templates.py +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/serve/__init__.py +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/serve/app.py +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/telemetry/__init__.py +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/telemetry/capture.py +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/telemetry/corrections.py +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/telemetry/cost.py +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/telemetry/models.py +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/telemetry/parser.py +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/telemetry/stages.py +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/templates/components/filter.svg.j2 +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/templates/components/glyph-inline.svg.j2 +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/templates/components/glyph.svg.j2 +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/templates/components/gradient.svg.j2 +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/templates/components/metadata.svg.j2 +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/templates/components/metric.svg.j2 +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/templates/components/phase-segment.svg.j2 +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/templates/components/rule.svg.j2 +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/templates/components/status.svg.j2 +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/templates/components/terminal.svg.j2 +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/templates/document.svg.j2 +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/templates/error-badge.svg.j2 +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/templates/frames/badge/brutalist-content.j2 +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/templates/frames/badge/brutalist-defs.j2 +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/templates/frames/badge/chrome-content.j2 +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/templates/frames/badge/chrome-defs.j2 +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/templates/frames/badge/default-content.j2 +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/templates/frames/badge/default-defs.j2 +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/templates/frames/badge.svg.j2 +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/templates/frames/banner/brutalist-defs.j2 +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/templates/frames/banner/chrome-content.j2 +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/templates/frames/banner/chrome-defs.j2 +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/templates/frames/banner/default-content.j2 +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/templates/frames/banner/default-defs.j2 +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/templates/frames/banner.svg.j2 +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/templates/frames/catalog.svg.j2 +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/templates/frames/chart/brutalist-defs.j2 +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/templates/frames/chart/chrome-defs.j2 +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/templates/frames/chart.svg.j2 +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/templates/frames/divider.svg.j2 +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/templates/frames/icon/brutalist-content.j2 +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/templates/frames/icon/brutalist-defs.j2 +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/templates/frames/icon/chrome-content.j2 +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/templates/frames/icon/chrome-defs.j2 +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/templates/frames/icon/default-content.j2 +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/templates/frames/icon/default-defs.j2 +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/templates/frames/icon.svg.j2 +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/templates/frames/marquee-counter/brutalist-content.j2 +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/templates/frames/marquee-counter/brutalist-defs.j2 +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/templates/frames/marquee-counter/chrome-content.j2 +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/templates/frames/marquee-counter/chrome-defs.j2 +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/templates/frames/marquee-counter.svg.j2 +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/templates/frames/marquee-horizontal/brutalist-content.j2 +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/templates/frames/marquee-horizontal/brutalist-defs.j2 +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/templates/frames/marquee-horizontal/chrome-content.j2 +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/templates/frames/marquee-horizontal/chrome-defs.j2 +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/templates/frames/marquee-horizontal.svg.j2 +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/templates/frames/marquee-vertical/brutalist-content.j2 +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/templates/frames/marquee-vertical/brutalist-defs.j2 +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/templates/frames/marquee-vertical/chrome-content.j2 +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/templates/frames/marquee-vertical/chrome-defs.j2 +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/templates/frames/marquee-vertical.svg.j2 +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/templates/frames/master-card.svg.j2 +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/templates/frames/rhythm-strip.svg.j2 +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/templates/frames/stats/brutalist-content.j2 +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/templates/frames/stats/brutalist-defs.j2 +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/templates/frames/stats/chrome-content.j2 +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/templates/frames/stats/chrome-defs.j2 +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/templates/frames/stats.svg.j2 +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/templates/frames/strip/brutalist-content.j2 +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/templates/frames/strip/brutalist-defs.j2 +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/templates/frames/strip/brutalist-status.j2 +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/templates/frames/strip/chrome-content.j2 +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/templates/frames/strip/chrome-defs.j2 +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/templates/frames/strip/chrome-status.j2 +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/templates/frames/strip/default-content.j2 +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/templates/frames/strip/default-defs.j2 +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/templates/frames/strip/default-status.j2 +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/templates/frames/strip.svg.j2 +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/templates/frames/timeline/default-content.j2 +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/templates/frames/timeline.svg.j2 +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/templates/motions/border/chromatic-pulse.svg.j2 +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/templates/motions/border/corner-trace.svg.j2 +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/templates/motions/border/dual-orbit.svg.j2 +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/templates/motions/border/entanglement.svg.j2 +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/templates/motions/border/rimrun.svg.j2 +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/templates/motions/kinetic/bars.svg.j2 +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/templates/motions/kinetic/breach.svg.j2 +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/templates/motions/kinetic/broadcast.svg.j2 +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/templates/motions/kinetic/cascade.svg.j2 +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/templates/motions/kinetic/collapse.svg.j2 +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/templates/motions/kinetic/converge.svg.j2 +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/templates/motions/kinetic/crash.svg.j2 +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/templates/motions/kinetic/drop.svg.j2 +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/src/hyperweave/templates/motions/kinetic/pulse.svg.j2 +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/tests/__init__.py +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/tests/conftest.py +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/tests/fixtures/github_contributions/synthetic.html +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/tests/fixtures/session.json +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/tests/fixtures/session.jsonl +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/tests/helpers.py +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/tests/test_assembler.py +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/tests/test_core.py +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/tests/test_github_scrape.py +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/tests/test_kit.py +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/tests/test_paradigm_dispatch.py +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/tests/test_paradigm_extensibility.py +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/tests/test_proofset.py +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/tests/test_render.py +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/tests/test_serve.py +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/tests/test_serve_live_state.py +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/tests/test_stats_card.py +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/tests/test_timeline.py +0 -0
- {hyperweave-0.2.6 → hyperweave-0.2.7}/uv.lock +0 -0
|
@@ -5,6 +5,30 @@ 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.2.7] - 2026-04-20
|
|
9
|
+
|
|
10
|
+
### Fixed
|
|
11
|
+
|
|
12
|
+
- **Star history charts now end at the current date.** On very large repositories (40k+ stars), GitHub caps stargazer pagination at roughly the first 40,000 stars, so the latest sample pulled from the API was often years old and the polyline terminated in the past. The chart now always appends a final data point stamped at the current time, while still reporting the real total star count. Small repos are unaffected.
|
|
13
|
+
- **Session receipt labels are honest again.** The "N corrections" line previously counted every user turn — any pushback, redirect, or elaboration — and rendered them next to tool-failure marks (`✗N`) on the token treemap, which created the impression that the two numbers should reconcile. They're now split into "N user turns" and "N tool errors", with tool-error counts tinted red to match the cell marks. A new legend — `✗N = failed tool calls` — appears above the token map so the red marks are self-explanatory.
|
|
14
|
+
- **Session receipt hero no longer misreports the dominant work phase.** The hero badge previously showed the label of the first detected stage, even when that stage lasted two minutes and a later stage dominated the session. It now uses the stage with the largest share of tool calls, and falls back to `MIXED` when no single stage exceeds 20%.
|
|
15
|
+
- **Rhythm bars stop overflowing their track on long sessions.** On sessions with many stages (~30+), the rightmost rhythm bar on the receipt could render hundreds of pixels past the track's right edge. Receipt and rhythm-strip now share a single layout routine with a proper gap budget and a post-hoc rescale, so bars always fit the track regardless of stage count.
|
|
16
|
+
- **Rhythm bars encode time, not tool-call share.** When the telemetry contract carries start and end timestamps per stage, bar widths are now proportional to stage duration, so the time-axis labels (`0m · 104m · 209m`) actually correspond to bar positions. Bar heights are now uniform; the previous height scaling was effectively noise because most bars hit the minimum-height floor.
|
|
17
|
+
- **Live badges now recognize `building` as a state.** A badge with `value="building"` (or `"rebuilding"` / `"build"`) now renders as the building state instead of falling through to the default `active`. Longer phrases containing the word "build" are not affected — only those three exact tokens.
|
|
18
|
+
- **Deploying `HW_GITHUB_TOKENS` no longer crashes the app.** A vestigial `github_tokens` field on the settings schema was trying to parse the environment variable as JSON on startup, so setting the plain comma-separated secret that the token-rotation code actually expects caused a 500. The field has been removed; the connector reads the secret directly, and plain comma-separated deployments now work as documented.
|
|
19
|
+
|
|
20
|
+
### Added
|
|
21
|
+
|
|
22
|
+
- **`hw_discover` advertises the `stats`, `chart`, and `timeline` routes.** MCP clients calling `hw_discover(what="url_grammar")` now receive URL patterns, example URLs, and method hints for the three routes that shipped in v0.2.0 but were missing from discovery.
|
|
23
|
+
|
|
24
|
+
### Changed
|
|
25
|
+
|
|
26
|
+
- **Chart markers render through template partials.** Marker shapes (rect, circle, diamond, and their endpoint variants) now live as Jinja partials under `templates/components/chart-markers/`, matching how the rest of the rendering pipeline handles SVG. No visual change; output is byte-identical for the shipped genomes.
|
|
27
|
+
|
|
28
|
+
### Dev
|
|
29
|
+
|
|
30
|
+
- GitHub token pool rotation is now covered by unit tests (`HW_GITHUB_TOKENS` comma-separated list, `GITHUB_TOKEN` single-token fallback, whitespace tolerance, empty-env behavior).
|
|
31
|
+
|
|
8
32
|
## [0.2.6] - 2026-04-19
|
|
9
33
|
|
|
10
34
|
### Added
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: hyperweave
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.7
|
|
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
|
|
@@ -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.2.
|
|
22
|
-
__version_tuple__ = version_tuple = (0, 2,
|
|
21
|
+
__version__ = version = '0.2.7'
|
|
22
|
+
__version_tuple__ = version_tuple = (0, 2, 7)
|
|
23
23
|
|
|
24
24
|
__commit_id__ = commit_id = None
|
|
@@ -341,7 +341,7 @@ def _ctx_chart(spec: ComposeSpec, resolved: ResolvedArtifact, css: dict[str, str
|
|
|
341
341
|
ctx["chart_gridlines"] = ""
|
|
342
342
|
ctx["chart_area"] = ""
|
|
343
343
|
ctx["chart_polyline"] = ""
|
|
344
|
-
ctx["chart_markers"] =
|
|
344
|
+
ctx["chart_markers"] = []
|
|
345
345
|
ctx["chart_milestones"] = ""
|
|
346
346
|
ctx["data_hw_status"] = "fresh"
|
|
347
347
|
ctx.update(resolved.frame_context)
|
|
@@ -371,7 +371,7 @@ def _ctx_stats(spec: ComposeSpec, resolved: ResolvedArtifact, css: dict[str, str
|
|
|
371
371
|
ctx["embedded_chart_defs"] = ""
|
|
372
372
|
ctx["embedded_chart_area"] = ""
|
|
373
373
|
ctx["embedded_chart_polyline"] = ""
|
|
374
|
-
ctx["embedded_chart_markers"] =
|
|
374
|
+
ctx["embedded_chart_markers"] = []
|
|
375
375
|
ctx.update(resolved.frame_context)
|
|
376
376
|
return ctx
|
|
377
377
|
|
|
@@ -6,6 +6,7 @@ from datetime import datetime
|
|
|
6
6
|
from pathlib import Path
|
|
7
7
|
from typing import TYPE_CHECKING, Any
|
|
8
8
|
|
|
9
|
+
from hyperweave.compose.rhythm import layout_rhythm_bars
|
|
9
10
|
from hyperweave.core.enums import (
|
|
10
11
|
FrameType,
|
|
11
12
|
GlyphMode,
|
|
@@ -1112,7 +1113,7 @@ def resolve_receipt(
|
|
|
1112
1113
|
profile_data: dict[str, Any] = tel.get("profile", {})
|
|
1113
1114
|
tools_raw = tel.get("tools", {})
|
|
1114
1115
|
stages_raw: list[dict[str, Any]] = tel.get("stages", [])
|
|
1115
|
-
|
|
1116
|
+
user_events: list[dict[str, Any]] = tel.get("user_events", [])
|
|
1116
1117
|
agents: list[dict[str, Any]] = tel.get("agents", [])
|
|
1117
1118
|
|
|
1118
1119
|
# ── Normalize tools: contract produces dict keyed by name, templates need list ──
|
|
@@ -1122,7 +1123,8 @@ def resolve_receipt(
|
|
|
1122
1123
|
tools = list(tools_raw)
|
|
1123
1124
|
|
|
1124
1125
|
# ── Normalize stages: contract produces {label, dominant_class, start, end, tools} ──
|
|
1125
|
-
# Templates need {name, pct, tool_class}
|
|
1126
|
+
# Templates need {name, pct, tool_class}; start/end are preserved so
|
|
1127
|
+
# :func:`layout_rhythm_bars` can lay out time-proportional x/w.
|
|
1126
1128
|
total_stage_tools = sum(s.get("tools", 1) for s in stages_raw) or 1
|
|
1127
1129
|
stages: list[dict[str, Any]] = [
|
|
1128
1130
|
{
|
|
@@ -1130,6 +1132,8 @@ def resolve_receipt(
|
|
|
1130
1132
|
"pct": round(s.get("tools", 1) / total_stage_tools * 100),
|
|
1131
1133
|
"label": s.get("label", ""),
|
|
1132
1134
|
"tool_class": s.get("dominant_class", "explore"),
|
|
1135
|
+
"start": s.get("start"),
|
|
1136
|
+
"end": s.get("end"),
|
|
1133
1137
|
}
|
|
1134
1138
|
for s in stages_raw
|
|
1135
1139
|
]
|
|
@@ -1161,21 +1165,43 @@ def resolve_receipt(
|
|
|
1161
1165
|
"Send": "coordinate",
|
|
1162
1166
|
}
|
|
1163
1167
|
|
|
1168
|
+
# ── Dominant phase (drives hero badge + bottom-right phase label) ──
|
|
1169
|
+
# Using stages[0] was the old bug — for a session where the first 2-minute
|
|
1170
|
+
# stage classified as "validation" but the dominant (45% of tool calls)
|
|
1171
|
+
# was "implementation", the hero badge lied. When no single stage owns
|
|
1172
|
+
# at least 20% of the tool calls, fall back to "MIXED" to avoid
|
|
1173
|
+
# overclaiming a dominant phase that doesn't exist.
|
|
1174
|
+
dominant = max(stages, key=lambda s: s.get("pct", 0)) if stages else None
|
|
1175
|
+
dominant_label = (dominant.get("label") or dominant.get("name") or "") if dominant else ""
|
|
1176
|
+
dominant_pct = dominant.get("pct", 0) if dominant else 0
|
|
1177
|
+
|
|
1164
1178
|
# ── Hero row ──
|
|
1165
1179
|
hero_headline = f"{_fmt_tok(total_tok)} tokens billed · ${total_cost:.2f}"
|
|
1166
1180
|
dur_label = f"{int(duration_m)}m" if duration_m else "—"
|
|
1167
1181
|
hero_subline = f"{model} · {dur_label} · {calls} calls"
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1182
|
+
if not dominant:
|
|
1183
|
+
hero_profile = "SESSION"
|
|
1184
|
+
elif dominant_pct < 20:
|
|
1185
|
+
hero_profile = "MIXED"
|
|
1186
|
+
else:
|
|
1187
|
+
hero_profile = dominant_label.upper()
|
|
1188
|
+
hero_tool_class = dominant["tool_class"] if dominant else "explore"
|
|
1189
|
+
# Split "pushbacks" into distinct signals so the card stops labeling them
|
|
1190
|
+
# as one opaque "N corrections" lie. user_events counts every non-continuation
|
|
1191
|
+
# user turn (corrections + redirects + elaborations); tool errors count
|
|
1192
|
+
# failing/blocked tool calls (the red ✗N cell marks reconcile to this).
|
|
1193
|
+
n_user_turns = len(user_events)
|
|
1194
|
+
n_tool_errors = sum(t.get("errors", 0) + t.get("blocked", 0) for t in tools)
|
|
1171
1195
|
n_agents = len(agents)
|
|
1172
|
-
hero_right = [
|
|
1173
|
-
f"{_fmt_tok(total_input)} in / {_fmt_tok(total_output)} out",
|
|
1196
|
+
hero_right: list[dict[str, str]] = [
|
|
1197
|
+
{"text": f"{_fmt_tok(total_input)} in / {_fmt_tok(total_output)} out"},
|
|
1174
1198
|
]
|
|
1175
1199
|
if total_cache_read or total_cache_create:
|
|
1176
|
-
hero_right.append(f"{_fmt_tok(total_cache_read)} cached / {_fmt_tok(total_cache_create)} written")
|
|
1177
|
-
if
|
|
1178
|
-
hero_right.append(f"{
|
|
1200
|
+
hero_right.append({"text": f"{_fmt_tok(total_cache_read)} cached / {_fmt_tok(total_cache_create)} written"})
|
|
1201
|
+
if n_user_turns:
|
|
1202
|
+
hero_right.append({"text": f"{n_user_turns} user turn{'s' if n_user_turns != 1 else ''}"})
|
|
1203
|
+
if n_tool_errors:
|
|
1204
|
+
hero_right.append({"text": f"{n_tool_errors} tool errors", "accent": "failing"})
|
|
1179
1205
|
|
|
1180
1206
|
# ── Treemap layout (3-tier, 752px wide, token-proportional) ──
|
|
1181
1207
|
content_w = 752
|
|
@@ -1253,29 +1279,16 @@ def resolve_receipt(
|
|
|
1253
1279
|
)
|
|
1254
1280
|
x += min(w, 180) + 4
|
|
1255
1281
|
|
|
1256
|
-
# ── Rhythm bars
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
rx = 0
|
|
1282
|
+
# ── Rhythm bars ──
|
|
1283
|
+
# Delegated to the shared helper so receipt + rhythm-strip can't drift.
|
|
1284
|
+
# See src/hyperweave/compose/rhythm.py for the two-pass algorithm.
|
|
1260
1285
|
bar_area_h = 92
|
|
1261
|
-
|
|
1262
|
-
pct = s.get("pct", 0)
|
|
1263
|
-
w = max(int(content_w * pct / total_stage_pct), 6)
|
|
1264
|
-
h = max(int(bar_area_h * (pct / 50)), 8)
|
|
1265
|
-
y = bar_area_h - h
|
|
1266
|
-
tc = s.get("tool_class", s.get("name", "explore"))
|
|
1267
|
-
rhythm_bars.append({"x": rx, "y": y, "w": w, "h": h, "tool_class": tc})
|
|
1268
|
-
rx += w + 2
|
|
1286
|
+
rhythm_bars = layout_rhythm_bars(stages, area_w=content_w, area_h=bar_area_h)
|
|
1269
1287
|
|
|
1270
1288
|
# ── Legend entries ──
|
|
1271
1289
|
used_classes = sorted({c["tool_class"] for c in treemap_cells}) if treemap_cells else ["explore"]
|
|
1272
1290
|
treemap_legend = [{"tool_class": tc, "label": tc} for tc in used_classes]
|
|
1273
1291
|
|
|
1274
|
-
# ── Dominant phase ──
|
|
1275
|
-
dominant = max(stages, key=lambda s: s.get("pct", 0)) if stages else {"name": "", "pct": 0}
|
|
1276
|
-
dominant_label = dominant.get("label", dominant.get("name", ""))
|
|
1277
|
-
dominant_pct = dominant.get("pct", 0)
|
|
1278
|
-
|
|
1279
1292
|
# ── Metadata band (v0.4): provenance row between rhythm legend and footer ──
|
|
1280
1293
|
session_id = session.get("id", "")
|
|
1281
1294
|
session_id_short = session_id[:8].rstrip("-") if session_id else ""
|
|
@@ -1307,11 +1320,14 @@ def resolve_receipt(
|
|
|
1307
1320
|
metadata_right = " · ".join(metadata_right_parts)
|
|
1308
1321
|
|
|
1309
1322
|
# ── Footer: session stats (left) + brand anchor (right) ──
|
|
1323
|
+
# Same split as hero so nothing in the card claims "corrections" anymore.
|
|
1310
1324
|
footer_parts = []
|
|
1311
|
-
if
|
|
1312
|
-
footer_parts.append(f"{
|
|
1325
|
+
if n_user_turns:
|
|
1326
|
+
footer_parts.append(f"{n_user_turns} user turn{'s' if n_user_turns != 1 else ''}")
|
|
1327
|
+
if n_tool_errors:
|
|
1328
|
+
footer_parts.append(f"{n_tool_errors} tool error{'s' if n_tool_errors != 1 else ''}")
|
|
1313
1329
|
if n_agents:
|
|
1314
|
-
footer_parts.append(f"{n_agents}
|
|
1330
|
+
footer_parts.append(f"{n_agents} agent{'s' if n_agents != 1 else ''}")
|
|
1315
1331
|
footer_left = " · ".join(footer_parts) if footer_parts else ""
|
|
1316
1332
|
footer_right = "hyperweave.app"
|
|
1317
1333
|
|
|
@@ -1325,7 +1341,7 @@ def resolve_receipt(
|
|
|
1325
1341
|
"hero_tool_class": hero_tool_class,
|
|
1326
1342
|
"hero_headline": hero_headline,
|
|
1327
1343
|
"hero_subline": hero_subline,
|
|
1328
|
-
"hero_right_stats":
|
|
1344
|
+
"hero_right_stats": hero_right,
|
|
1329
1345
|
"treemap_legend": treemap_legend,
|
|
1330
1346
|
"treemap_cells": treemap_cells,
|
|
1331
1347
|
"stage_count": len(stages),
|
|
@@ -1368,12 +1384,16 @@ def resolve_rhythm_strip(
|
|
|
1368
1384
|
tools = list(tools_raw)
|
|
1369
1385
|
|
|
1370
1386
|
# ── Normalize stages ──
|
|
1387
|
+
# start/end preserved so :func:`layout_rhythm_bars` can lay out
|
|
1388
|
+
# time-proportional widths when the contract carries them.
|
|
1371
1389
|
total_stage_tools = sum(s.get("tools", 1) for s in stages_raw) or 1
|
|
1372
1390
|
stages: list[dict[str, Any]] = [
|
|
1373
1391
|
{
|
|
1374
1392
|
"name": s.get("dominant_class", s.get("label", "explore")),
|
|
1375
1393
|
"pct": round(s.get("tools", 1) / total_stage_tools * 100),
|
|
1376
1394
|
"tool_class": s.get("dominant_class", "explore"),
|
|
1395
|
+
"start": s.get("start"),
|
|
1396
|
+
"end": s.get("end"),
|
|
1377
1397
|
}
|
|
1378
1398
|
for s in stages_raw
|
|
1379
1399
|
]
|
|
@@ -1389,38 +1409,10 @@ def resolve_rhythm_strip(
|
|
|
1389
1409
|
|
|
1390
1410
|
# Rhythm bars — must match template bar_w = sw - stats_w - right_w - 16.
|
|
1391
1411
|
# With sw=800, stats_w=180, right_w=120: bar_area_w = 484.
|
|
1392
|
-
#
|
|
1393
|
-
# overflow into the right-side loop-status panel.
|
|
1412
|
+
# Delegated to the shared helper — see src/hyperweave/compose/rhythm.py.
|
|
1394
1413
|
bar_area_w = 484
|
|
1395
1414
|
bar_area_h = 42
|
|
1396
|
-
|
|
1397
|
-
n_stages = len(stages)
|
|
1398
|
-
gap_budget = gap_px * max(n_stages - 1, 0)
|
|
1399
|
-
available_w = max(bar_area_w - gap_budget, bar_area_w // 2)
|
|
1400
|
-
min_bar_w = max(2, available_w // max(n_stages, 1) // 3) if n_stages else 6
|
|
1401
|
-
total_stage_pct = sum(s.get("pct", 0) for s in stages) or 100
|
|
1402
|
-
|
|
1403
|
-
raw_bars: list[dict[str, Any]] = []
|
|
1404
|
-
for s in stages:
|
|
1405
|
-
pct = s.get("pct", 0)
|
|
1406
|
-
w = max(int(available_w * pct / total_stage_pct), min_bar_w)
|
|
1407
|
-
h = max(int(bar_area_h * (pct / 50)), 6)
|
|
1408
|
-
y = bar_area_h - h
|
|
1409
|
-
tc = s.get("tool_class", "explore")
|
|
1410
|
-
raw_bars.append({"w": w, "h": h, "y": y, "tool_class": tc})
|
|
1411
|
-
|
|
1412
|
-
# Post-hoc uniform scale if floor-pressure still exceeds budget
|
|
1413
|
-
raw_total = sum(b["w"] for b in raw_bars)
|
|
1414
|
-
if raw_total > available_w and raw_total > 0:
|
|
1415
|
-
scale = available_w / raw_total
|
|
1416
|
-
for b in raw_bars:
|
|
1417
|
-
b["w"] = max(int(b["w"] * scale), 2)
|
|
1418
|
-
|
|
1419
|
-
rhythm_bars: list[dict[str, Any]] = []
|
|
1420
|
-
rx = 0
|
|
1421
|
-
for b in raw_bars:
|
|
1422
|
-
rhythm_bars.append({"x": rx, "y": b["y"], "w": b["w"], "h": b["h"], "tool_class": b["tool_class"]})
|
|
1423
|
-
rx += b["w"] + gap_px
|
|
1415
|
+
rhythm_bars = layout_rhythm_bars(stages, area_w=bar_area_w, area_h=bar_area_h)
|
|
1424
1416
|
|
|
1425
1417
|
# Velocity estimate
|
|
1426
1418
|
vel = int(total_tok / max(duration_m, 1)) if duration_m else 0
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
"""Rhythm-bar layout: time-proportional x/w with uniform heights.
|
|
2
|
+
|
|
3
|
+
Shared between :func:`resolve_receipt` and :func:`resolve_rhythm_strip`
|
|
4
|
+
so the receipt's "79-stage bars overflow the track" bug can't diverge
|
|
5
|
+
from rhythm-strip's correct two-pass algorithm.
|
|
6
|
+
|
|
7
|
+
Algorithm (two passes):
|
|
8
|
+
1. Reserve a gap budget up front so ``n_bars x gap_px`` never steals
|
|
9
|
+
from the width budget. Compute a minimum bar width floor based on
|
|
10
|
+
``n_bars`` so each bar is still visible under extreme stage counts.
|
|
11
|
+
2. Raw widths: time-proportional when every stage carries ISO
|
|
12
|
+
``start``/``end`` timestamps (preferred — the time-axis labels
|
|
13
|
+
then agree with the bar positions); fall back to tool-call-share
|
|
14
|
+
pct when timestamps are missing (legacy contract).
|
|
15
|
+
3. Post-hoc uniform rescale if the raw sum still exceeds the gap
|
|
16
|
+
budget (happens when the min-bar-width floor dominates).
|
|
17
|
+
4. Emit ``{x, y, w, h, tool_class}`` with ``h = BAR_HEIGHT`` uniform.
|
|
18
|
+
|
|
19
|
+
Uniform height is intentional. The old resolver's
|
|
20
|
+
``h = max(int(bar_area_h * (pct / 50)), 8)`` made ~76 of 79 bars hit
|
|
21
|
+
the 8px floor, implying a signal dimension the data didn't carry.
|
|
22
|
+
Three channels (time x category x color) is already enough; the
|
|
23
|
+
fourth (height) had no clear semantic and was therefore noise.
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
from __future__ import annotations
|
|
27
|
+
|
|
28
|
+
from datetime import datetime
|
|
29
|
+
from typing import Any
|
|
30
|
+
|
|
31
|
+
BAR_HEIGHT = 14
|
|
32
|
+
"""Uniform rhythm-bar height. Shared by receipt (area_h=92) and rhythm-strip
|
|
33
|
+
(area_h=42): both align the bars flush to the bottom of their track."""
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def layout_rhythm_bars(
|
|
37
|
+
stages: list[dict[str, Any]],
|
|
38
|
+
area_w: int,
|
|
39
|
+
area_h: int,
|
|
40
|
+
gap_px: int = 2,
|
|
41
|
+
) -> list[dict[str, Any]]:
|
|
42
|
+
"""Lay out rhythm bars into a bounded track with a gap budget + rescale.
|
|
43
|
+
|
|
44
|
+
Args:
|
|
45
|
+
stages: Normalized stage dicts. Each MAY carry ``start``/``end``
|
|
46
|
+
ISO strings for time-proportional layout; otherwise layout
|
|
47
|
+
falls back to ``pct`` (tool-call share).
|
|
48
|
+
area_w: Track width in pixels.
|
|
49
|
+
area_h: Track height in pixels. Used only for ``y`` (bars align
|
|
50
|
+
bottom-flush); ``h`` is always :data:`BAR_HEIGHT`.
|
|
51
|
+
gap_px: Inter-bar gap in pixels. Reserved upfront in the budget
|
|
52
|
+
so many-stage sessions don't overflow into adjacent panels.
|
|
53
|
+
|
|
54
|
+
Returns:
|
|
55
|
+
One dict per stage: ``{x, y, w, h, tool_class}``. Empty list when
|
|
56
|
+
``stages`` is empty.
|
|
57
|
+
"""
|
|
58
|
+
if not stages:
|
|
59
|
+
return []
|
|
60
|
+
|
|
61
|
+
n = len(stages)
|
|
62
|
+
gap_budget = gap_px * max(n - 1, 0)
|
|
63
|
+
# Cap the worst case: never give up more than half the track to gaps.
|
|
64
|
+
available_w = max(area_w - gap_budget, area_w // 2)
|
|
65
|
+
|
|
66
|
+
has_timestamps = all(s.get("start") and s.get("end") for s in stages)
|
|
67
|
+
if has_timestamps:
|
|
68
|
+
t0 = datetime.fromisoformat(stages[0]["start"])
|
|
69
|
+
t_end = datetime.fromisoformat(stages[-1]["end"])
|
|
70
|
+
total_s = max((t_end - t0).total_seconds(), 1.0)
|
|
71
|
+
raw_w = [
|
|
72
|
+
max(
|
|
73
|
+
int(
|
|
74
|
+
available_w
|
|
75
|
+
* (datetime.fromisoformat(s["end"]) - datetime.fromisoformat(s["start"])).total_seconds()
|
|
76
|
+
/ total_s
|
|
77
|
+
),
|
|
78
|
+
2,
|
|
79
|
+
)
|
|
80
|
+
for s in stages
|
|
81
|
+
]
|
|
82
|
+
else:
|
|
83
|
+
total_pct = sum(s.get("pct", 0) for s in stages) or 100
|
|
84
|
+
# Per-bar minimum: 1/3 of the equal-share width so tiny stages
|
|
85
|
+
# stay visible without swallowing the whole budget.
|
|
86
|
+
min_bar_w = max(2, available_w // max(n, 1) // 3)
|
|
87
|
+
raw_w = [max(int(available_w * s.get("pct", 0) / total_pct), min_bar_w) for s in stages]
|
|
88
|
+
|
|
89
|
+
# Post-hoc uniform rescale: if the floor pressure drove the sum over budget.
|
|
90
|
+
raw_total = sum(raw_w)
|
|
91
|
+
if raw_total > available_w and raw_total > 0:
|
|
92
|
+
scale = available_w / raw_total
|
|
93
|
+
raw_w = [max(int(w * scale), 2) for w in raw_w]
|
|
94
|
+
|
|
95
|
+
y = area_h - BAR_HEIGHT
|
|
96
|
+
bars: list[dict[str, Any]] = []
|
|
97
|
+
rx = 0
|
|
98
|
+
for s, w in zip(stages, raw_w, strict=True):
|
|
99
|
+
bars.append(
|
|
100
|
+
{
|
|
101
|
+
"x": rx,
|
|
102
|
+
"y": y,
|
|
103
|
+
"w": w,
|
|
104
|
+
"h": BAR_HEIGHT,
|
|
105
|
+
"tool_class": s.get("tool_class", s.get("dominant_class", "explore")),
|
|
106
|
+
}
|
|
107
|
+
)
|
|
108
|
+
rx += w + gap_px
|
|
109
|
+
return bars
|
|
@@ -37,7 +37,11 @@ class HyperWeaveSettings(BaseSettings):
|
|
|
37
37
|
data_cache_ttl: int = Field(default=300, description="Data-bound artifact max-age (5 min)")
|
|
38
38
|
|
|
39
39
|
# -- Connectors --
|
|
40
|
-
|
|
40
|
+
# NOTE: GitHub token rotation lives in ``connectors.base._get_github_token``,
|
|
41
|
+
# which reads ``HW_GITHUB_TOKENS`` as a plain comma-separated string directly
|
|
42
|
+
# from ``os.environ``. Do NOT add a ``github_tokens`` Pydantic field here —
|
|
43
|
+
# Pydantic Settings would auto-map the same env var and try to JSON-parse
|
|
44
|
+
# the CSV value, crashing app startup.
|
|
41
45
|
connect_timeout: float = Field(default=10.0, description="HTTP connect timeout in seconds")
|
|
42
46
|
total_timeout: float = Field(default=15.0, description="HTTP total timeout in seconds")
|
|
43
47
|
|
|
@@ -5,6 +5,7 @@ from __future__ import annotations
|
|
|
5
5
|
import asyncio
|
|
6
6
|
import math
|
|
7
7
|
import re
|
|
8
|
+
from datetime import UTC, datetime
|
|
8
9
|
from typing import Any
|
|
9
10
|
|
|
10
11
|
from hyperweave.connectors.base import fetch_json, fetch_text
|
|
@@ -134,7 +135,13 @@ async def fetch_metric(identifier: str, metric: str) -> dict[str, Any]:
|
|
|
134
135
|
# ── Session 2A+2B: star history sampling ───────────────────────────────────
|
|
135
136
|
|
|
136
137
|
|
|
137
|
-
_STARGAZER_PAGE_SIZE =
|
|
138
|
+
_STARGAZER_PAGE_SIZE = 100
|
|
139
|
+
# GitHub hard-caps deep pagination on /stargazers at ~400 pages.
|
|
140
|
+
# With per_page=100 that gives ~40k stargazer visibility per repo; we still
|
|
141
|
+
# report the real total_stars in the "now" point, we just can't sample past
|
|
142
|
+
# this wall. For mega-repos (100k+ stars) we therefore sample within a fixed
|
|
143
|
+
# window and mark the final point with the current timestamp.
|
|
144
|
+
_STARGAZER_PAGE_CAP = 400
|
|
138
145
|
_STARGAZER_ACCEPT_HEADER = "application/vnd.github.v3.star+json"
|
|
139
146
|
|
|
140
147
|
|
|
@@ -183,8 +190,9 @@ async def fetch_stargazer_history(
|
|
|
183
190
|
cache.set(cache_key, empty_result, STARGAZER_HISTORY_TTL)
|
|
184
191
|
return empty_result
|
|
185
192
|
|
|
186
|
-
# Step 2: compute total pages
|
|
193
|
+
# Step 2: compute total pages (clamped at GitHub's deep-pagination cap)
|
|
187
194
|
total_pages = max(1, math.ceil(total_stars / _STARGAZER_PAGE_SIZE))
|
|
195
|
+
effective_pages = min(total_pages, _STARGAZER_PAGE_CAP)
|
|
188
196
|
|
|
189
197
|
# Single-page case: repo has ≤ 30 stars. The "first starred_at of the page"
|
|
190
198
|
# sampling trick would otherwise collapse to a single aggregated point plus
|
|
@@ -211,14 +219,14 @@ async def fetch_stargazer_history(
|
|
|
211
219
|
cache.set(cache_key, single_page_result, STARGAZER_HISTORY_TTL)
|
|
212
220
|
return single_page_result
|
|
213
221
|
|
|
214
|
-
# Cap the sample count at the number of
|
|
215
|
-
sample_count = min(sample_pages,
|
|
222
|
+
# Cap the sample count at the number of reachable pages.
|
|
223
|
+
sample_count = min(sample_pages, effective_pages)
|
|
216
224
|
|
|
217
225
|
# Step 3: pick evenly distributed page numbers (always include first + last).
|
|
218
226
|
if sample_count == 1:
|
|
219
227
|
page_numbers: list[int] = [1]
|
|
220
228
|
else:
|
|
221
|
-
step = (
|
|
229
|
+
step = (effective_pages - 1) / (sample_count - 1)
|
|
222
230
|
page_numbers = sorted({max(1, round(1 + step * i)) for i in range(sample_count)})
|
|
223
231
|
|
|
224
232
|
async def _fetch_page(page: int) -> tuple[int, list[dict[str, Any]]]:
|
|
@@ -247,10 +255,13 @@ async def fetch_stargazer_history(
|
|
|
247
255
|
cumulative = 1 # first starred timestamp → at least 1 star
|
|
248
256
|
points.append({"date": starred_at, "count": cumulative})
|
|
249
257
|
|
|
250
|
-
#
|
|
258
|
+
# Append an honest "now" point: real current star total at real current
|
|
259
|
+
# timestamp. For mega-repos where sampling is capped at page 400, the
|
|
260
|
+
# deepest reachable starred_at may be years old — using that as the
|
|
261
|
+
# terminal timestamp produced polylines that ended in the past. The count
|
|
262
|
+
# is still the real stargazers_count; only the timestamp becomes "now".
|
|
251
263
|
if points:
|
|
252
|
-
|
|
253
|
-
points.append({"date": latest_date, "count": total_stars})
|
|
264
|
+
points.append({"date": datetime.now(UTC).isoformat(), "count": total_stars})
|
|
254
265
|
points.sort(key=lambda p: p["date"])
|
|
255
266
|
|
|
256
267
|
result: dict[str, Any] = {
|
|
@@ -27,6 +27,12 @@ def infer_state(label: str, value: str) -> str:
|
|
|
27
27
|
return "failing"
|
|
28
28
|
if "warn" in value_lower:
|
|
29
29
|
return "warning"
|
|
30
|
+
# Explicit set match (not substring) so values like "rebuild required"
|
|
31
|
+
# or "build failure" don't get miscategorized as "building" state.
|
|
32
|
+
# Only the three literal tokens below qualify; any richer phrase should
|
|
33
|
+
# route via the percent-threshold or default-active fall-throughs.
|
|
34
|
+
if value_lower in {"building", "rebuilding", "build"}:
|
|
35
|
+
return "building"
|
|
30
36
|
if "build" in label_lower and "run" in value_lower:
|
|
31
37
|
return "building"
|
|
32
38
|
|
|
@@ -297,6 +297,28 @@ async def hw_discover(
|
|
|
297
297
|
},
|
|
298
298
|
"example": "/v1/live/github/eli64s/readme-ai/stars/brutalist-emerald.static",
|
|
299
299
|
},
|
|
300
|
+
"stats": {
|
|
301
|
+
"pattern": "/v1/stats/{username}/{genome}.{motion}",
|
|
302
|
+
"query_params": {
|
|
303
|
+
"glyph": "Glyph identifier",
|
|
304
|
+
"state": "Semantic state",
|
|
305
|
+
},
|
|
306
|
+
"example": "/v1/stats/jiahongc/chrome-horizon.static",
|
|
307
|
+
},
|
|
308
|
+
"chart": {
|
|
309
|
+
"pattern": "/v1/chart/stars/{owner}/{repo}/{genome}.{motion}",
|
|
310
|
+
"query_params": {
|
|
311
|
+
"state": "Semantic state",
|
|
312
|
+
},
|
|
313
|
+
"example": "/v1/chart/stars/torvalds/linux/brutalist-emerald.static",
|
|
314
|
+
},
|
|
315
|
+
"timeline": {
|
|
316
|
+
"pattern": "/v1/timeline/{genome}.{motion}",
|
|
317
|
+
"method": "POST",
|
|
318
|
+
"body": 'JSON: {"items": [{"title": str, "subtitle": str, "date": str, "status": str}, ...]}',
|
|
319
|
+
"query_params": {},
|
|
320
|
+
"example": "POST /v1/timeline/chrome-horizon.static with items payload",
|
|
321
|
+
},
|
|
300
322
|
}
|
|
301
323
|
|
|
302
324
|
return result
|