vis-agent 0.2.53.dev7782__tar.gz → 0.2.53.dev7784__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 (142) hide show
  1. {vis_agent-0.2.53.dev7782 → vis_agent-0.2.53.dev7784}/PKG-INFO +1 -1
  2. {vis_agent-0.2.53.dev7782 → vis_agent-0.2.53.dev7784}/pyproject.toml +1 -1
  3. vis_agent-0.2.53.dev7784/release-tag +1 -0
  4. {vis_agent-0.2.53.dev7782 → vis_agent-0.2.53.dev7784}/src/blockether/vis/engine/_extensions.py +1 -0
  5. {vis_agent-0.2.53.dev7782 → vis_agent-0.2.53.dev7784}/src/blockether/vis/extension.py +47 -0
  6. {vis_agent-0.2.53.dev7782 → vis_agent-0.2.53.dev7784}/tests/test_declarations.py +27 -0
  7. vis_agent-0.2.53.dev7782/release-tag +0 -1
  8. {vis_agent-0.2.53.dev7782 → vis_agent-0.2.53.dev7784}/.gitignore +0 -0
  9. {vis_agent-0.2.53.dev7782 → vis_agent-0.2.53.dev7784}/LICENSE +0 -0
  10. {vis_agent-0.2.53.dev7782 → vis_agent-0.2.53.dev7784}/README.md +0 -0
  11. {vis_agent-0.2.53.dev7782 → vis_agent-0.2.53.dev7784}/contracts/fixtures/activity-arguments.json +0 -0
  12. {vis_agent-0.2.53.dev7782 → vis_agent-0.2.53.dev7784}/contracts/fixtures/activity-cases.json +0 -0
  13. {vis_agent-0.2.53.dev7782 → vis_agent-0.2.53.dev7784}/contracts/fixtures/activity-copy.json +0 -0
  14. {vis_agent-0.2.53.dev7782 → vis_agent-0.2.53.dev7784}/contracts/fixtures/activity-execution.json +0 -0
  15. {vis_agent-0.2.53.dev7782 → vis_agent-0.2.53.dev7784}/contracts/fixtures/activity-groups.json +0 -0
  16. {vis_agent-0.2.53.dev7782 → vis_agent-0.2.53.dev7784}/contracts/fixtures/activity-read-session.json +0 -0
  17. {vis_agent-0.2.53.dev7782 → vis_agent-0.2.53.dev7784}/contracts/fixtures/activity-reads.json +0 -0
  18. {vis_agent-0.2.53.dev7782 → vis_agent-0.2.53.dev7784}/contracts/fixtures/activity-results.json +0 -0
  19. {vis_agent-0.2.53.dev7782 → vis_agent-0.2.53.dev7784}/contracts/fixtures/activity-tables.json +0 -0
  20. {vis_agent-0.2.53.dev7782 → vis_agent-0.2.53.dev7784}/contracts/fixtures/activity.json +0 -0
  21. {vis_agent-0.2.53.dev7782 → vis_agent-0.2.53.dev7784}/contracts/fixtures/diff.json +0 -0
  22. {vis_agent-0.2.53.dev7782 → vis_agent-0.2.53.dev7784}/contracts/fixtures/live-primitives.json +0 -0
  23. {vis_agent-0.2.53.dev7782 → vis_agent-0.2.53.dev7784}/contracts/fixtures/session-health.json +0 -0
  24. {vis_agent-0.2.53.dev7782 → vis_agent-0.2.53.dev7784}/contracts/fixtures/view.json +0 -0
  25. {vis_agent-0.2.53.dev7782 → vis_agent-0.2.53.dev7784}/contracts/schema/activity.json +0 -0
  26. {vis_agent-0.2.53.dev7782 → vis_agent-0.2.53.dev7784}/contracts/schema/agents.json +0 -0
  27. {vis_agent-0.2.53.dev7782 → vis_agent-0.2.53.dev7784}/contracts/schema/automations.json +0 -0
  28. {vis_agent-0.2.53.dev7782 → vis_agent-0.2.53.dev7784}/contracts/schema/common.json +0 -0
  29. {vis_agent-0.2.53.dev7782 → vis_agent-0.2.53.dev7784}/contracts/schema/config.json +0 -0
  30. {vis_agent-0.2.53.dev7782 → vis_agent-0.2.53.dev7784}/contracts/schema/content.json +0 -0
  31. {vis_agent-0.2.53.dev7782 → vis_agent-0.2.53.dev7784}/contracts/schema/council.json +0 -0
  32. {vis_agent-0.2.53.dev7782 → vis_agent-0.2.53.dev7784}/contracts/schema/diff.json +0 -0
  33. {vis_agent-0.2.53.dev7782 → vis_agent-0.2.53.dev7784}/contracts/schema/evals.json +0 -0
  34. {vis_agent-0.2.53.dev7782 → vis_agent-0.2.53.dev7784}/contracts/schema/gateway.json +0 -0
  35. {vis_agent-0.2.53.dev7782 → vis_agent-0.2.53.dev7784}/contracts/schema/improve.json +0 -0
  36. {vis_agent-0.2.53.dev7782 → vis_agent-0.2.53.dev7784}/contracts/schema/provider.json +0 -0
  37. {vis_agent-0.2.53.dev7782 → vis_agent-0.2.53.dev7784}/contracts/schema/rooms.json +0 -0
  38. {vis_agent-0.2.53.dev7782 → vis_agent-0.2.53.dev7784}/contracts/schema/symbol.json +0 -0
  39. {vis_agent-0.2.53.dev7782 → vis_agent-0.2.53.dev7784}/contracts/schema/toggle.json +0 -0
  40. {vis_agent-0.2.53.dev7782 → vis_agent-0.2.53.dev7784}/contracts/schema/view.json +0 -0
  41. {vis_agent-0.2.53.dev7782 → vis_agent-0.2.53.dev7784}/examples/greeter/tests/test_greeter.py +0 -0
  42. {vis_agent-0.2.53.dev7782 → vis_agent-0.2.53.dev7784}/hatch_build.py +0 -0
  43. {vis_agent-0.2.53.dev7782 → vis_agent-0.2.53.dev7784}/src/blockether/vis/__init__.py +0 -0
  44. {vis_agent-0.2.53.dev7782 → vis_agent-0.2.53.dev7784}/src/blockether/vis/_contracts.py +0 -0
  45. {vis_agent-0.2.53.dev7782 → vis_agent-0.2.53.dev7784}/src/blockether/vis/_outside.py +0 -0
  46. {vis_agent-0.2.53.dev7782 → vis_agent-0.2.53.dev7784}/src/blockether/vis/_wire.py +0 -0
  47. {vis_agent-0.2.53.dev7782 → vis_agent-0.2.53.dev7784}/src/blockether/vis/activity.py +0 -0
  48. {vis_agent-0.2.53.dev7782 → vis_agent-0.2.53.dev7784}/src/blockether/vis/engine/__init__.py +0 -0
  49. {vis_agent-0.2.53.dev7782 → vis_agent-0.2.53.dev7784}/src/blockether/vis/engine/_agent.py +0 -0
  50. {vis_agent-0.2.53.dev7782 → vis_agent-0.2.53.dev7784}/src/blockether/vis/engine/_agents.py +0 -0
  51. {vis_agent-0.2.53.dev7782 → vis_agent-0.2.53.dev7784}/src/blockether/vis/engine/_client.py +0 -0
  52. {vis_agent-0.2.53.dev7782 → vis_agent-0.2.53.dev7784}/src/blockether/vis/engine/_council.py +0 -0
  53. {vis_agent-0.2.53.dev7782 → vis_agent-0.2.53.dev7784}/src/blockether/vis/engine/_install.py +0 -0
  54. {vis_agent-0.2.53.dev7782 → vis_agent-0.2.53.dev7784}/src/blockether/vis/engine/_local.py +0 -0
  55. {vis_agent-0.2.53.dev7782 → vis_agent-0.2.53.dev7784}/src/blockether/vis/evals/__init__.py +0 -0
  56. {vis_agent-0.2.53.dev7782 → vis_agent-0.2.53.dev7784}/src/blockether/vis/evals/_gateway.py +0 -0
  57. {vis_agent-0.2.53.dev7782 → vis_agent-0.2.53.dev7784}/src/blockether/vis/evals/agent.py +0 -0
  58. {vis_agent-0.2.53.dev7782 → vis_agent-0.2.53.dev7784}/src/blockether/vis/evals/bench.py +0 -0
  59. {vis_agent-0.2.53.dev7782 → vis_agent-0.2.53.dev7784}/src/blockether/vis/evals/check.py +0 -0
  60. {vis_agent-0.2.53.dev7782 → vis_agent-0.2.53.dev7784}/src/blockether/vis/evals/compare.py +0 -0
  61. {vis_agent-0.2.53.dev7782 → vis_agent-0.2.53.dev7784}/src/blockether/vis/evals/environment.py +0 -0
  62. {vis_agent-0.2.53.dev7782 → vis_agent-0.2.53.dev7784}/src/blockether/vis/evals/grade.py +0 -0
  63. {vis_agent-0.2.53.dev7782 → vis_agent-0.2.53.dev7784}/src/blockether/vis/evals/harbor.py +0 -0
  64. {vis_agent-0.2.53.dev7782 → vis_agent-0.2.53.dev7784}/src/blockether/vis/evals/judge-rubric.toml +0 -0
  65. {vis_agent-0.2.53.dev7782 → vis_agent-0.2.53.dev7784}/src/blockether/vis/evals/judge.py +0 -0
  66. {vis_agent-0.2.53.dev7782 → vis_agent-0.2.53.dev7784}/src/blockether/vis/evals/labels.py +0 -0
  67. {vis_agent-0.2.53.dev7782 → vis_agent-0.2.53.dev7784}/src/blockether/vis/evals/leaderboard.py +0 -0
  68. {vis_agent-0.2.53.dev7782 → vis_agent-0.2.53.dev7784}/src/blockether/vis/evals/repeats.py +0 -0
  69. {vis_agent-0.2.53.dev7782 → vis_agent-0.2.53.dev7784}/src/blockether/vis/evals/rounds.py +0 -0
  70. {vis_agent-0.2.53.dev7782 → vis_agent-0.2.53.dev7784}/src/blockether/vis/evals/stages.py +0 -0
  71. {vis_agent-0.2.53.dev7782 → vis_agent-0.2.53.dev7784}/src/blockether/vis/evals/stats.py +0 -0
  72. {vis_agent-0.2.53.dev7782 → vis_agent-0.2.53.dev7784}/src/blockether/vis/evals/trajectory.py +0 -0
  73. {vis_agent-0.2.53.dev7782 → vis_agent-0.2.53.dev7784}/src/blockether/vis/extension_package.py +0 -0
  74. {vis_agent-0.2.53.dev7782 → vis_agent-0.2.53.dev7784}/src/blockether/vis/py.typed +0 -0
  75. {vis_agent-0.2.53.dev7782 → vis_agent-0.2.53.dev7784}/src/blockether/vis/rooms.py +0 -0
  76. {vis_agent-0.2.53.dev7782 → vis_agent-0.2.53.dev7784}/src/blockether/vis/views.py +0 -0
  77. {vis_agent-0.2.53.dev7782 → vis_agent-0.2.53.dev7784}/tests/conftest.py +0 -0
  78. {vis_agent-0.2.53.dev7782 → vis_agent-0.2.53.dev7784}/tests/evals_atif.py +0 -0
  79. {vis_agent-0.2.53.dev7782 → vis_agent-0.2.53.dev7784}/tests/test_activity.py +0 -0
  80. {vis_agent-0.2.53.dev7782 → vis_agent-0.2.53.dev7784}/tests/test_agent.py +0 -0
  81. {vis_agent-0.2.53.dev7782 → vis_agent-0.2.53.dev7784}/tests/test_agent_extension_lifecycle.py +0 -0
  82. {vis_agent-0.2.53.dev7782 → vis_agent-0.2.53.dev7784}/tests/test_agent_extensions.py +0 -0
  83. {vis_agent-0.2.53.dev7782 → vis_agent-0.2.53.dev7784}/tests/test_agents.py +0 -0
  84. {vis_agent-0.2.53.dev7782 → vis_agent-0.2.53.dev7784}/tests/test_api_guides.py +0 -0
  85. {vis_agent-0.2.53.dev7782 → vis_agent-0.2.53.dev7784}/tests/test_attachments.py +0 -0
  86. {vis_agent-0.2.53.dev7782 → vis_agent-0.2.53.dev7784}/tests/test_authoring.py +0 -0
  87. {vis_agent-0.2.53.dev7782 → vis_agent-0.2.53.dev7784}/tests/test_catalog.py +0 -0
  88. {vis_agent-0.2.53.dev7782 → vis_agent-0.2.53.dev7784}/tests/test_catalog_recipe.py +0 -0
  89. {vis_agent-0.2.53.dev7782 → vis_agent-0.2.53.dev7784}/tests/test_ci_report_recipe.py +0 -0
  90. {vis_agent-0.2.53.dev7782 → vis_agent-0.2.53.dev7784}/tests/test_client.py +0 -0
  91. {vis_agent-0.2.53.dev7782 → vis_agent-0.2.53.dev7784}/tests/test_client_extensions_engine.py +0 -0
  92. {vis_agent-0.2.53.dev7782 → vis_agent-0.2.53.dev7784}/tests/test_code_quality_recipe.py +0 -0
  93. {vis_agent-0.2.53.dev7782 → vis_agent-0.2.53.dev7784}/tests/test_contracts.py +0 -0
  94. {vis_agent-0.2.53.dev7782 → vis_agent-0.2.53.dev7784}/tests/test_council.py +0 -0
  95. {vis_agent-0.2.53.dev7782 → vis_agent-0.2.53.dev7784}/tests/test_engine.py +0 -0
  96. {vis_agent-0.2.53.dev7782 → vis_agent-0.2.53.dev7784}/tests/test_engine_docstrings.py +0 -0
  97. {vis_agent-0.2.53.dev7782 → vis_agent-0.2.53.dev7784}/tests/test_evals_agent.py +0 -0
  98. {vis_agent-0.2.53.dev7782 → vis_agent-0.2.53.dev7784}/tests/test_evals_bench.py +0 -0
  99. {vis_agent-0.2.53.dev7782 → vis_agent-0.2.53.dev7784}/tests/test_evals_check.py +0 -0
  100. {vis_agent-0.2.53.dev7782 → vis_agent-0.2.53.dev7784}/tests/test_evals_compare.py +0 -0
  101. {vis_agent-0.2.53.dev7782 → vis_agent-0.2.53.dev7784}/tests/test_evals_environment.py +0 -0
  102. {vis_agent-0.2.53.dev7782 → vis_agent-0.2.53.dev7784}/tests/test_evals_gateway.py +0 -0
  103. {vis_agent-0.2.53.dev7782 → vis_agent-0.2.53.dev7784}/tests/test_evals_grade.py +0 -0
  104. {vis_agent-0.2.53.dev7782 → vis_agent-0.2.53.dev7784}/tests/test_evals_guards.py +0 -0
  105. {vis_agent-0.2.53.dev7782 → vis_agent-0.2.53.dev7784}/tests/test_evals_harbor.py +0 -0
  106. {vis_agent-0.2.53.dev7782 → vis_agent-0.2.53.dev7784}/tests/test_evals_judge.py +0 -0
  107. {vis_agent-0.2.53.dev7782 → vis_agent-0.2.53.dev7784}/tests/test_evals_labels.py +0 -0
  108. {vis_agent-0.2.53.dev7782 → vis_agent-0.2.53.dev7784}/tests/test_evals_repeats.py +0 -0
  109. {vis_agent-0.2.53.dev7782 → vis_agent-0.2.53.dev7784}/tests/test_evals_rounds.py +0 -0
  110. {vis_agent-0.2.53.dev7782 → vis_agent-0.2.53.dev7784}/tests/test_evals_stages.py +0 -0
  111. {vis_agent-0.2.53.dev7782 → vis_agent-0.2.53.dev7784}/tests/test_evals_stats.py +0 -0
  112. {vis_agent-0.2.53.dev7782 → vis_agent-0.2.53.dev7784}/tests/test_evals_tasks.py +0 -0
  113. {vis_agent-0.2.53.dev7782 → vis_agent-0.2.53.dev7784}/tests/test_evals_trajectory.py +0 -0
  114. {vis_agent-0.2.53.dev7782 → vis_agent-0.2.53.dev7784}/tests/test_extension_package.py +0 -0
  115. {vis_agent-0.2.53.dev7782 → vis_agent-0.2.53.dev7784}/tests/test_extension_reference.py +0 -0
  116. {vis_agent-0.2.53.dev7782 → vis_agent-0.2.53.dev7784}/tests/test_gateway_performance.py +0 -0
  117. {vis_agent-0.2.53.dev7782 → vis_agent-0.2.53.dev7784}/tests/test_goals.py +0 -0
  118. {vis_agent-0.2.53.dev7782 → vis_agent-0.2.53.dev7784}/tests/test_install.py +0 -0
  119. {vis_agent-0.2.53.dev7782 → vis_agent-0.2.53.dev7784}/tests/test_live_primitives.py +0 -0
  120. {vis_agent-0.2.53.dev7782 → vis_agent-0.2.53.dev7784}/tests/test_live_wait.py +0 -0
  121. {vis_agent-0.2.53.dev7782 → vis_agent-0.2.53.dev7784}/tests/test_local.py +0 -0
  122. {vis_agent-0.2.53.dev7782 → vis_agent-0.2.53.dev7784}/tests/test_method_typing.py +0 -0
  123. {vis_agent-0.2.53.dev7782 → vis_agent-0.2.53.dev7784}/tests/test_monitor_recipe.py +0 -0
  124. {vis_agent-0.2.53.dev7782 → vis_agent-0.2.53.dev7784}/tests/test_namespace.py +0 -0
  125. {vis_agent-0.2.53.dev7782 → vis_agent-0.2.53.dev7784}/tests/test_outside.py +0 -0
  126. {vis_agent-0.2.53.dev7782 → vis_agent-0.2.53.dev7784}/tests/test_providers.py +0 -0
  127. {vis_agent-0.2.53.dev7782 → vis_agent-0.2.53.dev7784}/tests/test_read_session.py +0 -0
  128. {vis_agent-0.2.53.dev7782 → vis_agent-0.2.53.dev7784}/tests/test_record_arguments.py +0 -0
  129. {vis_agent-0.2.53.dev7782 → vis_agent-0.2.53.dev7784}/tests/test_registration.py +0 -0
  130. {vis_agent-0.2.53.dev7782 → vis_agent-0.2.53.dev7784}/tests/test_repair_hooks.py +0 -0
  131. {vis_agent-0.2.53.dev7782 → vis_agent-0.2.53.dev7784}/tests/test_result_sequence.py +0 -0
  132. {vis_agent-0.2.53.dev7782 → vis_agent-0.2.53.dev7784}/tests/test_rooms.py +0 -0
  133. {vis_agent-0.2.53.dev7782 → vis_agent-0.2.53.dev7784}/tests/test_scoped_settings.py +0 -0
  134. {vis_agent-0.2.53.dev7782 → vis_agent-0.2.53.dev7784}/tests/test_sdk_bootstrap.py +0 -0
  135. {vis_agent-0.2.53.dev7782 → vis_agent-0.2.53.dev7784}/tests/test_sdk_guide.py +0 -0
  136. {vis_agent-0.2.53.dev7782 → vis_agent-0.2.53.dev7784}/tests/test_sdk_typing.py +0 -0
  137. {vis_agent-0.2.53.dev7782 → vis_agent-0.2.53.dev7784}/tests/test_structured_live.py +0 -0
  138. {vis_agent-0.2.53.dev7782 → vis_agent-0.2.53.dev7784}/tests/test_symbol_contract.py +0 -0
  139. {vis_agent-0.2.53.dev7782 → vis_agent-0.2.53.dev7784}/tests/test_testing.py +0 -0
  140. {vis_agent-0.2.53.dev7782 → vis_agent-0.2.53.dev7784}/tests/test_text_list_arguments.py +0 -0
  141. {vis_agent-0.2.53.dev7782 → vis_agent-0.2.53.dev7784}/tests/test_tools.py +0 -0
  142. {vis_agent-0.2.53.dev7782 → vis_agent-0.2.53.dev7784}/tests/test_views.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: vis-agent
