vis-agent 0.3.5.dev8276__tar.gz → 0.4.3.dev8313__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.
Files changed (157) hide show
  1. {vis_agent-0.3.5.dev8276 → vis_agent-0.4.3.dev8313}/PKG-INFO +1 -1
  2. {vis_agent-0.3.5.dev8276 → vis_agent-0.4.3.dev8313}/contracts/schema/agents.json +0 -4
  3. {vis_agent-0.3.5.dev8276 → vis_agent-0.4.3.dev8313}/contracts/schema/view.json +8 -0
  4. {vis_agent-0.3.5.dev8276 → vis_agent-0.4.3.dev8313}/pyproject.toml +1 -1
  5. vis_agent-0.4.3.dev8313/release-tag +1 -0
  6. {vis_agent-0.3.5.dev8276 → vis_agent-0.4.3.dev8313}/src/blockether/vis/_contracts.py +16 -0
  7. {vis_agent-0.3.5.dev8276 → vis_agent-0.4.3.dev8313}/src/blockether/vis/_outside.py +7 -0
  8. {vis_agent-0.3.5.dev8276 → vis_agent-0.4.3.dev8313}/src/blockether/vis/engine/_client.py +1 -1
  9. {vis_agent-0.3.5.dev8276 → vis_agent-0.4.3.dev8313}/src/blockether/vis/extension.py +25 -8
  10. {vis_agent-0.3.5.dev8276 → vis_agent-0.4.3.dev8313}/src/blockether/vis/extension_package.py +8 -2
  11. {vis_agent-0.3.5.dev8276 → vis_agent-0.4.3.dev8313}/tests/test_live_primitives.py +29 -1
  12. vis_agent-0.3.5.dev8276/release-tag +0 -1
  13. {vis_agent-0.3.5.dev8276 → vis_agent-0.4.3.dev8313}/.gitignore +0 -0
  14. {vis_agent-0.3.5.dev8276 → vis_agent-0.4.3.dev8313}/LICENSE +0 -0
  15. {vis_agent-0.3.5.dev8276 → vis_agent-0.4.3.dev8313}/README.md +0 -0
  16. {vis_agent-0.3.5.dev8276 → vis_agent-0.4.3.dev8313}/contracts/fixtures/activity-arguments.json +0 -0
  17. {vis_agent-0.3.5.dev8276 → vis_agent-0.4.3.dev8313}/contracts/fixtures/activity-cases.json +0 -0
  18. {vis_agent-0.3.5.dev8276 → vis_agent-0.4.3.dev8313}/contracts/fixtures/activity-copy.json +0 -0
  19. {vis_agent-0.3.5.dev8276 → vis_agent-0.4.3.dev8313}/contracts/fixtures/activity-execution.json +0 -0
  20. {vis_agent-0.3.5.dev8276 → vis_agent-0.4.3.dev8313}/contracts/fixtures/activity-groups.json +0 -0
  21. {vis_agent-0.3.5.dev8276 → vis_agent-0.4.3.dev8313}/contracts/fixtures/activity-read-session.json +0 -0
  22. {vis_agent-0.3.5.dev8276 → vis_agent-0.4.3.dev8313}/contracts/fixtures/activity-reads.json +0 -0
  23. {vis_agent-0.3.5.dev8276 → vis_agent-0.4.3.dev8313}/contracts/fixtures/activity-results.json +0 -0
  24. {vis_agent-0.3.5.dev8276 → vis_agent-0.4.3.dev8313}/contracts/fixtures/activity-stream.json +0 -0
  25. {vis_agent-0.3.5.dev8276 → vis_agent-0.4.3.dev8313}/contracts/fixtures/activity-tables.json +0 -0
  26. {vis_agent-0.3.5.dev8276 → vis_agent-0.4.3.dev8313}/contracts/fixtures/activity.json +0 -0
  27. {vis_agent-0.3.5.dev8276 → vis_agent-0.4.3.dev8313}/contracts/fixtures/diff.json +0 -0
  28. {vis_agent-0.3.5.dev8276 → vis_agent-0.4.3.dev8313}/contracts/fixtures/live-primitives.json +0 -0
  29. {vis_agent-0.3.5.dev8276 → vis_agent-0.4.3.dev8313}/contracts/fixtures/session-health.json +0 -0
  30. {vis_agent-0.3.5.dev8276 → vis_agent-0.4.3.dev8313}/contracts/fixtures/view.json +0 -0
  31. {vis_agent-0.3.5.dev8276 → vis_agent-0.4.3.dev8313}/contracts/schema/activity.json +0 -0
  32. {vis_agent-0.3.5.dev8276 → vis_agent-0.4.3.dev8313}/contracts/schema/automations.json +0 -0
  33. {vis_agent-0.3.5.dev8276 → vis_agent-0.4.3.dev8313}/contracts/schema/common.json +0 -0
  34. {vis_agent-0.3.5.dev8276 → vis_agent-0.4.3.dev8313}/contracts/schema/config.json +0 -0
  35. {vis_agent-0.3.5.dev8276 → vis_agent-0.4.3.dev8313}/contracts/schema/content.json +0 -0
  36. {vis_agent-0.3.5.dev8276 → vis_agent-0.4.3.dev8313}/contracts/schema/council.json +0 -0
  37. {vis_agent-0.3.5.dev8276 → vis_agent-0.4.3.dev8313}/contracts/schema/diff.json +0 -0
  38. {vis_agent-0.3.5.dev8276 → vis_agent-0.4.3.dev8313}/contracts/schema/evals.json +0 -0
  39. {vis_agent-0.3.5.dev8276 → vis_agent-0.4.3.dev8313}/contracts/schema/gateway.json +0 -0
  40. {vis_agent-0.3.5.dev8276 → vis_agent-0.4.3.dev8313}/contracts/schema/provider.json +0 -0
  41. {vis_agent-0.3.5.dev8276 → vis_agent-0.4.3.dev8313}/contracts/schema/symbol.json +0 -0
  42. {vis_agent-0.3.5.dev8276 → vis_agent-0.4.3.dev8313}/contracts/schema/toggle.json +0 -0
  43. {vis_agent-0.3.5.dev8276 → vis_agent-0.4.3.dev8313}/examples/greeter/tests/test_greeter.py +0 -0
  44. {vis_agent-0.3.5.dev8276 → vis_agent-0.4.3.dev8313}/hatch_build.py +0 -0
  45. {vis_agent-0.3.5.dev8276 → vis_agent-0.4.3.dev8313}/src/blockether/vis/__init__.py +0 -0
  46. {vis_agent-0.3.5.dev8276 → vis_agent-0.4.3.dev8313}/src/blockether/vis/_wire.py +0 -0
  47. {vis_agent-0.3.5.dev8276 → vis_agent-0.4.3.dev8313}/src/blockether/vis/activity.py +0 -0
  48. {vis_agent-0.3.5.dev8276 → vis_agent-0.4.3.dev8313}/src/blockether/vis/engine/__init__.py +0 -0
  49. {vis_agent-0.3.5.dev8276 → vis_agent-0.4.3.dev8313}/src/blockether/vis/engine/_agent.py +0 -0
  50. {vis_agent-0.3.5.dev8276 → vis_agent-0.4.3.dev8313}/src/blockether/vis/engine/_agents.py +0 -0
  51. {vis_agent-0.3.5.dev8276 → vis_agent-0.4.3.dev8313}/src/blockether/vis/engine/_council.py +0 -0
  52. {vis_agent-0.3.5.dev8276 → vis_agent-0.4.3.dev8313}/src/blockether/vis/engine/_extensions.py +0 -0
  53. {vis_agent-0.3.5.dev8276 → vis_agent-0.4.3.dev8313}/src/blockether/vis/engine/_host.py +0 -0
  54. {vis_agent-0.3.5.dev8276 → vis_agent-0.4.3.dev8313}/src/blockether/vis/engine/_install.py +0 -0
  55. {vis_agent-0.3.5.dev8276 → vis_agent-0.4.3.dev8313}/src/blockether/vis/engine/_local.py +0 -0
  56. {vis_agent-0.3.5.dev8276 → vis_agent-0.4.3.dev8313}/src/blockether/vis/engine/_models.py +0 -0
  57. {vis_agent-0.3.5.dev8276 → vis_agent-0.4.3.dev8313}/src/blockether/vis/engine/_speech.py +0 -0
  58. {vis_agent-0.3.5.dev8276 → vis_agent-0.4.3.dev8313}/src/blockether/vis/evals/__init__.py +0 -0
  59. {vis_agent-0.3.5.dev8276 → vis_agent-0.4.3.dev8313}/src/blockether/vis/evals/_gateway.py +0 -0
  60. {vis_agent-0.3.5.dev8276 → vis_agent-0.4.3.dev8313}/src/blockether/vis/evals/agent.py +0 -0
  61. {vis_agent-0.3.5.dev8276 → vis_agent-0.4.3.dev8313}/src/blockether/vis/evals/bench.py +0 -0
  62. {vis_agent-0.3.5.dev8276 → vis_agent-0.4.3.dev8313}/src/blockether/vis/evals/check.py +0 -0
  63. {vis_agent-0.3.5.dev8276 → vis_agent-0.4.3.dev8313}/src/blockether/vis/evals/compare.py +0 -0
  64. {vis_agent-0.3.5.dev8276 → vis_agent-0.4.3.dev8313}/src/blockether/vis/evals/environment.py +0 -0
  65. {vis_agent-0.3.5.dev8276 → vis_agent-0.4.3.dev8313}/src/blockether/vis/evals/grade.py +0 -0
  66. {vis_agent-0.3.5.dev8276 → vis_agent-0.4.3.dev8313}/src/blockether/vis/evals/harbor.py +0 -0
  67. {vis_agent-0.3.5.dev8276 → vis_agent-0.4.3.dev8313}/src/blockether/vis/evals/judge-rubric.toml +0 -0
  68. {vis_agent-0.3.5.dev8276 → vis_agent-0.4.3.dev8313}/src/blockether/vis/evals/judge.py +0 -0
  69. {vis_agent-0.3.5.dev8276 → vis_agent-0.4.3.dev8313}/src/blockether/vis/evals/labels.py +0 -0
  70. {vis_agent-0.3.5.dev8276 → vis_agent-0.4.3.dev8313}/src/blockether/vis/evals/leaderboard.py +0 -0
  71. {vis_agent-0.3.5.dev8276 → vis_agent-0.4.3.dev8313}/src/blockether/vis/evals/lic.py +0 -0
  72. {vis_agent-0.3.5.dev8276 → vis_agent-0.4.3.dev8313}/src/blockether/vis/evals/longmemeval.py +0 -0
  73. {vis_agent-0.3.5.dev8276 → vis_agent-0.4.3.dev8313}/src/blockether/vis/evals/mrcr.py +0 -0
  74. {vis_agent-0.3.5.dev8276 → vis_agent-0.4.3.dev8313}/src/blockether/vis/evals/repeats.py +0 -0
  75. {vis_agent-0.3.5.dev8276 → vis_agent-0.4.3.dev8313}/src/blockether/vis/evals/report.py +0 -0
  76. {vis_agent-0.3.5.dev8276 → vis_agent-0.4.3.dev8313}/src/blockether/vis/evals/reports.py +0 -0
  77. {vis_agent-0.3.5.dev8276 → vis_agent-0.4.3.dev8313}/src/blockether/vis/evals/rounds.py +0 -0
  78. {vis_agent-0.3.5.dev8276 → vis_agent-0.4.3.dev8313}/src/blockether/vis/evals/stages.py +0 -0
  79. {vis_agent-0.3.5.dev8276 → vis_agent-0.4.3.dev8313}/src/blockether/vis/evals/stats.py +0 -0
  80. {vis_agent-0.3.5.dev8276 → vis_agent-0.4.3.dev8313}/src/blockether/vis/evals/trajectory.py +0 -0
  81. {vis_agent-0.3.5.dev8276 → vis_agent-0.4.3.dev8313}/src/blockether/vis/py.typed +0 -0
  82. {vis_agent-0.3.5.dev8276 → vis_agent-0.4.3.dev8313}/src/blockether/vis/views.py +0 -0
  83. {vis_agent-0.3.5.dev8276 → vis_agent-0.4.3.dev8313}/tests/conftest.py +0 -0
  84. {vis_agent-0.3.5.dev8276 → vis_agent-0.4.3.dev8313}/tests/evals_atif.py +0 -0
  85. {vis_agent-0.3.5.dev8276 → vis_agent-0.4.3.dev8313}/tests/test_activity.py +0 -0
  86. {vis_agent-0.3.5.dev8276 → vis_agent-0.4.3.dev8313}/tests/test_agent.py +0 -0
  87. {vis_agent-0.3.5.dev8276 → vis_agent-0.4.3.dev8313}/tests/test_agent_extension_lifecycle.py +0 -0
  88. {vis_agent-0.3.5.dev8276 → vis_agent-0.4.3.dev8313}/tests/test_agent_extensions.py +0 -0
  89. {vis_agent-0.3.5.dev8276 → vis_agent-0.4.3.dev8313}/tests/test_agents.py +0 -0
  90. {vis_agent-0.3.5.dev8276 → vis_agent-0.4.3.dev8313}/tests/test_api_guides.py +0 -0
  91. {vis_agent-0.3.5.dev8276 → vis_agent-0.4.3.dev8313}/tests/test_app_attachments.py +0 -0
  92. {vis_agent-0.3.5.dev8276 → vis_agent-0.4.3.dev8313}/tests/test_app_door.py +0 -0
  93. {vis_agent-0.3.5.dev8276 → vis_agent-0.4.3.dev8313}/tests/test_attachments.py +0 -0
  94. {vis_agent-0.3.5.dev8276 → vis_agent-0.4.3.dev8313}/tests/test_authoring.py +0 -0
  95. {vis_agent-0.3.5.dev8276 → vis_agent-0.4.3.dev8313}/tests/test_catalog.py +0 -0
  96. {vis_agent-0.3.5.dev8276 → vis_agent-0.4.3.dev8313}/tests/test_catalog_recipe.py +0 -0
  97. {vis_agent-0.3.5.dev8276 → vis_agent-0.4.3.dev8313}/tests/test_ci_report_recipe.py +0 -0
  98. {vis_agent-0.3.5.dev8276 → vis_agent-0.4.3.dev8313}/tests/test_client.py +0 -0
  99. {vis_agent-0.3.5.dev8276 → vis_agent-0.4.3.dev8313}/tests/test_client_extensions_engine.py +0 -0
  100. {vis_agent-0.3.5.dev8276 → vis_agent-0.4.3.dev8313}/tests/test_code_quality_recipe.py +0 -0
  101. {vis_agent-0.3.5.dev8276 → vis_agent-0.4.3.dev8313}/tests/test_contracts.py +0 -0
  102. {vis_agent-0.3.5.dev8276 → vis_agent-0.4.3.dev8313}/tests/test_council.py +0 -0
  103. {vis_agent-0.3.5.dev8276 → vis_agent-0.4.3.dev8313}/tests/test_declarations.py +0 -0
  104. {vis_agent-0.3.5.dev8276 → vis_agent-0.4.3.dev8313}/tests/test_engine.py +0 -0
  105. {vis_agent-0.3.5.dev8276 → vis_agent-0.4.3.dev8313}/tests/test_engine_docstrings.py +0 -0
  106. {vis_agent-0.3.5.dev8276 → vis_agent-0.4.3.dev8313}/tests/test_evals_agent.py +0 -0
  107. {vis_agent-0.3.5.dev8276 → vis_agent-0.4.3.dev8313}/tests/test_evals_bench.py +0 -0
  108. {vis_agent-0.3.5.dev8276 → vis_agent-0.4.3.dev8313}/tests/test_evals_check.py +0 -0
  109. {vis_agent-0.3.5.dev8276 → vis_agent-0.4.3.dev8313}/tests/test_evals_compare.py +0 -0
  110. {vis_agent-0.3.5.dev8276 → vis_agent-0.4.3.dev8313}/tests/test_evals_environment.py +0 -0
  111. {vis_agent-0.3.5.dev8276 → vis_agent-0.4.3.dev8313}/tests/test_evals_gateway.py +0 -0
  112. {vis_agent-0.3.5.dev8276 → vis_agent-0.4.3.dev8313}/tests/test_evals_grade.py +0 -0
  113. {vis_agent-0.3.5.dev8276 → vis_agent-0.4.3.dev8313}/tests/test_evals_guards.py +0 -0
  114. {vis_agent-0.3.5.dev8276 → vis_agent-0.4.3.dev8313}/tests/test_evals_harbor.py +0 -0
  115. {vis_agent-0.3.5.dev8276 → vis_agent-0.4.3.dev8313}/tests/test_evals_judge.py +0 -0
  116. {vis_agent-0.3.5.dev8276 → vis_agent-0.4.3.dev8313}/tests/test_evals_labels.py +0 -0
  117. {vis_agent-0.3.5.dev8276 → vis_agent-0.4.3.dev8313}/tests/test_evals_lic.py +0 -0
  118. {vis_agent-0.3.5.dev8276 → vis_agent-0.4.3.dev8313}/tests/test_evals_longmemeval.py +0 -0
  119. {vis_agent-0.3.5.dev8276 → vis_agent-0.4.3.dev8313}/tests/test_evals_mrcr.py +0 -0
  120. {vis_agent-0.3.5.dev8276 → vis_agent-0.4.3.dev8313}/tests/test_evals_repeats.py +0 -0
  121. {vis_agent-0.3.5.dev8276 → vis_agent-0.4.3.dev8313}/tests/test_evals_report.py +0 -0
  122. {vis_agent-0.3.5.dev8276 → vis_agent-0.4.3.dev8313}/tests/test_evals_rounds.py +0 -0
  123. {vis_agent-0.3.5.dev8276 → vis_agent-0.4.3.dev8313}/tests/test_evals_stages.py +0 -0
  124. {vis_agent-0.3.5.dev8276 → vis_agent-0.4.3.dev8313}/tests/test_evals_stats.py +0 -0
  125. {vis_agent-0.3.5.dev8276 → vis_agent-0.4.3.dev8313}/tests/test_evals_tasks.py +0 -0
  126. {vis_agent-0.3.5.dev8276 → vis_agent-0.4.3.dev8313}/tests/test_evals_trajectory.py +0 -0
  127. {vis_agent-0.3.5.dev8276 → vis_agent-0.4.3.dev8313}/tests/test_extension_package.py +0 -0
  128. {vis_agent-0.3.5.dev8276 → vis_agent-0.4.3.dev8313}/tests/test_extension_reference.py +0 -0
  129. {vis_agent-0.3.5.dev8276 → vis_agent-0.4.3.dev8313}/tests/test_gateway_performance.py +0 -0
  130. {vis_agent-0.3.5.dev8276 → vis_agent-0.4.3.dev8313}/tests/test_goals.py +0 -0
  131. {vis_agent-0.3.5.dev8276 → vis_agent-0.4.3.dev8313}/tests/test_install.py +0 -0
  132. {vis_agent-0.3.5.dev8276 → vis_agent-0.4.3.dev8313}/tests/test_keybindings.py +0 -0
  133. {vis_agent-0.3.5.dev8276 → vis_agent-0.4.3.dev8313}/tests/test_live_wait.py +0 -0
  134. {vis_agent-0.3.5.dev8276 → vis_agent-0.4.3.dev8313}/tests/test_local.py +0 -0
  135. {vis_agent-0.3.5.dev8276 → vis_agent-0.4.3.dev8313}/tests/test_method_typing.py +0 -0
  136. {vis_agent-0.3.5.dev8276 → vis_agent-0.4.3.dev8313}/tests/test_models.py +0 -0
  137. {vis_agent-0.3.5.dev8276 → vis_agent-0.4.3.dev8313}/tests/test_monitor_recipe.py +0 -0
  138. {vis_agent-0.3.5.dev8276 → vis_agent-0.4.3.dev8313}/tests/test_namespace.py +0 -0
  139. {vis_agent-0.3.5.dev8276 → vis_agent-0.4.3.dev8313}/tests/test_outside.py +0 -0
  140. {vis_agent-0.3.5.dev8276 → vis_agent-0.4.3.dev8313}/tests/test_providers.py +0 -0
  141. {vis_agent-0.3.5.dev8276 → vis_agent-0.4.3.dev8313}/tests/test_read_session.py +0 -0
  142. {vis_agent-0.3.5.dev8276 → vis_agent-0.4.3.dev8313}/tests/test_record_arguments.py +0 -0
  143. {vis_agent-0.3.5.dev8276 → vis_agent-0.4.3.dev8313}/tests/test_registration.py +0 -0
  144. {vis_agent-0.3.5.dev8276 → vis_agent-0.4.3.dev8313}/tests/test_repair_hooks.py +0 -0
  145. {vis_agent-0.3.5.dev8276 → vis_agent-0.4.3.dev8313}/tests/test_result_sequence.py +0 -0
  146. {vis_agent-0.3.5.dev8276 → vis_agent-0.4.3.dev8313}/tests/test_scoped_settings.py +0 -0
  147. {vis_agent-0.3.5.dev8276 → vis_agent-0.4.3.dev8313}/tests/test_sdk_bootstrap.py +0 -0
  148. {vis_agent-0.3.5.dev8276 → vis_agent-0.4.3.dev8313}/tests/test_sdk_guide.py +0 -0
  149. {vis_agent-0.3.5.dev8276 → vis_agent-0.4.3.dev8313}/tests/test_sdk_typing.py +0 -0
  150. {vis_agent-0.3.5.dev8276 → vis_agent-0.4.3.dev8313}/tests/test_shortcuts_live.py +0 -0
  151. {vis_agent-0.3.5.dev8276 → vis_agent-0.4.3.dev8313}/tests/test_speech.py +0 -0
  152. {vis_agent-0.3.5.dev8276 → vis_agent-0.4.3.dev8313}/tests/test_structured_live.py +0 -0
  153. {vis_agent-0.3.5.dev8276 → vis_agent-0.4.3.dev8313}/tests/test_symbol_contract.py +0 -0
  154. {vis_agent-0.3.5.dev8276 → vis_agent-0.4.3.dev8313}/tests/test_testing.py +0 -0
  155. {vis_agent-0.3.5.dev8276 → vis_agent-0.4.3.dev8313}/tests/test_text_list_arguments.py +0 -0
  156. {vis_agent-0.3.5.dev8276 → vis_agent-0.4.3.dev8313}/tests/test_tools.py +0 -0
  157. {vis_agent-0.3.5.dev8276 → vis_agent-0.4.3.dev8313}/tests/test_views.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: vis-agent
