vis-agent 0.2.52.dev7755__tar.gz → 0.2.53__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 (141) hide show
  1. {vis_agent-0.2.52.dev7755 → vis_agent-0.2.53}/PKG-INFO +1 -1
  2. {vis_agent-0.2.52.dev7755 → vis_agent-0.2.53}/contracts/schema/agents.json +72 -12
  3. {vis_agent-0.2.52.dev7755 → vis_agent-0.2.53}/contracts/schema/gateway.json +9 -1
  4. {vis_agent-0.2.52.dev7755 → vis_agent-0.2.53}/pyproject.toml +1 -1
  5. {vis_agent-0.2.52.dev7755 → vis_agent-0.2.53}/src/blockether/vis/engine/_agents.py +1 -1
  6. {vis_agent-0.2.52.dev7755 → vis_agent-0.2.53}/src/blockether/vis/engine/_council.py +4 -1
  7. {vis_agent-0.2.52.dev7755 → vis_agent-0.2.53}/src/blockether/vis/engine/_extensions.py +1 -0
  8. {vis_agent-0.2.52.dev7755 → vis_agent-0.2.53}/src/blockether/vis/extension.py +47 -0
  9. {vis_agent-0.2.52.dev7755 → vis_agent-0.2.53}/tests/test_agents.py +5 -2
  10. {vis_agent-0.2.52.dev7755 → vis_agent-0.2.53}/tests/test_client.py +0 -1
  11. {vis_agent-0.2.52.dev7755 → vis_agent-0.2.53}/tests/test_declarations.py +27 -0
  12. vis_agent-0.2.52.dev7755/release-tag +0 -1
  13. {vis_agent-0.2.52.dev7755 → vis_agent-0.2.53}/.gitignore +0 -0
  14. {vis_agent-0.2.52.dev7755 → vis_agent-0.2.53}/LICENSE +0 -0
  15. {vis_agent-0.2.52.dev7755 → vis_agent-0.2.53}/README.md +0 -0
  16. {vis_agent-0.2.52.dev7755 → vis_agent-0.2.53}/contracts/fixtures/activity-arguments.json +0 -0
  17. {vis_agent-0.2.52.dev7755 → vis_agent-0.2.53}/contracts/fixtures/activity-cases.json +0 -0
  18. {vis_agent-0.2.52.dev7755 → vis_agent-0.2.53}/contracts/fixtures/activity-copy.json +0 -0
  19. {vis_agent-0.2.52.dev7755 → vis_agent-0.2.53}/contracts/fixtures/activity-execution.json +0 -0
  20. {vis_agent-0.2.52.dev7755 → vis_agent-0.2.53}/contracts/fixtures/activity-groups.json +0 -0
  21. {vis_agent-0.2.52.dev7755 → vis_agent-0.2.53}/contracts/fixtures/activity-read-session.json +0 -0
  22. {vis_agent-0.2.52.dev7755 → vis_agent-0.2.53}/contracts/fixtures/activity-reads.json +0 -0
  23. {vis_agent-0.2.52.dev7755 → vis_agent-0.2.53}/contracts/fixtures/activity-results.json +0 -0
  24. {vis_agent-0.2.52.dev7755 → vis_agent-0.2.53}/contracts/fixtures/activity-tables.json +0 -0
  25. {vis_agent-0.2.52.dev7755 → vis_agent-0.2.53}/contracts/fixtures/activity.json +0 -0
  26. {vis_agent-0.2.52.dev7755 → vis_agent-0.2.53}/contracts/fixtures/diff.json +0 -0
  27. {vis_agent-0.2.52.dev7755 → vis_agent-0.2.53}/contracts/fixtures/live-primitives.json +0 -0
  28. {vis_agent-0.2.52.dev7755 → vis_agent-0.2.53}/contracts/fixtures/session-health.json +0 -0
  29. {vis_agent-0.2.52.dev7755 → vis_agent-0.2.53}/contracts/fixtures/view.json +0 -0
  30. {vis_agent-0.2.52.dev7755 → vis_agent-0.2.53}/contracts/schema/activity.json +0 -0
  31. {vis_agent-0.2.52.dev7755 → vis_agent-0.2.53}/contracts/schema/automations.json +0 -0
  32. {vis_agent-0.2.52.dev7755 → vis_agent-0.2.53}/contracts/schema/common.json +0 -0
  33. {vis_agent-0.2.52.dev7755 → vis_agent-0.2.53}/contracts/schema/config.json +0 -0
  34. {vis_agent-0.2.52.dev7755 → vis_agent-0.2.53}/contracts/schema/content.json +0 -0
  35. {vis_agent-0.2.52.dev7755 → vis_agent-0.2.53}/contracts/schema/council.json +0 -0
  36. {vis_agent-0.2.52.dev7755 → vis_agent-0.2.53}/contracts/schema/diff.json +0 -0
  37. {vis_agent-0.2.52.dev7755 → vis_agent-0.2.53}/contracts/schema/evals.json +0 -0
  38. {vis_agent-0.2.52.dev7755 → vis_agent-0.2.53}/contracts/schema/improve.json +0 -0
  39. {vis_agent-0.2.52.dev7755 → vis_agent-0.2.53}/contracts/schema/provider.json +0 -0
  40. {vis_agent-0.2.52.dev7755 → vis_agent-0.2.53}/contracts/schema/rooms.json +0 -0
  41. {vis_agent-0.2.52.dev7755 → vis_agent-0.2.53}/contracts/schema/symbol.json +0 -0
  42. {vis_agent-0.2.52.dev7755 → vis_agent-0.2.53}/contracts/schema/toggle.json +0 -0
  43. {vis_agent-0.2.52.dev7755 → vis_agent-0.2.53}/contracts/schema/view.json +0 -0
  44. {vis_agent-0.2.52.dev7755 → vis_agent-0.2.53}/examples/greeter/tests/test_greeter.py +0 -0
  45. {vis_agent-0.2.52.dev7755 → vis_agent-0.2.53}/hatch_build.py +0 -0
  46. {vis_agent-0.2.52.dev7755 → vis_agent-0.2.53}/src/blockether/vis/__init__.py +0 -0
  47. {vis_agent-0.2.52.dev7755 → vis_agent-0.2.53}/src/blockether/vis/_contracts.py +0 -0
  48. {vis_agent-0.2.52.dev7755 → vis_agent-0.2.53}/src/blockether/vis/_outside.py +0 -0
  49. {vis_agent-0.2.52.dev7755 → vis_agent-0.2.53}/src/blockether/vis/_wire.py +0 -0
  50. {vis_agent-0.2.52.dev7755 → vis_agent-0.2.53}/src/blockether/vis/activity.py +0 -0
  51. {vis_agent-0.2.52.dev7755 → vis_agent-0.2.53}/src/blockether/vis/engine/__init__.py +0 -0
  52. {vis_agent-0.2.52.dev7755 → vis_agent-0.2.53}/src/blockether/vis/engine/_agent.py +0 -0
  53. {vis_agent-0.2.52.dev7755 → vis_agent-0.2.53}/src/blockether/vis/engine/_client.py +0 -0
  54. {vis_agent-0.2.52.dev7755 → vis_agent-0.2.53}/src/blockether/vis/engine/_install.py +0 -0
  55. {vis_agent-0.2.52.dev7755 → vis_agent-0.2.53}/src/blockether/vis/engine/_local.py +0 -0
  56. {vis_agent-0.2.52.dev7755 → vis_agent-0.2.53}/src/blockether/vis/evals/__init__.py +0 -0
  57. {vis_agent-0.2.52.dev7755 → vis_agent-0.2.53}/src/blockether/vis/evals/_gateway.py +0 -0
  58. {vis_agent-0.2.52.dev7755 → vis_agent-0.2.53}/src/blockether/vis/evals/agent.py +0 -0
  59. {vis_agent-0.2.52.dev7755 → vis_agent-0.2.53}/src/blockether/vis/evals/bench.py +0 -0
  60. {vis_agent-0.2.52.dev7755 → vis_agent-0.2.53}/src/blockether/vis/evals/check.py +0 -0
  61. {vis_agent-0.2.52.dev7755 → vis_agent-0.2.53}/src/blockether/vis/evals/compare.py +0 -0
  62. {vis_agent-0.2.52.dev7755 → vis_agent-0.2.53}/src/blockether/vis/evals/environment.py +0 -0
  63. {vis_agent-0.2.52.dev7755 → vis_agent-0.2.53}/src/blockether/vis/evals/grade.py +0 -0
  64. {vis_agent-0.2.52.dev7755 → vis_agent-0.2.53}/src/blockether/vis/evals/harbor.py +0 -0
  65. {vis_agent-0.2.52.dev7755 → vis_agent-0.2.53}/src/blockether/vis/evals/judge-rubric.toml +0 -0
  66. {vis_agent-0.2.52.dev7755 → vis_agent-0.2.53}/src/blockether/vis/evals/judge.py +0 -0
  67. {vis_agent-0.2.52.dev7755 → vis_agent-0.2.53}/src/blockether/vis/evals/labels.py +0 -0
  68. {vis_agent-0.2.52.dev7755 → vis_agent-0.2.53}/src/blockether/vis/evals/leaderboard.py +0 -0
  69. {vis_agent-0.2.52.dev7755 → vis_agent-0.2.53}/src/blockether/vis/evals/repeats.py +0 -0
  70. {vis_agent-0.2.52.dev7755 → vis_agent-0.2.53}/src/blockether/vis/evals/rounds.py +0 -0
  71. {vis_agent-0.2.52.dev7755 → vis_agent-0.2.53}/src/blockether/vis/evals/stages.py +0 -0
  72. {vis_agent-0.2.52.dev7755 → vis_agent-0.2.53}/src/blockether/vis/evals/stats.py +0 -0
  73. {vis_agent-0.2.52.dev7755 → vis_agent-0.2.53}/src/blockether/vis/evals/trajectory.py +0 -0
  74. {vis_agent-0.2.52.dev7755 → vis_agent-0.2.53}/src/blockether/vis/extension_package.py +0 -0
  75. {vis_agent-0.2.52.dev7755 → vis_agent-0.2.53}/src/blockether/vis/py.typed +0 -0
  76. {vis_agent-0.2.52.dev7755 → vis_agent-0.2.53}/src/blockether/vis/rooms.py +0 -0
  77. {vis_agent-0.2.52.dev7755 → vis_agent-0.2.53}/src/blockether/vis/views.py +0 -0
  78. {vis_agent-0.2.52.dev7755 → vis_agent-0.2.53}/tests/conftest.py +0 -0
  79. {vis_agent-0.2.52.dev7755 → vis_agent-0.2.53}/tests/evals_atif.py +0 -0
  80. {vis_agent-0.2.52.dev7755 → vis_agent-0.2.53}/tests/test_activity.py +0 -0
  81. {vis_agent-0.2.52.dev7755 → vis_agent-0.2.53}/tests/test_agent.py +0 -0
  82. {vis_agent-0.2.52.dev7755 → vis_agent-0.2.53}/tests/test_agent_extension_lifecycle.py +0 -0
  83. {vis_agent-0.2.52.dev7755 → vis_agent-0.2.53}/tests/test_agent_extensions.py +0 -0
  84. {vis_agent-0.2.52.dev7755 → vis_agent-0.2.53}/tests/test_api_guides.py +0 -0
  85. {vis_agent-0.2.52.dev7755 → vis_agent-0.2.53}/tests/test_attachments.py +0 -0
  86. {vis_agent-0.2.52.dev7755 → vis_agent-0.2.53}/tests/test_authoring.py +0 -0
  87. {vis_agent-0.2.52.dev7755 → vis_agent-0.2.53}/tests/test_catalog.py +0 -0
  88. {vis_agent-0.2.52.dev7755 → vis_agent-0.2.53}/tests/test_catalog_recipe.py +0 -0
  89. {vis_agent-0.2.52.dev7755 → vis_agent-0.2.53}/tests/test_ci_report_recipe.py +0 -0
  90. {vis_agent-0.2.52.dev7755 → vis_agent-0.2.53}/tests/test_client_extensions_engine.py +0 -0
  91. {vis_agent-0.2.52.dev7755 → vis_agent-0.2.53}/tests/test_code_quality_recipe.py +0 -0
  92. {vis_agent-0.2.52.dev7755 → vis_agent-0.2.53}/tests/test_contracts.py +0 -0
  93. {vis_agent-0.2.52.dev7755 → vis_agent-0.2.53}/tests/test_council.py +0 -0
  94. {vis_agent-0.2.52.dev7755 → vis_agent-0.2.53}/tests/test_engine.py +0 -0
  95. {vis_agent-0.2.52.dev7755 → vis_agent-0.2.53}/tests/test_engine_docstrings.py +0 -0
  96. {vis_agent-0.2.52.dev7755 → vis_agent-0.2.53}/tests/test_evals_agent.py +0 -0
  97. {vis_agent-0.2.52.dev7755 → vis_agent-0.2.53}/tests/test_evals_bench.py +0 -0
  98. {vis_agent-0.2.52.dev7755 → vis_agent-0.2.53}/tests/test_evals_check.py +0 -0
  99. {vis_agent-0.2.52.dev7755 → vis_agent-0.2.53}/tests/test_evals_compare.py +0 -0
  100. {vis_agent-0.2.52.dev7755 → vis_agent-0.2.53}/tests/test_evals_environment.py +0 -0
  101. {vis_agent-0.2.52.dev7755 → vis_agent-0.2.53}/tests/test_evals_gateway.py +0 -0
  102. {vis_agent-0.2.52.dev7755 → vis_agent-0.2.53}/tests/test_evals_grade.py +0 -0
  103. {vis_agent-0.2.52.dev7755 → vis_agent-0.2.53}/tests/test_evals_guards.py +0 -0
  104. {vis_agent-0.2.52.dev7755 → vis_agent-0.2.53}/tests/test_evals_harbor.py +0 -0
  105. {vis_agent-0.2.52.dev7755 → vis_agent-0.2.53}/tests/test_evals_judge.py +0 -0
  106. {vis_agent-0.2.52.dev7755 → vis_agent-0.2.53}/tests/test_evals_labels.py +0 -0
  107. {vis_agent-0.2.52.dev7755 → vis_agent-0.2.53}/tests/test_evals_repeats.py +0 -0
  108. {vis_agent-0.2.52.dev7755 → vis_agent-0.2.53}/tests/test_evals_rounds.py +0 -0
  109. {vis_agent-0.2.52.dev7755 → vis_agent-0.2.53}/tests/test_evals_stages.py +0 -0
  110. {vis_agent-0.2.52.dev7755 → vis_agent-0.2.53}/tests/test_evals_stats.py +0 -0
  111. {vis_agent-0.2.52.dev7755 → vis_agent-0.2.53}/tests/test_evals_tasks.py +0 -0
  112. {vis_agent-0.2.52.dev7755 → vis_agent-0.2.53}/tests/test_evals_trajectory.py +0 -0
  113. {vis_agent-0.2.52.dev7755 → vis_agent-0.2.53}/tests/test_extension_package.py +0 -0
  114. {vis_agent-0.2.52.dev7755 → vis_agent-0.2.53}/tests/test_extension_reference.py +0 -0
  115. {vis_agent-0.2.52.dev7755 → vis_agent-0.2.53}/tests/test_gateway_performance.py +0 -0
  116. {vis_agent-0.2.52.dev7755 → vis_agent-0.2.53}/tests/test_goals.py +0 -0
  117. {vis_agent-0.2.52.dev7755 → vis_agent-0.2.53}/tests/test_install.py +0 -0
  118. {vis_agent-0.2.52.dev7755 → vis_agent-0.2.53}/tests/test_live_primitives.py +0 -0
  119. {vis_agent-0.2.52.dev7755 → vis_agent-0.2.53}/tests/test_live_wait.py +0 -0
  120. {vis_agent-0.2.52.dev7755 → vis_agent-0.2.53}/tests/test_local.py +0 -0
  121. {vis_agent-0.2.52.dev7755 → vis_agent-0.2.53}/tests/test_method_typing.py +0 -0
  122. {vis_agent-0.2.52.dev7755 → vis_agent-0.2.53}/tests/test_monitor_recipe.py +0 -0
  123. {vis_agent-0.2.52.dev7755 → vis_agent-0.2.53}/tests/test_namespace.py +0 -0
  124. {vis_agent-0.2.52.dev7755 → vis_agent-0.2.53}/tests/test_outside.py +0 -0
  125. {vis_agent-0.2.52.dev7755 → vis_agent-0.2.53}/tests/test_providers.py +0 -0
  126. {vis_agent-0.2.52.dev7755 → vis_agent-0.2.53}/tests/test_read_session.py +0 -0
  127. {vis_agent-0.2.52.dev7755 → vis_agent-0.2.53}/tests/test_record_arguments.py +0 -0
  128. {vis_agent-0.2.52.dev7755 → vis_agent-0.2.53}/tests/test_registration.py +0 -0
  129. {vis_agent-0.2.52.dev7755 → vis_agent-0.2.53}/tests/test_repair_hooks.py +0 -0
  130. {vis_agent-0.2.52.dev7755 → vis_agent-0.2.53}/tests/test_result_sequence.py +0 -0
  131. {vis_agent-0.2.52.dev7755 → vis_agent-0.2.53}/tests/test_rooms.py +0 -0
  132. {vis_agent-0.2.52.dev7755 → vis_agent-0.2.53}/tests/test_scoped_settings.py +0 -0
  133. {vis_agent-0.2.52.dev7755 → vis_agent-0.2.53}/tests/test_sdk_bootstrap.py +0 -0
  134. {vis_agent-0.2.52.dev7755 → vis_agent-0.2.53}/tests/test_sdk_guide.py +0 -0
  135. {vis_agent-0.2.52.dev7755 → vis_agent-0.2.53}/tests/test_sdk_typing.py +0 -0
  136. {vis_agent-0.2.52.dev7755 → vis_agent-0.2.53}/tests/test_structured_live.py +0 -0
  137. {vis_agent-0.2.52.dev7755 → vis_agent-0.2.53}/tests/test_symbol_contract.py +0 -0
  138. {vis_agent-0.2.52.dev7755 → vis_agent-0.2.53}/tests/test_testing.py +0 -0
  139. {vis_agent-0.2.52.dev7755 → vis_agent-0.2.53}/tests/test_text_list_arguments.py +0 -0
  140. {vis_agent-0.2.52.dev7755 → vis_agent-0.2.53}/tests/test_tools.py +0 -0
  141. {vis_agent-0.2.52.dev7755 → vis_agent-0.2.53}/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.52.dev7755