3
- Version: 0.2.53.dev7782
3
+ Version: 0.2.53.dev7784
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
@@ -8,7 +8,7 @@ build-backend = "hatchling.build"
8
8
 
9
9
  [project]
10
10
  name = "vis-agent"
11
- version = "0.2.53.dev7782"
11
+ version = "0.2.53.dev7784"
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-73c3a9c33c127594962deaa28836f5e9b9b487ac
@@ -88,6 +88,7 @@ class ClientExtensions:
88
88
  "network_filters",
89
89
  "slash_commands",
90
90
  "settings",
91
+ "grants",
91
92
  )
92
93
  if getattr(extension, name)
93
94
  ]
@@ -1127,6 +1127,8 @@ class Extension:
1127
1127
  network_filters: `NetworkFilter` declarations.
1128
1128
  settings: Host-owned `Setting` declarations with explicit allowed scopes.
1129
1129
  env: Environment variable names resolved at host registration.
1130
+ grants: `Grant` declarations: toolchain paths for this extension's
1131
+ `jailed_shell` children.
1130
1132
 
1131
1133
  Collection inputs are copied into tuples. Constructing an Extension is pure: it does
1132
1134
  not run callbacks or register anything. In an application, pass it to
@@ -1155,6 +1157,7 @@ class Extension:
1155
1157
  network_filters: Sequence[NetworkFilter] = ()