3
- Version: 0.3.5.dev8276
3
+ Version: 0.4.3.dev8313
4
4
  Summary: The Vis Python SDK — author extensions and control local or remote engines.
5
5
  Project-URL: Homepage, https://vis.blockether.com
6
6
  Project-URL: Documentation, https://vis.blockether.com
@@ -638,16 +638,12 @@
638
638
  "type": "integer",
639
639
  "minimum": 1
640
640
  },
641
- "kind": {
642
- "type": "string"
643
- },
644
641
  "content": {
645
642
  "type": "string"
646
643
  }
647
644
  },
648
645
  "required": [
649
646
  "entry_id",
650
- "kind",
651
647
  "content"
652
648
  ],
653
649
  "additionalProperties": false
@@ -1493,6 +1493,14 @@
1493
1493
  ],
1494
1494
  "default": "activate"
1495
1495
  },
1496
+ "variant": {
1497
+ "enum": [
1498
+ "primary",
1499
+ "secondary",
1500
+ "destructive"
1501
+ ],
1502
+ "default": "secondary"
1503
+ },
1496
1504
  "is_disabled": {
1497
1505
  "type": "boolean"
1498
1506
  },
@@ -8,7 +8,7 @@ build-backend = "hatchling.build"
8
8
 
9
9
  [project]