3
+ Version: 0.2.53
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
@@ -38,16 +38,13 @@
38
38
  "maxItems": 64,
39
39
  "uniqueItems": true
40
40
  },
41
+ "files": {
42
+ "$ref": "#/$defs/files"
43
+ },
41
44
  "key": {
42
45
  "type": "string",
43
46
  "minLength": 1,
44
47
  "maxLength": 256
45
- },
46
- "context": {
47
- "enum": [
48
- "full",
49
- "task"
50
- ]
51
48
  }
52
49
  },
53
50
  "required": [
@@ -119,6 +116,43 @@
119
116
  ],
120
117
  "additionalProperties": false
121
118
  },
119
+ "grant": {
120
+ "$comment": "Add file rules to the grant of a subagent. A child without write rules writes only the granted rules after a write grant.",
121
+ "type": "object",
122
+ "properties": {
123
+ "session_id": {
124
+ "type": "string",
125
+ "minLength": 1
126
+ },
127
+ "files": {
128
+ "$ref": "#/$defs/files"
129
+ }
130
+ },
131
+ "required": [
132
+ "session_id",
133
+ "files"
134
+ ],
135
+ "additionalProperties": false
136
+ },
137
+ "ask_files": {
138
+ "type": "object",
139
+ "properties": {
140
+ "files": {
141
+ "$ref": "#/$defs/files"
142
+ },
143
+ "reason": {
144
+ "type": "string",
145
+ "minLength": 1,
146
+ "maxLength": 2000,
147
+ "pattern": "\\S"
148
+ }
149
+ },
150
+ "required": [
151
+ "files",
152
+ "reason"
153
+ ],
154
+ "additionalProperties": false
155
+ },
122
156
  "resume": {
123
157
  "type": "object",
124
158
  "properties": {
@@ -176,6 +210,35 @@
176
210
  ],
177
211
  "additionalProperties": false
178
212
  },
