vis-agent 0.3.4.dev7992__tar.gz → 0.3.4.dev8033__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 (156) hide show
  1. {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/.gitignore +3 -0
  2. {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/PKG-INFO +1 -1
  3. {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/contracts/schema/agents.json +88 -0
  4. {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/contracts/schema/config.json +29 -0
  5. {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/contracts/schema/gateway.json +25 -1
  6. {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/contracts/schema/toggle.json +67 -2
  7. {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/pyproject.toml +1 -1
  8. vis_agent-0.3.4.dev8033/release-tag +1 -0
  9. {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/src/blockether/vis/engine/_agents.py +6 -0
  10. {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/src/blockether/vis/engine/_council.py +11 -2
  11. vis_agent-0.3.4.dev8033/src/blockether/vis/evals/mrcr.py +273 -0
  12. {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/src/blockether/vis/extension.py +12 -2
  13. {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/tests/test_agents.py +7 -2
  14. {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/tests/test_client.py +1 -0
  15. {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/tests/test_contracts.py +3 -3
  16. vis_agent-0.3.4.dev8033/tests/test_evals_mrcr.py +101 -0
  17. {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/tests/test_scoped_settings.py +32 -0
  18. vis_agent-0.3.4.dev7992/release-tag +0 -1
  19. {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/LICENSE +0 -0
  20. {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/README.md +0 -0
  21. {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/contracts/fixtures/activity-arguments.json +0 -0
  22. {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/contracts/fixtures/activity-cases.json +0 -0
  23. {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/contracts/fixtures/activity-copy.json +0 -0
  24. {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/contracts/fixtures/activity-execution.json +0 -0
  25. {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/contracts/fixtures/activity-groups.json +0 -0
  26. {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/contracts/fixtures/activity-read-session.json +0 -0
  27. {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/contracts/fixtures/activity-reads.json +0 -0
  28. {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/contracts/fixtures/activity-results.json +0 -0
  29. {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/contracts/fixtures/activity-stream.json +0 -0
  30. {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/contracts/fixtures/activity-tables.json +0 -0
  31. {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/contracts/fixtures/activity.json +0 -0
  32. {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/contracts/fixtures/diff.json +0 -0
  33. {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/contracts/fixtures/live-primitives.json +0 -0
  34. {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/contracts/fixtures/session-health.json +0 -0
  35. {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/contracts/fixtures/view.json +0 -0
  36. {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/contracts/schema/activity.json +0 -0
  37. {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/contracts/schema/automations.json +0 -0
  38. {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/contracts/schema/common.json +0 -0
  39. {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/contracts/schema/content.json +0 -0
  40. {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/contracts/schema/council.json +0 -0
  41. {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/contracts/schema/diff.json +0 -0
  42. {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/contracts/schema/evals.json +0 -0
  43. {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/contracts/schema/improve.json +0 -0
  44. {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/contracts/schema/provider.json +0 -0
  45. {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/contracts/schema/rooms.json +0 -0
  46. {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/contracts/schema/symbol.json +0 -0
  47. {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/contracts/schema/view.json +0 -0
  48. {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/examples/greeter/tests/test_greeter.py +0 -0
  49. {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/hatch_build.py +0 -0
  50. {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/src/blockether/vis/__init__.py +0 -0
  51. {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/src/blockether/vis/_contracts.py +0 -0
  52. {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/src/blockether/vis/_outside.py +0 -0
  53. {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/src/blockether/vis/_wire.py +0 -0
  54. {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/src/blockether/vis/activity.py +0 -0
  55. {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/src/blockether/vis/engine/__init__.py +0 -0
  56. {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/src/blockether/vis/engine/_agent.py +0 -0
  57. {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/src/blockether/vis/engine/_client.py +0 -0
  58. {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/src/blockether/vis/engine/_extensions.py +0 -0
  59. {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/src/blockether/vis/engine/_host.py +0 -0
  60. {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/src/blockether/vis/engine/_install.py +0 -0
  61. {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/src/blockether/vis/engine/_local.py +0 -0
  62. {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/src/blockether/vis/engine/_models.py +0 -0
  63. {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/src/blockether/vis/evals/__init__.py +0 -0
  64. {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/src/blockether/vis/evals/_gateway.py +0 -0
  65. {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/src/blockether/vis/evals/agent.py +0 -0
  66. {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/src/blockether/vis/evals/bench.py +0 -0
  67. {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/src/blockether/vis/evals/check.py +0 -0
  68. {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/src/blockether/vis/evals/compare.py +0 -0
  69. {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/src/blockether/vis/evals/environment.py +0 -0
  70. {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/src/blockether/vis/evals/grade.py +0 -0
  71. {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/src/blockether/vis/evals/harbor.py +0 -0
  72. {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/src/blockether/vis/evals/judge-rubric.toml +0 -0
  73. {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/src/blockether/vis/evals/judge.py +0 -0
  74. {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/src/blockether/vis/evals/labels.py +0 -0
  75. {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/src/blockether/vis/evals/leaderboard.py +0 -0
  76. {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/src/blockether/vis/evals/lic.py +0 -0
  77. {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/src/blockether/vis/evals/longmemeval.py +0 -0
  78. {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/src/blockether/vis/evals/repeats.py +0 -0
  79. {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/src/blockether/vis/evals/report.py +0 -0
  80. {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/src/blockether/vis/evals/reports.py +0 -0
  81. {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/src/blockether/vis/evals/rounds.py +0 -0
  82. {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/src/blockether/vis/evals/stages.py +0 -0
  83. {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/src/blockether/vis/evals/stats.py +0 -0
  84. {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/src/blockether/vis/evals/trajectory.py +0 -0
  85. {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/src/blockether/vis/extension_package.py +0 -0
  86. {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/src/blockether/vis/py.typed +0 -0
  87. {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/src/blockether/vis/rooms.py +0 -0
  88. {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/src/blockether/vis/views.py +0 -0
  89. {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/tests/conftest.py +0 -0
  90. {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/tests/evals_atif.py +0 -0
  91. {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/tests/test_activity.py +0 -0
  92. {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/tests/test_agent.py +0 -0
  93. {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/tests/test_agent_extension_lifecycle.py +0 -0
  94. {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/tests/test_agent_extensions.py +0 -0
  95. {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/tests/test_api_guides.py +0 -0
  96. {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/tests/test_attachments.py +0 -0
  97. {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/tests/test_authoring.py +0 -0
  98. {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/tests/test_catalog.py +0 -0
  99. {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/tests/test_catalog_recipe.py +0 -0
  100. {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/tests/test_ci_report_recipe.py +0 -0
  101. {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/tests/test_client_extensions_engine.py +0 -0
  102. {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/tests/test_code_quality_recipe.py +0 -0
  103. {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/tests/test_council.py +0 -0
  104. {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/tests/test_declarations.py +0 -0
  105. {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/tests/test_engine.py +0 -0
  106. {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/tests/test_engine_docstrings.py +0 -0
  107. {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/tests/test_evals_agent.py +0 -0
  108. {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/tests/test_evals_bench.py +0 -0
  109. {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/tests/test_evals_check.py +0 -0
  110. {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/tests/test_evals_compare.py +0 -0
  111. {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/tests/test_evals_environment.py +0 -0
  112. {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/tests/test_evals_gateway.py +0 -0
  113. {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/tests/test_evals_grade.py +0 -0
  114. {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/tests/test_evals_guards.py +0 -0
  115. {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/tests/test_evals_harbor.py +0 -0
  116. {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/tests/test_evals_judge.py +0 -0
  117. {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/tests/test_evals_labels.py +0 -0
  118. {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/tests/test_evals_lic.py +0 -0
  119. {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/tests/test_evals_longmemeval.py +0 -0
  120. {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/tests/test_evals_repeats.py +0 -0
  121. {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/tests/test_evals_report.py +0 -0
  122. {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/tests/test_evals_rounds.py +0 -0
  123. {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/tests/test_evals_stages.py +0 -0
  124. {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/tests/test_evals_stats.py +0 -0
  125. {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/tests/test_evals_tasks.py +0 -0
  126. {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/tests/test_evals_trajectory.py +0 -0
  127. {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/tests/test_extension_package.py +0 -0
  128. {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/tests/test_extension_reference.py +0 -0
  129. {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/tests/test_gateway_performance.py +0 -0
  130. {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/tests/test_goals.py +0 -0
  131. {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/tests/test_install.py +0 -0
  132. {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/tests/test_live_primitives.py +0 -0
  133. {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/tests/test_live_wait.py +0 -0
  134. {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/tests/test_local.py +0 -0
  135. {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/tests/test_method_typing.py +0 -0
  136. {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/tests/test_models.py +0 -0
  137. {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/tests/test_monitor_recipe.py +0 -0
  138. {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/tests/test_namespace.py +0 -0
  139. {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/tests/test_outside.py +0 -0
  140. {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/tests/test_providers.py +0 -0
  141. {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/tests/test_read_session.py +0 -0
  142. {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/tests/test_record_arguments.py +0 -0
  143. {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/tests/test_registration.py +0 -0
  144. {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/tests/test_repair_hooks.py +0 -0
  145. {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/tests/test_result_sequence.py +0 -0
  146. {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/tests/test_rooms.py +0 -0
  147. {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/tests/test_sdk_bootstrap.py +0 -0
  148. {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/tests/test_sdk_guide.py +0 -0
  149. {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/tests/test_sdk_typing.py +0 -0
  150. {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/tests/test_shortcuts_live.py +0 -0
  151. {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/tests/test_structured_live.py +0 -0
  152. {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/tests/test_symbol_contract.py +0 -0
  153. {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/tests/test_testing.py +0 -0
  154. {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/tests/test_text_list_arguments.py +0 -0
  155. {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/tests/test_tools.py +0 -0
  156. {vis_agent-0.3.4.dev7992 → vis_agent-0.3.4.dev8033}/tests/test_views.py +0 -0
@@ -117,3 +117,6 @@ own_your_harness.pptx
117
117
  # Call journal written by the e2e schema-probe fixture when it runs from this
118
118
  # checkout instead of a temporary workspace.
119
119
  /schema-calls.jsonl
120
+
121
+ # uv lockfiles. The SDK and the examples are libraries; uv writes the lock locally.
122
+ uv.lock
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: vis-agent
3
- Version: 0.3.4.dev7992
3
+ Version: 0.3.4.dev8033
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
@@ -15,6 +15,9 @@
15
15
  "maxLength": 8192,
16
16
  "pattern": "\\S"
17
17
  },
18
+ "role": {
19
+ "$ref": "#/$defs/role"
20
+ },
18
21
  "model": {
19
22
  "type": "string",
20
23
  "minLength": 1
@@ -77,6 +80,9 @@
77
80
  "task": {
78
81
  "$ref": "#/$defs/spawn/properties/task"
79
82
  },
83
+ "role": {
84
+ "$ref": "#/$defs/spawn/properties/role"
85
+ },
80
86
  "model": {
81
87
  "$ref": "#/$defs/spawn/properties/model"
82
88
  },
@@ -191,6 +197,27 @@
191
197
  ],
192
198
  "additionalProperties": false
193
199
  },
200
+ "answer": {
201
+ "$comment": "A person answers the open question of a subagent for its parent.",
202
+ "type": "object",
203
+ "properties": {
204
+ "session_id": {
205
+ "type": "string",
206
+ "minLength": 1
207
+ },
208
+ "text": {
209
+ "type": "string",
210
+ "minLength": 1,
211
+ "maxLength": 8192,
212
+ "pattern": "\\S"
213
+ }
214
+ },
215
+ "required": [
216
+ "session_id",
217
+ "text"
218
+ ],
219
+ "additionalProperties": false
220
+ },
194
221
  "grant": {
195
222
  "$comment": "Add file rules to the grant of a subagent. A child without write rules already writes where its parent writes, so a write grant changes nothing for it. A write rule under a read_only or deny rule of the child fails.",
196
223
  "type": "object",
@@ -274,6 +301,32 @@
274
301
  ],
275
302
  "additionalProperties": false
276
303
  },
304
+ "role": {
305
+ "description": "Choose the role by the result that you need: facts, changed files, a pass or fail, or findings.",
306
+ "$comment": "A role is a label for people and the leader. It sets no tool, file or model limit. The TUI writes the role name. The Companion draws a Lucide icon for each role.",
307
+ "oneOf": [
308
+ {
309
+ "const": "research",
310
+ "title": "Finds facts",
311
+ "description": "Finds facts. Reads code, docs, logs and history, and answers one question with evidence. Use it before you decide or plan."
312
+ },
313
+ {
314
+ "const": "implement",
315
+ "title": "Changes files",
316
+ "description": "Changes files. Writes code, tests, docs or configuration and runs the checks for its change. Use it for one bounded change."
317
+ },
318
+ {
319
+ "const": "verify",
320
+ "title": "Proves it works",
321
+ "description": "Proves that something works. Runs tests, reproduces a bug or checks the app, and reports pass or fail with evidence."
322
+ },
323
+ {
324
+ "const": "audit",
325
+ "title": "Judges the work",
326
+ "description": "Judges finished work. Reviews a change, plan or release for risks and mistakes, and reports findings. Give it the work, not your conclusion."
327
+ }
328
+ ]
329
+ },
277
330
  "model_pair": {
278
331
  "type": "object",
279
332
  "properties": {
@@ -342,6 +395,9 @@
342
395
  "type": "string",
343
396
  "minLength": 1
344
397
  },
398
+ "role": {
399
+ "$ref": "#/$defs/role"
400
+ },
345
401
  "status": {
346
402
  "enum": [
347
403
  "queued",
@@ -385,6 +441,38 @@
385
441
  "type": "string",
386
442
  "minLength": 1
387
443
  },
444
+ "question_entry_id": {
445
+ "type": "integer",
446
+ "minimum": 1
447
+ },
448
+ "budget_request": {
449
+ "type": "object",
450
+ "properties": {
451
+ "iterations": {
452
+ "type": "integer",
453
+ "minimum": 1
454
+ },
455
+ "reason": {
456
+ "type": "string"
457
+ }
458
+ }
459
+ },
460
+ "files_request": {
461
+ "type": "object",
462
+ "properties": {
463
+ "files": {
464
+ "$ref": "#/$defs/files"
465
+ },
466
+ "reason": {
467
+ "type": "string"
468
+ }
469
+ }
470
+ },
471
+ "activity": {
472
+ "$comment": "The latest Activity line of a queued or running agent, such as Patched ~/src/a.clj.",
473
+ "type": "string",
474
+ "minLength": 1
475
+ },
388
476
  "usage": {
389
477
  "type": [
390
478
  "object",
@@ -694,6 +694,32 @@
694
694
  },
695
695
  "additionalProperties": false
696
696
  },
697
+ "toolList": {
698
+ "type": "array",
699
+ "maxItems": 256,
700
+ "uniqueItems": true,
701
+ "items": {
702
+ "type": "string",
703
+ "pattern": "^[A-Za-z_*][A-Za-z0-9_.*]*$",
704
+ "maxLength": 200
705
+ },
706
+ "description": "Tool names as python_execution binds them, such as memo.recall. * matches any characters."
707
+ },
708
+ "tools": {
709
+ "type": "object",
710
+ "properties": {
711
+ "allow": {
712
+ "$ref": "#/$defs/toolList",
713
+ "description": "Keep only these tools of each extension that the list names. Other tools of that extension, also later ones, are off."
714
+ },
715
+ "deny": {
716
+ "$ref": "#/$defs/toolList",
717
+ "description": "Turn these tools off."
718
+ }
719
+ },
720
+ "additionalProperties": false,
721
+ "description": "Tools of optional extensions that a session can call. Every scope applies: a narrower scope can turn tools off, never on."
722
+ },
697
723
  "jail": {
698
724
  "type": "object",
699
725
  "properties": {
@@ -1328,6 +1354,9 @@
1328
1354
  "jail": {
1329
1355
  "$ref": "#/$defs/jail"
1330
1356
  },
1357
+ "tools": {
1358
+ "$ref": "#/$defs/tools"
1359
+ },
1331
1360
  "environment": {
1332
1361
  "$ref": "#/$defs/environment"
1333
1362
  },
@@ -736,6 +736,17 @@
736
736
  },
737
737
  "path": "/v1/sessions/:sid/agents"
738
738
  },
739
+ {
740
+ "audience": "sdk",
741
+ "operations": {
742
+ "post": {
743
+ "request": "json",
744
+ "response": "json",
745
+ "extension": "background"
746
+ }
747
+ },
748
+ "path": "/v1/sessions/:sid/agents/answer"
749
+ },
739
750
  {
740
751
  "audience": "sdk",
741
752
  "operations": {
@@ -3000,14 +3011,27 @@
3000
3011
  "switch",
3001
3012
  "select",
3002
3013
  "text",
3014
+ "secret",
3003
3015
  "multiline",
3004
3016
  "number",
3005
3017
  "paths",
3006
3018
  "filesystem",
3007
3019
  "network",
3008
- "list"
3020
+ "list",
3021
+ "tools"
3009
3022
  ]
3010
3023
  },
3024
+ "is_set": {
3025
+ "type": "boolean",
3026
+ "description": "Whether a secret row has a saved value. A secret row never carries the value itself."
3027
+ },
3028
+ "suggestions": {
3029
+ "type": "array",
3030
+ "description": "Values that an editor offers as completions. Other values can still be valid.",
3031
+ "items": {
3032
+ "type": "string"
3033
+ }
3034
+ },
3011
3035
  "schema": {
3012
3036
  "type": "string"
3013
3037
  },
@@ -2,7 +2,7 @@
2
2
  "$schema": "https://json-schema.org/draft/2020-12/schema",
3
3
  "$id": "https://contract.blockether.com/vis/schema/toggle.json",
4
4
  "title": "Feature-toggle contract",
5
- "description": "Feature-toggle contributions and the boolean tokens accepted by settings.",
5
+ "description": "Feature-toggle contributions, and the boolean tokens and string values that settings accept.",
6
6
  "$defs": {
7
7
  "boolean_true": {
8
8
  "type": "string",
@@ -32,6 +32,12 @@
32
32
  }
33
33
  ]
34
34
  },
35
+ "string_value": {
36
+ "type": "string",
37
+ "maxLength": 4096,
38
+ "pattern": "^[^\\u0000-\\u001f\\u007f]*$",
39
+ "description": "The value of a string setting: one line without control characters."
40
+ },
35
41
  "contribution": {
36
42
  "type": "object",
37
43
  "additionalProperties": true,
@@ -83,10 +89,15 @@
83
89
  "type": {
84
90
  "enum": [
85
91
  "boolean",
86
- "enum"
92
+ "enum",
93
+ "string"
87
94
  ],
88
95
  "default": "boolean"
89
96
  },
97
+ "is_secret": {
98
+ "type": "boolean",
99
+ "description": "Makes a string setting write-only. Settings show only whether it is set, never its value."
100
+ },
90
101
  "choices": {
91
102
  "type": "array",
92
103
  "items": {},
@@ -169,6 +180,60 @@
169
180
  }
170
181
  ]
171
182
  }
183
+ },
184
+ {
185
+ "if": {
186
+ "properties": {
187
+ "type": {
188
+ "const": "string"
189
+ }
190
+ },
191
+ "required": [
192
+ "type"
193
+ ]
194
+ },
195
+ "then": {
196
+ "properties": {
197
+ "default": {
198
+ "$ref": "#/$defs/string_value"
199
+ }
200
+ },
201
+ "not": {
202
+ "required": [
203
+ "choices"
204
+ ]
205
+ }
206
+ }
207
+ },
208
+ {
209
+ "if": {
210
+ "properties": {
211
+ "is_secret": {
212
+ "const": true
213
+ }
214
+ },
215
+ "required": [
216
+ "is_secret"
217
+ ]
218
+ },
219
+ "then": {
220
+ "properties": {
221
+ "type": {
222
+ "const": "string"
223
+ },
224
+ "default": {
225
+ "const": ""
226
+ },
227
+ "scopes": {
228
+ "const": [
229
+ "global"
230
+ ]
231
+ }
232
+ },
233
+ "required": [
234
+ "type"
235
+ ]
236
+ }
172
237
  }
173
238
  ]
174
239
  },
@@ -8,7 +8,7 @@ build-backend = "hatchling.build"
8
8
 
9
9
  [project]
10
10
  name = "vis-agent"
11
- version = "0.3.4.dev7992"
11
+ version = "0.3.4.dev8033"
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-8906d28bc9e78c61bfe556c52e15d3045f046731
@@ -18,10 +18,16 @@ class Subagent:
18
18
  status: str
19
19
  iteration_budget: int
20
20
  iterations_used: int
21
+ role: str | None = None
21
22
  provider: str | None = None
22
23
  model: str | None = None
23
24
  routing_locked: bool = False
24
25
  pending_input: bool = False
26
+ question: str | None = None
27
+ question_entry_id: int | None = None
28
+ budget_request: dict[str, Any] | None = None
29
+ files_request: dict[str, Any] | None = None
30
+ activity: str | None = None
25
31
  usage: dict[str, Any] | None = None
26
32
  files: dict[str, list[str]] | None = None
27
33
 
@@ -231,6 +231,7 @@ class Council:
231
231
  self,
232
232
  task: str,
233
233
  *,
234
+ role: str | None = None,
234
235
  provider: str | None = None,
235
236
  model: str | None = None,
236
237
  iteration_budget: int | None = None,
@@ -247,12 +248,15 @@ class Council:
247
248
  it with different input is rejected. This is not an ordinary independent fork,
248
249
  and it does not grant broader permissions. ``files`` narrows file access with
249
250
  ``write``, ``read_only`` and ``deny`` rules: paths or globs, also for future files.
251
+ ``role`` is ``research``, ``implement``, ``verify`` or ``audit``: a label for people
252
+ that sets no limit.
250
253
  """
251
254
  body = {"task": task}
252
255
  body.update(
253
256
  {
254
257
  name: value
255
258
  for name, value in {
259
+ "role": role,
256
260
  "provider": provider,
257
261
  "iteration_budget": iteration_budget,
258
262
  "model": model,
@@ -291,8 +295,8 @@ class Council:
291
295
  ) -> tuple[SubagentRecord, ...]:
292
296
  """Spawn one subagent for each task, wait for all of them and return their records.
293
297
 
294
- A task is a string, or a dict with ``task`` and optional ``model``, ``provider``,
295
- ``iteration_budget`` and ``files``. The wait ends when every child answers, asks
298
+ A task is a string, or a dict with ``task`` and optional ``role``, ``model``,
299
+ ``provider``, ``iteration_budget`` and ``files``. The wait ends when every child answers, asks
296
300
  or stops, or after ``timeout_secs`` (at most 1800). A running child stays running.
297
301
  """
298
302
  body = {"tasks": tasks, "timeout_secs": timeout_secs}
@@ -352,6 +356,11 @@ class Council:
352
356
  body = {"session_id": session_id, "reason": reason}
353
357
  return self._records("/deny", body, "deny")[0]
354
358
 
359
+ def answer(self, session_id: str, text: str) -> SubagentRecord:
360
+ """Answer the open question of a subagent for its parent; the child continues."""
361
+ body = {"session_id": session_id, "text": text}
362
+ return self._records("/answer", body, "answer")[0]
363
+
355
364
  def grant(self, session_id: str, files: dict[str, list[str]]) -> SubagentRecord:
356
365
  """Add ``write``, ``read_only`` or ``deny`` file rules to the grant of a subagent.
357
366
 
@@ -0,0 +1,273 @@
1
+ """Write OpenAI MRCR conversations as long multi-turn Harbor tasks.
2
+
3
+ [MRCR](https://huggingface.co/datasets/openai/mrcr) (multi-round coreference resolution)
4
+ hides several similar requests in one long conversation, for example two requests for
5
+ a poem about tapirs. The last request asks for one of the earlier answers, for example
6
+ the second poem about tapirs, word for word, with a random string at the start. The
7
+ model must find the correct answer among similar ones and repeat it exactly.
8
+
9
+ A task pastes the earlier conversation in user turns of about `turn_tokens` tokens
10
+ each, and the agent gives a short reply. The last turn gives the request. Only the
11
+ answer to that request counts. When the conversation is larger than the context budget
12
+ of the agent, the agent must fold old turns. A fold note seldom keeps a whole answer
13
+ word for word, so MRCR shows what a fold loses. Compare it with
14
+ `blockether.vis.evals.longmemeval`, which asks only for short facts.
15
+
16
+ The grader follows the MRCR grader: the answer must start with the random string, and
17
+ `difflib.SequenceMatcher` compares the rest with the expected answer. The MRCR score is
18
+ that ratio. A Harbor reward is a pass or a failure, so a task passes when the ratio is
19
+ at least `MIN_RATIO`. The protocol differs from MRCR in two ways:
20
+
21
+ - The conversation arrives as pasted text in user turns, not as one prompt with
22
+ assistant messages.
23
+ - The score is a pass at `MIN_RATIO`, not the mean ratio.
24
+
25
+ Importing this module makes no network calls. `load_rows` downloads only the rows that
26
+ it needs, about 1 MB for each long row. Run `python -m blockether.vis.evals.mrcr DATASET
27
+ --cache FILE` to write the tasks.
28
+ """
29
+
30
+ from __future__ import annotations
31
+
32
+ import argparse
33
+ import json
34
+ import urllib.request
35
+ from collections.abc import Callable, Iterable, Mapping, Sequence
36
+ from pathlib import Path
37
+ from typing import Any
38
+
39
+ from .harbor import HarborTask, write_task
40
+
41
+ __all__ = [
42
+ "MIN_RATIO",
43
+ "NEEDLES",
44
+ "fetch_row",
45
+ "load_rows",
46
+ "mrcr_task_name",
47
+ "task_turns",
48
+ "write_mrcr_tasks",
49
+ ]
50
+
51
+ ROWS_URL = (
52
+ "https://datasets-server.huggingface.co/rows"
53
+ "?dataset=openai/mrcr&config=default&split=train&offset={index}&length=1"
54
+ )
55
+ NEEDLES = {2: 0, 4: 800, 8: 1600}
56
+ """The first row of each needle count. Each needle count has `ROWS_PER_NEEDLES` rows."""
57
+ ROWS_PER_NEEDLES = 800
58
+ PROGRAM_FILE = "check_answer.py"
59
+ EXPECTED_FILE = "expected.json"
60
+ CHARS_PER_TOKEN = 4
61
+ TURN_TOKENS = 10_000
62
+ TURN_TIMEOUT_SEC = 300.0
63
+ MIN_RATIO = 0.9
64
+ MIN_CHARS = 600_000
65
+ MAX_CHARS = 1_300_000
66
+
67
+ _FIRST = (
68
+ "Over the next messages I will paste an earlier conversation between a user and "
69
+ "an AI assistant. Later I will ask you about it. Do not create or edit files: keep "
70
+ "what you need in this conversation. Until I give my request, a short reply is "
71
+ "enough."
72
+ )
73
+ _MORE = "More of the earlier conversation."
74
+ _LAST = (
75
+ "That is the whole earlier conversation. Answer my request about it. Give only the "
76
+ "requested text, without a code block or other words: {request}"
77
+ )
78
+
79
+ _PROGRAM = r"""# Grade an MRCR answer like the MRCR grader. The answer is on stdin.
80
+ import json
81
+ import sys
82
+ from difflib import SequenceMatcher
83
+ from pathlib import Path
84
+
85
+ expected = json.loads(Path(__file__).with_name("expected.json").read_text())
86
+ prefix = expected["prefix"]
87
+ answer = sys.stdin.read().strip()
88
+ if not answer.startswith(prefix):
89
+ sys.exit(f"the answer does not start with {prefix!r}: {answer[:80]!r}")
90
+ ratio = SequenceMatcher(
91
+ None, answer.removeprefix(prefix), expected["answer"].removeprefix(prefix)
92
+ ).ratio()
93
+ if ratio < expected["min_ratio"]:
94
+ sys.exit(f"the answer matches {ratio:.3f} of the expected text, less than {expected['min_ratio']}")
95
+ """
96
+
97
+
98
+ def fetch_row(index: int) -> dict[str, Any]:
99
+ """Download the MRCR row `index` from the Hugging Face dataset server."""
100
+ request = urllib.request.Request(
101
+ ROWS_URL.format(index=index), headers={"User-Agent": "vis-evals"}
102
+ )
103
+ with urllib.request.urlopen(request, timeout=300) as response:
104
+ rows = json.load(response)["rows"]
105
+ if len(rows) != 1:
106
+ raise ValueError(f"MRCR row {index} does not exist")
107
+ return {"row_idx": index, **rows[0]["row"]}
108
+
109
+
110
+ def load_rows(
111
+ cache: Path | str,
112
+ *,
113
+ needles: int = 2,
114
+ min_chars: int = MIN_CHARS,
115
+ max_chars: int = MAX_CHARS,
116
+ limit: int = 5,
117
+ fetch: Callable[[int], Mapping[str, Any]] = fetch_row,
118
+ ) -> list[dict[str, Any]]:
119
+ """Return the first `limit` MRCR rows with `needles` needles and a size in range.
120
+
121
+ `min_chars` and `max_chars` bound the `n_chars` size of a row. The JSON Lines file
122
+ `cache` keeps each row that `fetch` downloaded, and only the size of a row out of
123
+ range. So a second call with the same or a smaller selection downloads nothing. An
124
+ unknown needle count raises ValueError.
125
+ """
126
+ if needles not in NEEDLES:
127
+ raise ValueError(f"needles must be one of {sorted(NEEDLES)}: {needles!r}")
128
+ path = Path(cache)
129
+ known: dict[int, dict[str, Any]] = {}
130
+ if path.is_file():
131
+ for line in path.read_text(encoding="utf-8").splitlines():
132
+ if line.strip():
133
+ row = json.loads(line)
134
+ known[int(row["row_idx"])] = row
135
+ chosen: list[dict[str, Any]] = []
136
+ first = NEEDLES[needles]
137
+ for index in range(first, first + ROWS_PER_NEEDLES):
138
+ if len(chosen) >= limit:
139
+ break
140
+ row = known.get(index)
141
+ if row is None:
142
+ row = dict(fetch(index))
143
+ row["row_idx"] = index
144
+ if not min_chars <= int(row["n_chars"]) <= max_chars:
145
+ row = {key: row[key] for key in ("row_idx", "n_needles", "n_chars")}
146
+ path.parent.mkdir(parents=True, exist_ok=True)
147
+ with path.open("a", encoding="utf-8") as target:
148
+ target.write(json.dumps(row) + "\n")
149
+ known[index] = row
150
+ if "prompt" in row and min_chars <= int(row["n_chars"]) <= max_chars:
151
+ chosen.append(row)
152
+ return chosen
153
+
154
+
155
+ def mrcr_task_name(row: Mapping[str, Any]) -> str:
156
+ """Return the Harbor task name of `row`, for example `mrcr-2n-17`."""
157
+ return f"mrcr-{int(row['n_needles'])}n-{int(row['row_idx'])}"
158
+
159
+
160
+ def _tokens(text: str) -> int:
161
+ return len(text) // CHARS_PER_TOKEN
162
+
163
+
164
+ def _message_text(message: Mapping[str, Any]) -> str:
165
+ role = "USER" if message.get("role") == "user" else "ASSISTANT"
166
+ return f"{role}: {str(message.get('content', '')).strip()}"
167
+
168
+
169
+ def task_turns(row: Mapping[str, Any], *, turn_tokens: int = TURN_TOKENS) -> list[str]:
170
+ """Return the user messages of the task for `row`.
171
+
172
+ The earlier messages of the MRCR conversation come in pasted turns of about
173
+ `turn_tokens` tokens. A message never splits. The last turn gives the last request
174
+ of the conversation.
175
+ """
176
+ messages = json.loads(row["prompt"])
177
+ if not messages or messages[-1].get("role") != "user":
178
+ raise ValueError(f"MRCR row {row.get('row_idx')} does not end with a request")
179
+ turns: list[list[str]] = [[]]
180
+ size = 0
181
+ for message in messages[:-1]:
182
+ text = _message_text(message)
183
+ if turns[-1] and size + _tokens(text) > turn_tokens:
184
+ turns.append([])
185
+ size = 0
186
+ turns[-1].append(text)
187
+ size += _tokens(text)
188
+ pasted = [
189
+ f"{_FIRST if number == 0 else _MORE}\n\n" + "\n\n".join(chunk)
190
+ for number, chunk in enumerate(turns)
191
+ if chunk
192
+ ]
193
+ return [*pasted, _LAST.format(request=str(messages[-1]["content"]).strip())]
194
+
195
+
196
+ def write_mrcr_tasks(
197
+ dataset: Path | str,
198
+ rows: Iterable[Mapping[str, Any]],
199
+ *,
200
+ turn_tokens: int = TURN_TOKENS,
201
+ min_ratio: float = MIN_RATIO,
202
+ ) -> tuple[HarborTask, ...]:
203
+ """Write one Harbor task for each MRCR row in the folder `dataset`.
204
+
205
+ A task passes when the answer starts with the random string of the row and matches
206
+ at least `min_ratio` of the expected answer.
207
+ """
208
+ written: list[HarborTask] = []
209
+ for row in rows:
210
+ turns = task_turns(row, turn_tokens=turn_tokens)
211
+ name = mrcr_task_name(row)
212
+ written.append(
213
+ write_task(
214
+ dataset,
215
+ name,
216
+ turns,
217
+ {"want_answer_program": PROGRAM_FILE},
218
+ agent_timeout_sec=TURN_TIMEOUT_SEC * len(turns),
219
+ metadata={
220
+ "benchmark": "mrcr",
221
+ "row": int(row["row_idx"]),
222
+ "needles": int(row["n_needles"]),
223
+ "chars": int(row["n_chars"]),
224
+ "turns": len(turns),
225
+ "history_tokens": sum(_tokens(turn) for turn in turns),
226
+ },
227
+ )
228
+ )
229
+ tests = Path(dataset) / name / "tests"
230
+ (tests / PROGRAM_FILE).write_text(_PROGRAM, encoding="utf-8")
231
+ expected = {
232
+ "answer": str(row["answer"]),
233
+ "prefix": str(row["random_string_to_prepend"]),
234
+ "min_ratio": min_ratio,
235
+ }
236
+ (tests / EXPECTED_FILE).write_text(
237
+ json.dumps(expected, indent=2) + "\n", encoding="utf-8"
238
+ )
239
+ return tuple(written)
240
+
241
+
242
+ def main(argv: Sequence[str] | None = None) -> int:
243
+ parser = argparse.ArgumentParser(
244
+ prog="python -m blockether.vis.evals.mrcr",
245
+ description="Write OpenAI MRCR conversations as long multi-turn Harbor tasks.",
246
+ )
247
+ parser.add_argument("dataset", type=Path, help="folder for the new tasks")
248
+ parser.add_argument(
249
+ "--cache", type=Path, required=True, help="JSON Lines file of downloaded rows"
250
+ )
251
+ parser.add_argument("--needles", type=int, choices=sorted(NEEDLES), default=2)
252
+ parser.add_argument("--min-chars", type=int, default=MIN_CHARS)
253
+ parser.add_argument("--max-chars", type=int, default=MAX_CHARS)
254
+ parser.add_argument("--limit", type=int, default=5, help="rows to write")
255
+ parser.add_argument("--turn-tokens", type=int, default=TURN_TOKENS)
256
+ parser.add_argument("--min-ratio", type=float, default=MIN_RATIO)
257
+ args = parser.parse_args(argv)
258
+ rows = load_rows(
259
+ args.cache,
260
+ needles=args.needles,
261
+ min_chars=args.min_chars,
262
+ max_chars=args.max_chars,
263
+ limit=args.limit,
264
+ )
265
+ written = write_mrcr_tasks(
266
+ args.dataset, rows, turn_tokens=args.turn_tokens, min_ratio=args.min_ratio
267
+ )
268
+ print(f"wrote {len(written)} tasks to {args.dataset}")
269
+ return 0
270
+
271
+
272
+ if __name__ == "__main__":
273
+ raise SystemExit(main())