10
10
  name = "vis-agent"
11
- version = "0.3.5.dev8276"
11
+ version = "0.4.3.dev8313"
12
12
  description = "The Vis Python SDK — author extensions and control local or remote engines."
13
13
  readme = "README.md"
14
14
  requires-python = ">=3.12"
@@ -0,0 +1 @@
1
+ beta-6c75eae554a843314b4614ffb130645993e96acb
@@ -61,6 +61,22 @@ def live_items(key: str, items: list[dict[str, Any]]) -> list[dict[str, Any]]:
61
61
  return result
62
62
 
63
63
 
64
+ def _button_branch() -> dict[str, Any]:
65
+ return next(
66
+ branch
67
+ for branch in definition("view", "component")["oneOf"]
68
+ if branch["properties"]["type"].get("const") == "button"
69
+ )
70
+
71
+
72
+ def button_variant_error(variant: Any) -> str | None:
73
+ """Answer why a declared button variant is invalid, or None when it is valid."""
74
+ allowed = _button_branch()["properties"]["variant"]["enum"]
75
+ if variant is None or variant in allowed:
76
+ return None
77
+ return f"button variant must be one of {', '.join(allowed)}, not {variant!r}"
78
+
79
+
64
80
  def _defaults(key: str) -> dict[str, Any]:
65
81
  properties = definition("view", key)["properties"]
66
82
  return {
@@ -1076,6 +1076,10 @@ def _live_check_node(node, seen, *, is_declaration=True):
1076
1076
  return f"a {kind} input has no value: only the host holds its handle"
1077
1077
  node_id = node.get("id")
1078
1078
  if not isinstance(node_id, str) or not node_id.strip():
1079
+ if is_group:
1080
+ from .extension import _LIVE_GROUP_NEEDS_ID
1081
+
1082
+ return _LIVE_GROUP_NEEDS_ID
1079
1083
  return f"a {kind} node needs an id"
1080
1084
  if node_id in seen:
1081
1085
  return f"two nodes answer to {node_id!r}"
@@ -1129,6 +1133,9 @@ def _live_check_node(node, seen, *, is_declaration=True):
1129
1133
  return "a button needs a label"
1130
1134
  if is_declaration and "clicks" in node:
1131
1135
  return "button clicks are stamped by the engine"
1136
+ complaint = _contracts.button_variant_error(node.get("variant"))
1137
+ if complaint:
1138
+ return complaint
1132
1139
  node.setdefault("clicks", 0)
1133
1140
  node.setdefault("is_disabled", False)
1134
1141
  if type(node["clicks"]) is not int or node["clicks"] < 0:
@@ -1578,7 +1578,7 @@ class ExecutionLayer(ABC):
1578
1578
  def get_session_fs_file(
1579
1579
  self, sid: str, *, query: Query | None = None, timeout: float | None = None
1580
1580
  ) -> JSONValue:
1581
- """GET /v1/sessions/:sid/fs/file — read one workspace file as text."""
1581
+ """GET /v1/sessions/:sid/fs/file — preview one workspace file by its kind."""
1582
1582
  response = self._request(
1583
1583
  "GET",
1584
1584
  "/v1/sessions/:sid/fs/file",
@@ -6277,6 +6277,12 @@ def live(title, nodes, placement=None, key=None, on_event=None, **options):
6277
6277
 
6278
6278
  # -- Extension live-view test harness -----------------------------------------
6279
6279
 
6280
+ # The refusal of a live group without an id, in the recorder and the outside host.
6281
+ _LIVE_GROUP_NEEDS_ID = (
6282
+ "a live view group needs an id, because patches and after= name it: "
6283
+ "write vis.ui.group('actions', ...) or vis.ui.wrap('actions', ...)"
6284
+ )
6285
+
6280
6286
 
6281
6287
  class _LiveRecorder:
6282
6288
  """An isolated in-memory live host for testing any extension live view.
@@ -6366,6 +6372,12 @@ class _LiveRecorder:
6366
6372
  node.setdefault("variant", "braille")
6367
6373
  node.setdefault("is_active", True)
6368
6374
  elif kind == "button":
6375
+ from blockether.vis import _contracts
6376
+
6377
+ complaint = _contracts.button_variant_error(node.get("variant"))
6378
+ if complaint:
6379
+ # Vis raises VisToolError, a RuntimeError, so the recorder does too.
6380
+ raise RuntimeError(f"button {node.get('id')}: {complaint}")
6369
6381
  node["clicks"] = 0
6370
6382
  node.setdefault("is_disabled", False)
6371
6383
  elif kind == "card" and node.get("is_selectable"):
@@ -6568,12 +6580,9 @@ class _LiveRecorder:
6568
6580
  if action == "open":
6569
6581
  for node in self._nodes(envelope["view"].get("nodes")):
6570
6582
  # The engine refuses a live group without an id, so a test must too.
6583
+ # Vis raises VisToolError, a RuntimeError, so the recorder does too.
6571
6584
  if node.get("type") == "group" and not node.get("id"):
6572
- raise AssertionError(
6573
- "a live view group needs an id, because patches and "
6574
- "after= name it: write vis.ui.group('actions', ...) "
6575
- "or vis.ui.wrap('actions', ...)"
6576
- )
6585
+ raise RuntimeError(_LIVE_GROUP_NEEDS_ID)
6577
6586
  self._view = self._materialize(envelope["view"])
6578
6587
  self._declared = set(envelope.get("validators") or [])
6579
6588
  self._verdicts = {}
@@ -7077,14 +7086,22 @@ def _ui_spinner(node_id, text="Working", *, variant="braille", is_active=True, *
7077
7086
  )
7078
7087
 
7079
7088
 
7080
- def _ui_button(node_id, label, *, is_disabled=False, **spec):
7089
+ def _ui_button(node_id, label, *, is_disabled=False, variant=None, **spec):
7081
7090
  """An operator action. Accepted presses increment ``clicks`` in ``view.state()``.
7082
7091
 
7083
- No callback or code crosses the wire. The producer decides how to respond.
7092
+ ``variant`` is ``"primary"``, ``"secondary"`` (the default) or ``"destructive"``.
7093
+ Give one primary action per group. No callback or code crosses the wire.
7084
7094
  Disabled buttons and completed receipts cannot be activated.
7085
7095
  """
7096
+ from blockether.vis import _contracts
7097
+
7098
+ complaint = _contracts.button_variant_error(variant)
7099
+ if complaint:
7100
+ raise ValueError(f"button {node_id}: {complaint}")
7086
7101
  return _live_node(
7087
- "button", node_id, dict(spec, label=str(label), is_disabled=is_disabled)
7102
+ "button",
7103
+ node_id,
7104
+ dict(spec, label=str(label), is_disabled=is_disabled, variant=variant),
7088
7105
  )
7089
7106
 
7090
7107
 
@@ -43,6 +43,8 @@ LATEST = "latest"
43
43
  LOCK_WAIT_S = 300
44
44
  MAX_METADATA = 128 * 1024
45
45
  _EXCLUDED = {".git", ".venv", "venv", "__pycache__", "node_modules", ".DS_Store"}
46
+ # `.env*` holds secrets. `.venv-*` is an environment that Vis prepares for one interpreter.
47
+ _EXCLUDED_PREFIXES = (".env", ".venv-")
46
48
 
47
49
 
48
50
  def _relative(value):
@@ -51,7 +53,7 @@ def _relative(value):
51
53
  if len(value) > 512 or any(ord(c) < 32 or ord(c) == 127 for c in value):
52
54
  raise ValueError("Project paths are too long or contain control characters")
53
55
  if any(
54
- p in {"", ".", ".."} or p in _EXCLUDED or p.startswith(".env")
56
+ p in {"", ".", ".."} or p in _EXCLUDED or p.startswith(_EXCLUDED_PREFIXES)
55
57
  for p in value.split("/")
56
58
  ):
57
59
  raise ValueError("Project contains an unsafe or excluded path")
@@ -346,7 +348,11 @@ def _checkout(repository, directory, revision):
346
348
 
347
349
  def _copy_project(source, destination):
348
350
  def excluded(_directory, names):
349
- return {name for name in names if name in _EXCLUDED or name.startswith(".env")}
351
+ return {
352
+ name
353
+ for name in names
354
+ if name in _EXCLUDED or name.startswith(_EXCLUDED_PREFIXES)
355
+ }
350
356
 
351
357
  count, size = 0, 0
352
358
  for base, directories, files in os.walk(source):
@@ -241,16 +241,44 @@ def test_table_builder_refuses_text_columns():
241
241
  vis.ui.table("chats", columns=[vis.ui.table_column("chat", "Chat"), "Category"])
242
242
 
243
243
 
244
+ def test_button_variant_is_kept_and_a_typo_names_the_variants():
245
+ # #430: a button had no hierarchy, and a misspelled variant reached the host.
246
+ assert vis.ui.button("save", "Save", variant="primary")["variant"] == "primary"
247
+ assert "variant" not in vis.ui.button("save", "Save")
248
+ with pytest.raises(ValueError, match=r"primary, secondary, destructive.*'danger'"):
249
+ vis.ui.button("drop", "Drop", variant="danger")
250
+
251
+
252
+ def test_outside_refuses_an_unknown_button_variant():
253
+ # #430: outside Vis a raw button with a typo variant was accepted.
254
+ button = {"id": "drop", "type": "button", "label": "Drop", "variant": "danger"}
255
+ view = {"title": "Rows", "nodes": [button]}
256
+ with pytest.raises(RuntimeError, match=r"primary, secondary, destructive"):
257
+ _outside.live(json.dumps({"op": "open", "view": view}))
258
+
259
+
244
260
  def test_recorder_refuses_a_live_group_without_an_id(monkeypatch):
245
261
  # #428: the recorder hid the buttons of an unnamed group; the engine refuses it.
262
+ # Vis raises VisToolError, a RuntimeError, so the recorder must raise one too.
246
263
  monkeypatch.setattr(vis, "_host", vis.testing.LiveRecorder(vis._host))
247
- with pytest.raises(AssertionError, match="group needs an id"):
264
+ with pytest.raises(
265
+ RuntimeError, match=r"group needs an id.*vis\.ui\.group\('actions'"
266
+ ):
248
267
  with vis.live(
249
268
  "Chats", [vis.ui.wrap(vis.ui.button("save", "Save"))], flush_ms=0
250
269
  ):
251
270
  pass
252
271
 
253
272
 
273
+ def test_outside_refuses_a_live_group_without_an_id_with_the_fix():
274
+ # #428: outside Vis the refusal named no fix for an unnamed group.
275
+ view = {"title": "Chats", "nodes": [vis.ui.wrap(vis.ui.button("save", "Save"))]}
276
+ with pytest.raises(
277
+ RuntimeError, match=r"group needs an id.*vis\.ui\.group\('actions'"
278
+ ):
279
+ _outside.live(json.dumps({"op": "open", "view": view}))
280
+
281
+
254
282
  def test_outside_refused_patch_leaves_state_unchanged():
255
283
  opened = json.loads(
256
284
  _outside.live(
@@ -1 +0,0 @@
1
- beta-809a2a6395ced27be7bf360ccf2673c39bf8cf6f