213
+ "files": {
214
+ "$comment": "A subagent file grant. Each rule is a path or a glob, absolute, ~ or relative to the parent's workspace root. Rules also cover files that do not exist yet.",
215
+ "type": "object",
216
+ "properties": {
217
+ "write": {
218
+ "$ref": "#/$defs/file_rules"
219
+ },
220
+ "read_only": {
221
+ "$ref": "#/$defs/file_rules"
222
+ },
223
+ "deny": {
224
+ "$ref": "#/$defs/file_rules"
225
+ }
226
+ },
227
+ "minProperties": 1,
228
+ "additionalProperties": false
229
+ },
230
+ "file_rules": {
231
+ "type": "array",
232
+ "items": {
233
+ "type": "string",
234
+ "minLength": 1,
235
+ "maxLength": 4096,
236
+ "pattern": "\\S"
237
+ },
238
+ "minItems": 1,
239
+ "maxItems": 64,
240
+ "uniqueItems": true
241
+ },
179
242
  "agent": {
180
243
  "type": "object",
181
244
  "properties": {
@@ -209,11 +272,6 @@
209
272
  "budget_limited"
210
273
  ]
211
274
  },
212
- "depth": {
213
- "type": "integer",
214
- "minimum": 1,
215
- "maximum": 2
216
- },
217
275
  "iteration_budget": {
218
276
  "$ref": "#/$defs/spawn/properties/iteration_budget"
219
277
  },