1156
1158
  settings: Sequence[Setting] = ()
1157
1159
  env: Sequence[str] = ()
1160
+ grants: Sequence[Grant] = ()
1158
1161
 
1159
1162
  def __post_init__(self):
1160
1163
  if not isinstance(self.name, str) or not self.name.strip():
@@ -1186,6 +1189,7 @@ class Extension:
1186
1189
  ("providers", Provider),
1187
1190
  ("network_filters", NetworkFilter),
1188
1191
  ("settings", Setting),
1192
+ ("grants", Grant),
1189
1193
  ):
1190
1194
  value = getattr(self, name)
1191
1195
  if not isinstance(value, (tuple, list)) or not all(
@@ -1219,6 +1223,7 @@ class Extension:
1219
1223
  "providers",
1220
1224
  "network_filters",
1221
1225
  "settings",
1226
+ "grants",
1222
1227
  }
1223
1228
  return {
1224
1229
  field.name: (
@@ -2712,6 +2717,48 @@ class NetworkFilter:
2712
2717
  return {"marker": "network_filter", "fn": self.fn}
2713
2718
 
2714
2719
 
2720
+ @dataclass(frozen=True, slots=True)
2721
+ class Grant:
2722
+ """Filesystem access that this extension's own jailed child processes need.
2723
+
2724
+ A toolchain often lives outside the workspace: a JDK, a package cache or a
2725
+ launcher. Declare each path once. The host checks every grant at registration.
2726
+ It adds the grant only to the `jailed_shell` children of this extension. The
2727
+ model's shell never receives it, and the jail deny rules still win.
2728
+
2729
+ Args:
2730
+ path: Absolute path, or a path that starts with `~`.
2731
+ mode: `"read"` to read and run files, or `"read_write"` for a cache that
2732
+ the tool also writes.
2733
+ reason: Short text that tells people why the extension needs the path.
2734
+
2735
+ Raises:
2736
+ ValueError: The path or reason is blank, or the mode is unknown.
2737
+ """
2738
+
2739
+ path: str | PathLike[str]
2740
+ mode: Literal["read", "read_write"] = "read"
2741
+ reason: str = field(kw_only=True)
2742
+
2743
+ def __post_init__(self):
2744
+ path = os.fspath(self.path) if isinstance(self.path, PathLike) else self.path
2745
+ if not isinstance(path, str) or not path.strip():
2746
+ raise ValueError("vis.Grant requires path=<non-empty string>")
2747
+ object.__setattr__(self, "path", path)
2748
+ if self.mode not in ("read", "read_write"):
2749
+ raise ValueError('vis.Grant mode must be "read" or "read_write"')
2750
+ if not isinstance(self.reason, str) or not self.reason.strip():
2751
+ raise ValueError("vis.Grant requires reason=<non-empty string>")
2752
+
2753
+ def _spec(self):
2754
+ return {
2755
+ "marker": "grant",
2756
+ "path": self.path,
2757
+ "mode": self.mode,
2758
+ "reason": self.reason,
2759
+ }
2760
+
2761
+
2715
2762
  # Provider vocabularies are pinned to the canonical provider/config JSON in tests.
2716
2763
  ProviderJSON: TypeAlias = (
2717
2764
  str
@@ -87,6 +87,7 @@ def test_dictionary_builders_are_retired(name):
87
87
  "slash_commands",
88
88
  "op_hooks",
89
89
  "network_filters",
90
+ "grants",
90
91
  ],
91
92
  )
92
93
  def test_declarations_refuse_untyped_children(field, monkeypatch):
@@ -324,3 +325,29 @@ def test_registration_snapshots_collections_and_provider_config(monkeypatch):
324
325
  }