@@ -248,6 +306,9 @@
248
306
  "object",
249
307
  "null"
250
308
  ]
309
+ },
310
+ "files": {
311
+ "$ref": "#/$defs/files"
251
312
  }
252
313
  },
253
314
  "required": [
@@ -257,7 +318,6 @@
257
318
  "team_id",
258
319
  "task",
259
320
  "status",
260
- "depth",
261
321
  "iteration_budget",
262
322
  "iterations_used"
263
323
  ],
@@ -2370,6 +2370,9 @@
2370
2370
  {
2371
2371
  "const": "context.updated"
2372
2372
  },
2373
+ {
2374
+ "const": "extensions.reloaded"
2375
+ },
2373
2376
  {
2374
2377
  "const": "iteration.completed",
2375
2378
  "x-vis-turn-stream": true
@@ -2979,13 +2982,18 @@
2979
2982
  "required": [
2980
2983
  "loaded",
2981
2984
  "failed",
2982
- "scopes"
2985
+ "scopes",
2986
+ "reload_id"
2983
2987
  ],
2984
2988
  "properties": {
2985
2989
  "loaded": {
2986
2990
  "type": "integer",
2987
2991
  "minimum": 0
2988
2992
  },
2993
+ "reload_id": {
2994
+ "type": "string",
2995
+ "minLength": 1
2996
+ },
2989
2997
  "failed": {
2990
2998
  "type": "integer",
2991
2999
  "minimum": 0
@@ -8,7 +8,7 @@ build-backend = "hatchling.build"
8
8
 
9
9
  [project]
10
10
  name = "vis-agent"
11
- version = "0.2.52.dev7755"
11
+ version = "0.2.53"
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"
@@ -16,7 +16,6 @@ class Subagent:
16
16
  team_id: str
17
17
  task: str
18
18
  status: str
19
- depth: int
20
19
  iteration_budget: int
21
20
  iterations_used: int
22
21
  provider: str | None = None
@@ -24,6 +23,7 @@ class Subagent:
24
23
  routing_locked: bool = False
25
24
  pending_input: bool = False
26
25
  usage: dict[str, Any] | None = None
26
+ files: dict[str, list[str]] | None = None
27
27
 
28
28
  @classmethod
29
29
  def from_wire(cls, value: dict[str, Any]) -> Subagent:
@@ -235,6 +235,7 @@ class Council:
235
235
  model: str | None = None,
236
236
  iteration_budget: int | None = None,
237
237
  allowed_models: list[dict[str, str]] | None = None,
238
+ files: dict[str, list[str]] | None = None,
238
239
  key: str | None = None,
239
240
  ) -> Subagent:
240
241
  """Create a subagent and publish its delegated task through Council.
@@ -244,7 +245,8 @@ class Council:
244
245
  share the checkout, inherit authorization and use their own bounded model
245
246
  iterations. A key makes retries idempotent within the parent turn, and reusing
246
247
  it with different input is rejected. This is not an ordinary independent fork,
247
- and it does not grant broader permissions.
248
+ and it does not grant broader permissions. ``files`` narrows file access with
249
+ ``write``, ``read_only`` and ``deny`` rules: paths or globs, also for future files.
248
250
  """
249
251
  body = {"task": task}
250
252
  body.update(
@@ -255,6 +257,7 @@ class Council:
255
257
  "iteration_budget": iteration_budget,
256
258
  "model": model,
257
259
  "allowed_models": allowed_models,
260
+ "files": files,
258
261
  "key": key,
259
262
  }.items()
260
263
  if value is not None
@@ -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
@@ -13,7 +13,6 @@ CHILD = {
13
13
  "team_id": "task",
14
14
  "task": "Check the contract",
15
15
  "status": "running",
16
- "depth": 1,
17
16
  "iteration_budget": 4,
18
17
  "iterations_used": 1,
19
18
  "provider": "p",
@@ -63,6 +62,7 @@ def test_managed_team_transport():
63
62
  model="small",
64
63
  iteration_budget=4,
65
64
  allowed_models=[{"provider": "p", "model": "small"}],
65
+ files={"write": ["src/owner/**"]},
66
66
  key="contract",
67
67
  )
68
68
  assert isinstance(child, Subagent)
@@ -86,6 +86,7 @@ def test_managed_team_transport():
86
86
  "provider": "p",
87
87
  "model": "small",
88
88
  "allowed_models": [{"provider": "p", "model": "small"}],
89
+ "files": {"write": ["src/owner/**"]},
89
90
  "key": "contract",
90
91
  },
91
92
  ),
@@ -167,6 +168,9 @@ def test_group_members_are_not_the_managed_team():
167
168
  {"task": "x", "iteration_budget": 601},
168
169
  {"task": "x", "model": "small"},
169
170
  {"task": "x", "allowed_models": []},
171
+ {"task": "x", "files": {}},
172
+ {"task": "x", "files": {"exec": ["a"]}},
173
+ {"task": "x", "files": {"write": []}},
170
174
  ],
171
175
  )
172
176
  def test_spawn_rejects_invalid_input_before_http(opts):
@@ -293,7 +297,6 @@ def test_all_lifecycle_states_and_optional_response_defaults(status):
293
297
  "leader_id",
294
298
  "team_id",
295
299
  "task",
296
- "depth",
297
300
  "iteration_budget",
298
301
  "iterations_used",
299
302
  )
@@ -634,7 +634,6 @@ def test_dedicated_methods_cover_every_public_nonstreaming_operation():
634
634
  "team_id": "task",
635
635
  "task": "Check the contract",
636
636
  "status": "running",
637
- "depth": 1,
638
637
  "iteration_budget": 4,
639
638
  "iterations_used": 1,
640
639
  "provider": "fixture",
@@ -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-030ba6505ce00caad546fbff1bfdfe01b71c641d
File without changes
File without changes