325
326
  wire["providers"][0]["preset"]["extra_body"]["models"].clear()
326
327
  assert provider.preset.extra_body["models"][0]["name"] == "example"
328
+
329
+
330
+ # Regression for #394: an extension declares its toolchain paths as typed grants.
331
+ def test_grant_declarations_reach_the_wire(tmp_path):
332
+ grant = vis.Grant(tmp_path, "read_write", reason="Cache")
333
+ assert grant.path == str(tmp_path)
334
+ extension = vis.Extension(
335
+ name="tools",
336
+ description="Tools",
337
+ grants=[grant, vis.Grant("/opt/jdk", reason="JDK")],
338
+ )
339
+ assert extension._spec()["grants"] == [
340
+ {
341
+ "marker": "grant",
342
+ "path": str(tmp_path),
343
+ "mode": "read_write",
344
+ "reason": "Cache",
345
+ },
346
+ {"marker": "grant", "path": "/opt/jdk", "mode": "read", "reason": "JDK"},
347
+ ]
348
+ with pytest.raises(ValueError, match="mode"):
349
+ vis.Grant("/opt/jdk", "write", reason="JDK")
350
+ with pytest.raises(ValueError, match="reason"):
351
+ vis.Grant("/opt/jdk", reason=" ")
352
+ with pytest.raises(TypeError):
353
+ vis.Grant("/opt/jdk")
@@ -1 +0,0 @@
1
- beta-b03f680fc34d8769728de10fc9d67b1e3b064fab