vis-agent 0.2.47.dev7678__tar.gz → 0.2.48.dev7720__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 (143) hide show
  1. {vis_agent-0.2.47.dev7678 → vis_agent-0.2.48.dev7720}/PKG-INFO +15 -14
  2. vis_agent-0.2.48.dev7720/README.md +38 -0
  3. {vis_agent-0.2.47.dev7678 → vis_agent-0.2.48.dev7720}/contracts/schema/agents.json +57 -1
  4. {vis_agent-0.2.47.dev7678 → vis_agent-0.2.48.dev7720}/contracts/schema/gateway.json +13 -0
  5. vis_agent-0.2.48.dev7720/hatch_build.py +45 -0
  6. {vis_agent-0.2.47.dev7678 → vis_agent-0.2.48.dev7720}/pyproject.toml +1 -1
  7. vis_agent-0.2.48.dev7720/release-tag +1 -0
  8. {vis_agent-0.2.47.dev7678 → vis_agent-0.2.48.dev7720}/src/blockether/vis/engine/__init__.py +4 -0
  9. {vis_agent-0.2.47.dev7678 → vis_agent-0.2.48.dev7720}/src/blockether/vis/engine/_client.py +18 -0
  10. vis_agent-0.2.48.dev7720/src/blockether/vis/engine/_install.py +315 -0
  11. {vis_agent-0.2.47.dev7678 → vis_agent-0.2.48.dev7720}/src/blockether/vis/engine/_local.py +17 -6
  12. {vis_agent-0.2.47.dev7678 → vis_agent-0.2.48.dev7720}/src/blockether/vis/evals/agent.py +6 -6
  13. {vis_agent-0.2.47.dev7678 → vis_agent-0.2.48.dev7720}/tests/test_contracts.py +3 -3
  14. {vis_agent-0.2.47.dev7678 → vis_agent-0.2.48.dev7720}/tests/test_evals_agent.py +14 -0
  15. {vis_agent-0.2.47.dev7678 → vis_agent-0.2.48.dev7720}/tests/test_gateway_performance.py +74 -6
  16. vis_agent-0.2.48.dev7720/tests/test_install.py +235 -0
  17. vis_agent-0.2.47.dev7678/README.md +0 -37
  18. vis_agent-0.2.47.dev7678/hatch_build.py +0 -19
  19. {vis_agent-0.2.47.dev7678 → vis_agent-0.2.48.dev7720}/.gitignore +0 -0
  20. {vis_agent-0.2.47.dev7678 → vis_agent-0.2.48.dev7720}/LICENSE +0 -0
  21. {vis_agent-0.2.47.dev7678 → vis_agent-0.2.48.dev7720}/contracts/fixtures/activity-arguments.json +0 -0
  22. {vis_agent-0.2.47.dev7678 → vis_agent-0.2.48.dev7720}/contracts/fixtures/activity-cases.json +0 -0
  23. {vis_agent-0.2.47.dev7678 → vis_agent-0.2.48.dev7720}/contracts/fixtures/activity-copy.json +0 -0
  24. {vis_agent-0.2.47.dev7678 → vis_agent-0.2.48.dev7720}/contracts/fixtures/activity-execution.json +0 -0
  25. {vis_agent-0.2.47.dev7678 → vis_agent-0.2.48.dev7720}/contracts/fixtures/activity-groups.json +0 -0
  26. {vis_agent-0.2.47.dev7678 → vis_agent-0.2.48.dev7720}/contracts/fixtures/activity-read-session.json +0 -0
  27. {vis_agent-0.2.47.dev7678 → vis_agent-0.2.48.dev7720}/contracts/fixtures/activity-reads.json +0 -0
  28. {vis_agent-0.2.47.dev7678 → vis_agent-0.2.48.dev7720}/contracts/fixtures/activity-results.json +0 -0
  29. {vis_agent-0.2.47.dev7678 → vis_agent-0.2.48.dev7720}/contracts/fixtures/activity-tables.json +0 -0
  30. {vis_agent-0.2.47.dev7678 → vis_agent-0.2.48.dev7720}/contracts/fixtures/activity.json +0 -0
  31. {vis_agent-0.2.47.dev7678 → vis_agent-0.2.48.dev7720}/contracts/fixtures/diff.json +0 -0
  32. {vis_agent-0.2.47.dev7678 → vis_agent-0.2.48.dev7720}/contracts/fixtures/live-primitives.json +0 -0
  33. {vis_agent-0.2.47.dev7678 → vis_agent-0.2.48.dev7720}/contracts/fixtures/session-health.json +0 -0
  34. {vis_agent-0.2.47.dev7678 → vis_agent-0.2.48.dev7720}/contracts/fixtures/view.json +0 -0
  35. {vis_agent-0.2.47.dev7678 → vis_agent-0.2.48.dev7720}/contracts/schema/activity.json +0 -0
  36. {vis_agent-0.2.47.dev7678 → vis_agent-0.2.48.dev7720}/contracts/schema/automations.json +0 -0
  37. {vis_agent-0.2.47.dev7678 → vis_agent-0.2.48.dev7720}/contracts/schema/common.json +0 -0
  38. {vis_agent-0.2.47.dev7678 → vis_agent-0.2.48.dev7720}/contracts/schema/config.json +0 -0
  39. {vis_agent-0.2.47.dev7678 → vis_agent-0.2.48.dev7720}/contracts/schema/content.json +0 -0
  40. {vis_agent-0.2.47.dev7678 → vis_agent-0.2.48.dev7720}/contracts/schema/council.json +0 -0
  41. {vis_agent-0.2.47.dev7678 → vis_agent-0.2.48.dev7720}/contracts/schema/diff.json +0 -0
  42. {vis_agent-0.2.47.dev7678 → vis_agent-0.2.48.dev7720}/contracts/schema/evals.json +0 -0
  43. {vis_agent-0.2.47.dev7678 → vis_agent-0.2.48.dev7720}/contracts/schema/improve.json +0 -0
  44. {vis_agent-0.2.47.dev7678 → vis_agent-0.2.48.dev7720}/contracts/schema/provider.json +0 -0
  45. {vis_agent-0.2.47.dev7678 → vis_agent-0.2.48.dev7720}/contracts/schema/rooms.json +0 -0
  46. {vis_agent-0.2.47.dev7678 → vis_agent-0.2.48.dev7720}/contracts/schema/symbol.json +0 -0
  47. {vis_agent-0.2.47.dev7678 → vis_agent-0.2.48.dev7720}/contracts/schema/toggle.json +0 -0
  48. {vis_agent-0.2.47.dev7678 → vis_agent-0.2.48.dev7720}/contracts/schema/view.json +0 -0
  49. {vis_agent-0.2.47.dev7678 → vis_agent-0.2.48.dev7720}/examples/greeter/tests/test_greeter.py +0 -0
  50. {vis_agent-0.2.47.dev7678 → vis_agent-0.2.48.dev7720}/src/blockether/vis/__init__.py +0 -0
  51. {vis_agent-0.2.47.dev7678 → vis_agent-0.2.48.dev7720}/src/blockether/vis/_contracts.py +0 -0
  52. {vis_agent-0.2.47.dev7678 → vis_agent-0.2.48.dev7720}/src/blockether/vis/_outside.py +0 -0
  53. {vis_agent-0.2.47.dev7678 → vis_agent-0.2.48.dev7720}/src/blockether/vis/_wire.py +0 -0
  54. {vis_agent-0.2.47.dev7678 → vis_agent-0.2.48.dev7720}/src/blockether/vis/activity.py +0 -0
  55. {vis_agent-0.2.47.dev7678 → vis_agent-0.2.48.dev7720}/src/blockether/vis/engine/_agent.py +0 -0
  56. {vis_agent-0.2.47.dev7678 → vis_agent-0.2.48.dev7720}/src/blockether/vis/engine/_agents.py +0 -0
  57. {vis_agent-0.2.47.dev7678 → vis_agent-0.2.48.dev7720}/src/blockether/vis/engine/_council.py +0 -0
  58. {vis_agent-0.2.47.dev7678 → vis_agent-0.2.48.dev7720}/src/blockether/vis/engine/_extensions.py +0 -0
  59. {vis_agent-0.2.47.dev7678 → vis_agent-0.2.48.dev7720}/src/blockether/vis/evals/__init__.py +0 -0
  60. {vis_agent-0.2.47.dev7678 → vis_agent-0.2.48.dev7720}/src/blockether/vis/evals/_gateway.py +0 -0
  61. {vis_agent-0.2.47.dev7678 → vis_agent-0.2.48.dev7720}/src/blockether/vis/evals/bench.py +0 -0
  62. {vis_agent-0.2.47.dev7678 → vis_agent-0.2.48.dev7720}/src/blockether/vis/evals/check.py +0 -0
  63. {vis_agent-0.2.47.dev7678 → vis_agent-0.2.48.dev7720}/src/blockether/vis/evals/compare.py +0 -0
  64. {vis_agent-0.2.47.dev7678 → vis_agent-0.2.48.dev7720}/src/blockether/vis/evals/environment.py +0 -0
  65. {vis_agent-0.2.47.dev7678 → vis_agent-0.2.48.dev7720}/src/blockether/vis/evals/grade.py +0 -0
  66. {vis_agent-0.2.47.dev7678 → vis_agent-0.2.48.dev7720}/src/blockether/vis/evals/harbor.py +0 -0
  67. {vis_agent-0.2.47.dev7678 → vis_agent-0.2.48.dev7720}/src/blockether/vis/evals/judge-rubric.toml +0 -0
  68. {vis_agent-0.2.47.dev7678 → vis_agent-0.2.48.dev7720}/src/blockether/vis/evals/judge.py +0 -0
  69. {vis_agent-0.2.47.dev7678 → vis_agent-0.2.48.dev7720}/src/blockether/vis/evals/labels.py +0 -0
  70. {vis_agent-0.2.47.dev7678 → vis_agent-0.2.48.dev7720}/src/blockether/vis/evals/leaderboard.py +0 -0
  71. {vis_agent-0.2.47.dev7678 → vis_agent-0.2.48.dev7720}/src/blockether/vis/evals/repeats.py +0 -0
  72. {vis_agent-0.2.47.dev7678 → vis_agent-0.2.48.dev7720}/src/blockether/vis/evals/rounds.py +0 -0
  73. {vis_agent-0.2.47.dev7678 → vis_agent-0.2.48.dev7720}/src/blockether/vis/evals/stages.py +0 -0
  74. {vis_agent-0.2.47.dev7678 → vis_agent-0.2.48.dev7720}/src/blockether/vis/evals/stats.py +0 -0
  75. {vis_agent-0.2.47.dev7678 → vis_agent-0.2.48.dev7720}/src/blockether/vis/evals/trajectory.py +0 -0
  76. {vis_agent-0.2.47.dev7678 → vis_agent-0.2.48.dev7720}/src/blockether/vis/extension.py +0 -0
  77. {vis_agent-0.2.47.dev7678 → vis_agent-0.2.48.dev7720}/src/blockether/vis/extension_package.py +0 -0
  78. {vis_agent-0.2.47.dev7678 → vis_agent-0.2.48.dev7720}/src/blockether/vis/py.typed +0 -0
  79. {vis_agent-0.2.47.dev7678 → vis_agent-0.2.48.dev7720}/src/blockether/vis/rooms.py +0 -0
  80. {vis_agent-0.2.47.dev7678 → vis_agent-0.2.48.dev7720}/src/blockether/vis/views.py +0 -0
  81. {vis_agent-0.2.47.dev7678 → vis_agent-0.2.48.dev7720}/tests/conftest.py +0 -0
  82. {vis_agent-0.2.47.dev7678 → vis_agent-0.2.48.dev7720}/tests/evals_atif.py +0 -0
  83. {vis_agent-0.2.47.dev7678 → vis_agent-0.2.48.dev7720}/tests/test_activity.py +0 -0
  84. {vis_agent-0.2.47.dev7678 → vis_agent-0.2.48.dev7720}/tests/test_agent.py +0 -0
  85. {vis_agent-0.2.47.dev7678 → vis_agent-0.2.48.dev7720}/tests/test_agent_extension_lifecycle.py +0 -0
  86. {vis_agent-0.2.47.dev7678 → vis_agent-0.2.48.dev7720}/tests/test_agent_extensions.py +0 -0
  87. {vis_agent-0.2.47.dev7678 → vis_agent-0.2.48.dev7720}/tests/test_agents.py +0 -0
  88. {vis_agent-0.2.47.dev7678 → vis_agent-0.2.48.dev7720}/tests/test_api_guides.py +0 -0
  89. {vis_agent-0.2.47.dev7678 → vis_agent-0.2.48.dev7720}/tests/test_attachments.py +0 -0
  90. {vis_agent-0.2.47.dev7678 → vis_agent-0.2.48.dev7720}/tests/test_authoring.py +0 -0
  91. {vis_agent-0.2.47.dev7678 → vis_agent-0.2.48.dev7720}/tests/test_catalog.py +0 -0
  92. {vis_agent-0.2.47.dev7678 → vis_agent-0.2.48.dev7720}/tests/test_catalog_recipe.py +0 -0
  93. {vis_agent-0.2.47.dev7678 → vis_agent-0.2.48.dev7720}/tests/test_ci_report_recipe.py +0 -0
  94. {vis_agent-0.2.47.dev7678 → vis_agent-0.2.48.dev7720}/tests/test_client.py +0 -0
  95. {vis_agent-0.2.47.dev7678 → vis_agent-0.2.48.dev7720}/tests/test_client_extensions_engine.py +0 -0
  96. {vis_agent-0.2.47.dev7678 → vis_agent-0.2.48.dev7720}/tests/test_code_quality_recipe.py +0 -0
  97. {vis_agent-0.2.47.dev7678 → vis_agent-0.2.48.dev7720}/tests/test_council.py +0 -0
  98. {vis_agent-0.2.47.dev7678 → vis_agent-0.2.48.dev7720}/tests/test_declarations.py +0 -0
  99. {vis_agent-0.2.47.dev7678 → vis_agent-0.2.48.dev7720}/tests/test_engine.py +0 -0
  100. {vis_agent-0.2.47.dev7678 → vis_agent-0.2.48.dev7720}/tests/test_engine_docstrings.py +0 -0
  101. {vis_agent-0.2.47.dev7678 → vis_agent-0.2.48.dev7720}/tests/test_evals_bench.py +0 -0
  102. {vis_agent-0.2.47.dev7678 → vis_agent-0.2.48.dev7720}/tests/test_evals_check.py +0 -0
  103. {vis_agent-0.2.47.dev7678 → vis_agent-0.2.48.dev7720}/tests/test_evals_compare.py +0 -0
  104. {vis_agent-0.2.47.dev7678 → vis_agent-0.2.48.dev7720}/tests/test_evals_environment.py +0 -0
  105. {vis_agent-0.2.47.dev7678 → vis_agent-0.2.48.dev7720}/tests/test_evals_gateway.py +0 -0
  106. {vis_agent-0.2.47.dev7678 → vis_agent-0.2.48.dev7720}/tests/test_evals_grade.py +0 -0
  107. {vis_agent-0.2.47.dev7678 → vis_agent-0.2.48.dev7720}/tests/test_evals_guards.py +0 -0
  108. {vis_agent-0.2.47.dev7678 → vis_agent-0.2.48.dev7720}/tests/test_evals_harbor.py +0 -0
  109. {vis_agent-0.2.47.dev7678 → vis_agent-0.2.48.dev7720}/tests/test_evals_judge.py +0 -0
  110. {vis_agent-0.2.47.dev7678 → vis_agent-0.2.48.dev7720}/tests/test_evals_labels.py +0 -0
  111. {vis_agent-0.2.47.dev7678 → vis_agent-0.2.48.dev7720}/tests/test_evals_repeats.py +0 -0
  112. {vis_agent-0.2.47.dev7678 → vis_agent-0.2.48.dev7720}/tests/test_evals_rounds.py +0 -0
  113. {vis_agent-0.2.47.dev7678 → vis_agent-0.2.48.dev7720}/tests/test_evals_stages.py +0 -0
  114. {vis_agent-0.2.47.dev7678 → vis_agent-0.2.48.dev7720}/tests/test_evals_stats.py +0 -0
  115. {vis_agent-0.2.47.dev7678 → vis_agent-0.2.48.dev7720}/tests/test_evals_tasks.py +0 -0
  116. {vis_agent-0.2.47.dev7678 → vis_agent-0.2.48.dev7720}/tests/test_evals_trajectory.py +0 -0
  117. {vis_agent-0.2.47.dev7678 → vis_agent-0.2.48.dev7720}/tests/test_extension_package.py +0 -0
  118. {vis_agent-0.2.47.dev7678 → vis_agent-0.2.48.dev7720}/tests/test_extension_reference.py +0 -0
  119. {vis_agent-0.2.47.dev7678 → vis_agent-0.2.48.dev7720}/tests/test_goals.py +0 -0
  120. {vis_agent-0.2.47.dev7678 → vis_agent-0.2.48.dev7720}/tests/test_live_primitives.py +0 -0
  121. {vis_agent-0.2.47.dev7678 → vis_agent-0.2.48.dev7720}/tests/test_live_wait.py +0 -0
  122. {vis_agent-0.2.47.dev7678 → vis_agent-0.2.48.dev7720}/tests/test_local.py +0 -0
  123. {vis_agent-0.2.47.dev7678 → vis_agent-0.2.48.dev7720}/tests/test_method_typing.py +0 -0
  124. {vis_agent-0.2.47.dev7678 → vis_agent-0.2.48.dev7720}/tests/test_monitor_recipe.py +0 -0
  125. {vis_agent-0.2.47.dev7678 → vis_agent-0.2.48.dev7720}/tests/test_namespace.py +0 -0
  126. {vis_agent-0.2.47.dev7678 → vis_agent-0.2.48.dev7720}/tests/test_outside.py +0 -0
  127. {vis_agent-0.2.47.dev7678 → vis_agent-0.2.48.dev7720}/tests/test_providers.py +0 -0
  128. {vis_agent-0.2.47.dev7678 → vis_agent-0.2.48.dev7720}/tests/test_read_session.py +0 -0
  129. {vis_agent-0.2.47.dev7678 → vis_agent-0.2.48.dev7720}/tests/test_record_arguments.py +0 -0
  130. {vis_agent-0.2.47.dev7678 → vis_agent-0.2.48.dev7720}/tests/test_registration.py +0 -0
  131. {vis_agent-0.2.47.dev7678 → vis_agent-0.2.48.dev7720}/tests/test_repair_hooks.py +0 -0
  132. {vis_agent-0.2.47.dev7678 → vis_agent-0.2.48.dev7720}/tests/test_result_sequence.py +0 -0
  133. {vis_agent-0.2.47.dev7678 → vis_agent-0.2.48.dev7720}/tests/test_rooms.py +0 -0
  134. {vis_agent-0.2.47.dev7678 → vis_agent-0.2.48.dev7720}/tests/test_scoped_settings.py +0 -0
  135. {vis_agent-0.2.47.dev7678 → vis_agent-0.2.48.dev7720}/tests/test_sdk_bootstrap.py +0 -0
  136. {vis_agent-0.2.47.dev7678 → vis_agent-0.2.48.dev7720}/tests/test_sdk_guide.py +0 -0
  137. {vis_agent-0.2.47.dev7678 → vis_agent-0.2.48.dev7720}/tests/test_sdk_typing.py +0 -0
  138. {vis_agent-0.2.47.dev7678 → vis_agent-0.2.48.dev7720}/tests/test_structured_live.py +0 -0
  139. {vis_agent-0.2.47.dev7678 → vis_agent-0.2.48.dev7720}/tests/test_symbol_contract.py +0 -0
  140. {vis_agent-0.2.47.dev7678 → vis_agent-0.2.48.dev7720}/tests/test_testing.py +0 -0
  141. {vis_agent-0.2.47.dev7678 → vis_agent-0.2.48.dev7720}/tests/test_text_list_arguments.py +0 -0
  142. {vis_agent-0.2.47.dev7678 → vis_agent-0.2.48.dev7720}/tests/test_tools.py +0 -0
  143. {vis_agent-0.2.47.dev7678 → vis_agent-0.2.48.dev7720}/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.47.dev7678
3
+ Version: 0.2.48.dev7720
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
@@ -28,15 +28,18 @@ Description-Content-Type: text/markdown
28
28
 
29
29
  # vis-agent
30
30
 
31
- Python SDK for Vis extensions and agent sessions.
31
+ Python SDK for Vis agent sessions and extensions.
32
32
 
33
33
  ```sh
34
34
  pip install vis-agent
35
35
  ```
36
36
 
37
- Requires Python 3.12+. Install the Vis executable and configure a provider
38
- separately for local use. The execution-layer API below is unreleased: build
39
- the SDK and engine from the same source revision.
37
+ You need Python 3.12+ on Linux or macOS and a configured model provider. You do not
38
+ have to install Vis first. A local run uses the installed `vis-agent` only when it is
39
+ the same release as the SDK. Otherwise the SDK downloads only that release's engine into
40
+ `~/.vis/sdk` and checks it against the release checksums. Set `VIS_NO_AUTO_INSTALL=1` to
41
+ turn this off. To download the engine ahead of time, for example in a container image, run
42
+ `python -c "from blockether.vis.engine import install; print(install())"`.
40
43
 
41
44
  ## Run a local agent
42
45
 
@@ -48,18 +51,16 @@ with Agent(project=".") as agent:
48
51
  print(result["status"])
49
52
  ```
50
53
 
51
- The SDK starts an owned `vis-agent stdio` process, sets up a temporary session
52
- database and cleans it up on close. This is a Python transport, not an
53
- interactive CLI command or an MCP server. Tasks can incur provider charges and
54
- access project files. For a chosen executable or multiple sessions on one
55
- process, use `LocalEngine`; see the [stdio guide](https://vis.blockether.com/python-sdk.html#use-stdio-from-the-python-sdk).
54
+ The SDK starts a private Vis process with a temporary session database and removes both
55
+ when the block ends. Tasks can incur provider charges and can change project files. To
56
+ choose the executable or share one process between agents, use `LocalEngine`. See the
57
+ [stdio guide](https://vis.blockether.com/python-sdk.html#use-stdio-from-the-python-sdk).
56
58
 
57
59
  - [Python SDK guide](https://vis.blockether.com/python-sdk.html) — run a local
58
- agent, continue a conversation or connect to a shared gateway. `Agent` is
59
- available in SDK `0.2.3` and newer.
60
- - [Decision models](https://vis.blockether.com/decision-models.html) — download
61
- Laya or GLiNER2.5, train both heads offline, publish FP32 and select a version.
60
+ agent, continue a conversation or connect to a shared gateway.
62
61
  - [Python API reference](https://vis.blockether.com/python-sdk-api/) — browse
63
62
  classes, methods, signatures and types generated from the SDK source.
64
63
  - [Extension guide](https://vis.blockether.com/extending.html) — add tools to Vis.
64
+ - [Decision models](https://vis.blockether.com/decision-models.html) — download
65
+ Laya or GLiNER2.5, train both heads offline, publish FP32 and select a version.
65
66
  - [Documentation](https://vis.blockether.com) — browse all guides and reference pages.
@@ -0,0 +1,38 @@
1
+ # vis-agent
2
+
3
+ Python SDK for Vis agent sessions and extensions.
4
+
5
+ ```sh
6
+ pip install vis-agent
7
+ ```
8
+
9
+ You need Python 3.12+ on Linux or macOS and a configured model provider. You do not
10
+ have to install Vis first. A local run uses the installed `vis-agent` only when it is
11
+ the same release as the SDK. Otherwise the SDK downloads only that release's engine into
12
+ `~/.vis/sdk` and checks it against the release checksums. Set `VIS_NO_AUTO_INSTALL=1` to
13
+ turn this off. To download the engine ahead of time, for example in a container image, run
14
+ `python -c "from blockether.vis.engine import install; print(install())"`.
15
+
16
+ ## Run a local agent
17
+
18
+ ```python
19
+ from blockether.vis.engine import Agent
20
+
21
+ with Agent(project=".") as agent:
22
+ result = agent.run("Summarize this project without changing files.")
23
+ print(result["status"])
24
+ ```
25
+
26
+ The SDK starts a private Vis process with a temporary session database and removes both
27
+ when the block ends. Tasks can incur provider charges and can change project files. To
28
+ choose the executable or share one process between agents, use `LocalEngine`. See the
29
+ [stdio guide](https://vis.blockether.com/python-sdk.html#use-stdio-from-the-python-sdk).
30
+
31
+ - [Python SDK guide](https://vis.blockether.com/python-sdk.html) — run a local
32
+ agent, continue a conversation or connect to a shared gateway.
33
+ - [Python API reference](https://vis.blockether.com/python-sdk-api/) — browse
34
+ classes, methods, signatures and types generated from the SDK source.
35
+ - [Extension guide](https://vis.blockether.com/extending.html) — add tools to Vis.
36
+ - [Decision models](https://vis.blockether.com/decision-models.html) — download
37
+ Laya or GLiNER2.5, train both heads offline, publish FP32 and select a version.
38
+ - [Documentation](https://vis.blockether.com) — browse all guides and reference pages.
@@ -27,7 +27,7 @@
27
27
  "type": "integer",
28
28
  "minimum": 1,
29
29
  "maximum": 600,
30
- "default": 64
30
+ "default": 120
31
31
  },
32
32
  "allowed_models": {
33
33
  "type": "array",
@@ -80,6 +80,62 @@
80
80
  ],
81
81
  "additionalProperties": false
82
82
  },
83
+ "extend": {
84
+ "type": "object",
85
+ "properties": {
86
+ "session_id": {
87
+ "type": "string",
88
+ "minLength": 1
89
+ },
90
+ "iterations": {
91
+ "type": "integer",
92
+ "minimum": 1,
93
+ "maximum": 600
94
+ }
95
+ },
96
+ "required": [
97
+ "session_id",
98
+ "iterations"
99
+ ],
100
+ "additionalProperties": false
101
+ },
102
+ "deny": {
103
+ "type": "object",
104
+ "properties": {
105
+ "session_id": {
106
+ "type": "string",
107
+ "minLength": 1
108
+ },
109
+ "reason": {
110
+ "type": "string",
111
+ "minLength": 1,
112
+ "maxLength": 2000,
113
+ "pattern": "\\S"
114
+ }
115
+ },
116
+ "required": [
117
+ "session_id",
118
+ "reason"
119
+ ],
120
+ "additionalProperties": false
121
+ },
122
+ "resume": {
123
+ "type": "object",
124
+ "properties": {
125
+ "session_id": {
126
+ "type": "string",
127
+ "minLength": 1
128
+ },
129
+ "task": {
130
+ "$ref": "#/$defs/spawn/properties/task"
131
+ }
132
+ },
133
+ "required": [
134
+ "session_id",
135
+ "task"
136
+ ],
137
+ "additionalProperties": false
138
+ },
83
139
  "route": {
84
140
  "type": "object",
85
141
  "properties": {
@@ -1226,6 +1226,16 @@
1226
1226
  },
1227
1227
  "path": "/v1/sessions/:sid/workspace/root"
1228
1228
  },
1229
+ {
1230
+ "audience": "sdk",
1231
+ "operations": {
1232
+ "post": {
1233
+ "request": "json",
1234
+ "response": "json"
1235
+ }
1236
+ },
1237
+ "path": "/v1/sessions/actions/archive"
1238
+ },
1229
1239
  {
1230
1240
  "audience": "sdk",
1231
1241
  "operations": {
@@ -2383,6 +2393,9 @@
2383
2393
  {
2384
2394
  "const": "session.agent_name_updated"
2385
2395
  },
2396
+ {
2397
+ "const": "session.created"
2398
+ },
2386
2399
  {
2387
2400
  "const": "session.deleted"
2388
2401
  },
@@ -0,0 +1,45 @@
1
+ """Bundle the canonical contracts and the release tag of the matching Vis engine."""
2
+
3
+ import os
4
+ import re
5
+ import tempfile
6
+ from pathlib import Path
7
+
8
+ from hatchling.builders.hooks.plugin.interface import BuildHookInterface
9
+
10
+ # A beta SDK (X.Y.Z.devN) cannot name its engine from its version, so CI records it.
11
+ _TAG = re.compile(r"^(v\d+\.\d+\.\d+|beta-[0-9a-f]{40})$")
12
+
13
+
14
+ class ContractResources(BuildHookInterface):
15
+ def initialize(self, version, build_data):
16
+ root = Path(self.root)
17
+ source = root / "contracts"
18
+ if not source.is_dir():
19
+ source = root.parent / "vis-contract/resources/vis-contract"
20
+ if not source.is_dir():
21
+ raise FileNotFoundError("canonical Vis contract resources are missing")
22
+ destination = (
23
+ "blockether/vis/_data" if self.target_name == "wheel" else "contracts"
24
+ )
25
+ build_data["force_include"][str(source)] = destination
26
+ self._release_tag(root, build_data)
27
+
28
+ def _release_tag(self, root, build_data):
29
+ """Record VIS_SDK_RELEASE_TAG, or the tag of the sdist this wheel comes from."""
30
+ tag = os.environ.get("VIS_SDK_RELEASE_TAG", "").strip()
31
+ recorded = root / "release-tag"
32
+ if not tag and recorded.is_file():
33
+ tag = recorded.read_text(encoding="utf-8").strip()
34
+ if not tag:
35
+ return
36
+ if not _TAG.match(tag):
37
+ raise ValueError(f"VIS_SDK_RELEASE_TAG={tag!r} is not vX.Y.Z or beta-<sha>")
38
+ written = Path(tempfile.mkdtemp(prefix="vis-sdk-tag-")) / "release-tag"
39
+ written.write_text(f"{tag}\n", encoding="utf-8")
40
+ destination = (
41
+ "blockether/vis/engine/release-tag"
42
+ if self.target_name == "wheel"
43
+ else "release-tag"
44
+ )
45
+ build_data["force_include"][str(written)] = destination
@@ -8,7 +8,7 @@ build-backend = "hatchling.build"
8
8
 
9
9
  [project]
10
10
  name = "vis-agent"
11
- version = "0.2.47.dev7678"
11
+ version = "0.2.48.dev7720"
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-d22c87384575cfb0253119e1ad26ab78060d0305
@@ -33,6 +33,7 @@ from ._council import (
33
33
  CouncilSource,
34
34
  CouncilThread,
35
35
  )
36
+ from ._install import InstallError, ensure_installed, install
36
37
  from ._local import LocalEngine
37
38
 
38
39
  __all__ = [
@@ -51,6 +52,7 @@ __all__ = [
51
52
  "ExecutionLayer",
52
53
  "GatewayClient",
53
54
  "GatewayError",
55
+ "InstallError",
54
56
  "JobEvent",
55
57
  "JobEvents",
56
58
  "JSONValue",
@@ -63,4 +65,6 @@ __all__ = [
63
65
  "TransportError",
64
66
  "Turn",
65
67
  "VisTimeout",
68
+ "ensure_installed",
69
+ "install",
66
70
  ]
@@ -2183,6 +2183,24 @@ class ExecutionLayer(ABC):
2183
2183
  )
2184
2184
  return response.json()
2185
2185
 
2186
+ def post_sessions_archive(
2187
+ self,
2188
+ *,
2189
+ query: Query | None = None,
2190
+ timeout: float | None = None,
2191
+ body: JSONValue = None,
2192
+ ) -> JSONValue:
2193
+ """POST /v1/sessions/actions/archive — archive many sessions; skip working ones."""
2194
+ response = self._request(
2195
+ "POST",
2196
+ "/v1/sessions/actions/archive",
2197
+ path={},
2198
+ query=query,
2199
+ timeout=timeout,
2200
+ body=body,
2201
+ )
2202
+ return response.json()
2203
+
2186
2204
  def get_sessions_search(
2187
2205
  self, *, query: Query | None = None, timeout: float | None = None
2188
2206
  ) -> JSONValue:
@@ -0,0 +1,315 @@
1
+ """Install the Vis engine that matches this SDK.
2
+
3
+ `ensure_installed()` returns the `vis-agent` command for this SDK. It uses an
4
+ installed Vis only when that build is the same release as the SDK. Otherwise it
5
+ downloads the release once into `$VIS_HOME/sdk/<tag>`. `install()` makes that
6
+ download now, for example while you build a container image.
7
+
8
+ The download is the engine bundle of one platform: the `vis-agent` launcher, the
9
+ native engine and its Python runtime. It has no terminal client, web app or desktop
10
+ app. The SDK checks the archive against the release `SHA256SUMS` and the build stamp
11
+ against the release tag. It does not change shell profiles, the installed track or a
12
+ running gateway. Set `VIS_NO_AUTO_INSTALL=1` to turn off the automatic download.
13
+ """
14
+
15
+ from __future__ import annotations
16
+
17
+ import fcntl
18
+ import hashlib
19
+ import importlib.metadata
20
+ import importlib.resources
21
+ import json
22
+ import os
23
+ import platform
24
+ import re
25
+ import shutil
26
+ import sys
27
+ import tarfile
28
+ import tempfile
29
+ import urllib.error
30
+ import urllib.request
31
+ from pathlib import Path
32
+
33
+ _COMMAND = "vis-agent"
34
+ _NATIVE = "vis-agent-native"
35
+ # A launcher with this file beside it always runs the native engine beside it.
36
+ _PINNED = "vis-agent.pinned"
37
+ _RELEASE = re.compile(r"^\d+\.\d+\.\d+$")
38
+ _STABLE = re.compile(r"^v\d+\.\d+\.\d+$")
39
+ _TAG = re.compile(r"^(v\d+\.\d+\.\d+|beta-[0-9a-f]{40})$")
40
+ _OFF = {"1", "true", "yes", "on"}
41
+ _CHUNK = 1 << 20
42
+ # Each network read must progress within this time; the engine bundle is large.
43
+ _READ_TIMEOUT = 120
44
+
45
+
46
+ class InstallError(RuntimeError):
47
+ """The SDK could not find or download the Vis engine."""
48
+
49
+
50
+ def _repository() -> str:
51
+ return os.environ.get("VIS_REPO_SLUG") or "Blockether/vis"
52
+
53
+
54
+ def _vis_home() -> Path:
55
+ return Path(os.environ.get("VIS_HOME") or Path.home() / ".vis")
56
+
57
+
58
+ def _cache_root() -> Path:
59
+ return _vis_home() / "sdk"
60
+
61
+
62
+ def _embedded_tag() -> str | None:
63
+ """Return the release tag that the wheel build recorded, if any."""
64
+ try:
65
+ text = (
66
+ importlib.resources.files("blockether.vis.engine")
67
+ .joinpath("release-tag")
68
+ .read_text(encoding="utf-8")
69
+ )
70
+ except (OSError, ModuleNotFoundError):
71
+ return None
72
+ tag = text.strip()
73
+ return tag if _TAG.match(tag) else None
74
+
75
+
76
+ def _editable(distribution: importlib.metadata.Distribution) -> bool:
77
+ try:
78
+ url = json.loads(distribution.read_text("direct_url.json") or "{}")
79
+ except ValueError:
80
+ return False
81
+ return bool(url.get("dir_info", {}).get("editable"))
82
+
83
+
84
+ def _sdk_tag() -> str | None:
85
+ """Return the release tag of this SDK, or None for a development build.
86
+
87
+ A release SDK `X.Y.Z` uses `vX.Y.Z`. A beta SDK `X.Y.Z.devN` uses the
88
+ `beta-<sha>` tag that its wheel build recorded. An editable checkout is a
89
+ development build.
90
+ """
91
+ try:
92
+ distribution = importlib.metadata.distribution("vis-agent")
93
+ except importlib.metadata.PackageNotFoundError:
94
+ return None
95
+ if _editable(distribution):
96
+ return None
97
+ version = distribution.version
98
+ if _RELEASE.match(version):
99
+ return f"v{version}"
100
+ return _embedded_tag()
101
+
102
+
103
+ def _open(url: str):
104
+ return urllib.request.urlopen(url, timeout=_READ_TIMEOUT)
105
+
106
+
107
+ def _fetch(url: str) -> bytes:
108
+ with _open(url) as response:
109
+ return response.read()
110
+
111
+
112
+ def _fetch_file(url: str, target: Path) -> str:
113
+ """Stream `url` into `target` and return its SHA-256."""
114
+ digest = hashlib.sha256()
115
+ with _open(url) as response, open(target, "wb") as output:
116
+ while chunk := response.read(_CHUNK):
117
+ digest.update(chunk)
118
+ output.write(chunk)
119
+ return digest.hexdigest()
120
+
121
+
122
+ def _latest_tag() -> str:
123
+ url = f"https://github.com/{_repository()}/releases/latest"
124
+ with _open(url) as response:
125
+ tag = response.geturl().rstrip("/").rsplit("/", 1)[-1]
126
+ if not _STABLE.match(tag):
127
+ raise InstallError(f"could not find the latest Vis release at {url}")
128
+ return tag
129
+
130
+
131
+ def _expected_hash(sums: bytes, name: str) -> str:
132
+ for line in sums.decode("utf-8", "replace").splitlines():
133
+ parts = line.split()
134
+ if len(parts) == 2 and parts[1].lstrip("*") == name:
135
+ return parts[0].lower()
136
+ raise InstallError(f"SHA256SUMS has no entry for {name}")
137
+
138
+
139
+ def _platform_asset() -> str:
140
+ """Return the engine bundle name for this computer."""
141
+ machine = platform.machine().lower()
142
+ if sys.platform == "darwin" and machine == "arm64":
143
+ return "vis-agent-macos-arm64.tar.gz"
144
+ if sys.platform == "linux" and machine in ("x86_64", "amd64"):
145
+ return "vis-agent-linux-x64.tar.gz"
146
+ if sys.platform == "linux" and machine in ("aarch64", "arm64"):
147
+ return "vis-agent-linux-arm64.tar.gz"
148
+ raise InstallError(
149
+ f"Vis has no engine for {sys.platform} {machine}. "
150
+ "Use Linux (x64 or arm64) or macOS on Apple silicon."
151
+ )
152
+
153
+
154
+ def _stamp_matches(stamp: Path, tag: str) -> bool:
155
+ """True when the build stamp `version commit track …` is the build of `tag`."""
156
+ try:
157
+ fields = stamp.read_text(encoding="utf-8").split()
158
+ except OSError:
159
+ return False
160
+ if len(fields) < 3:
161
+ return False
162
+ version, commit, track = fields[:3]
163
+ if tag.startswith("beta-"):
164
+ return track == "beta" and commit == tag.removeprefix("beta-")
165
+ return track == "release" and version == tag.removeprefix("v")
166
+
167
+
168
+ def _executable(path: Path) -> bool:
169
+ return path.is_file() and os.access(path, os.X_OK)
170
+
171
+
172
+ def _complete(directory: Path, tag: str) -> bool:
173
+ return (
174
+ _executable(directory / _COMMAND)
175
+ and _executable(directory / _NATIVE)
176
+ and (directory / _PINNED).is_file()
177
+ and (directory / "vis-agent-python" / "python").is_dir()
178
+ and _stamp_matches(directory / f"{_NATIVE}.build", tag)
179
+ )
180
+
181
+
182
+ def _installed() -> str | None:
183
+ found = shutil.which(_COMMAND)
184
+ if found:
185
+ return found
186
+ candidate = Path.home() / ".local/bin" / _COMMAND
187
+ return str(candidate) if _executable(candidate) else None
188
+
189
+
190
+ def _installed_matches(command: str, tag: str) -> bool:
191
+ """True when the launcher `command` runs the native build of `tag`.
192
+
193
+ This follows the launcher: a pinned bundle runs the engine beside it, the `dev`
194
+ track runs source, and otherwise the engine beside the launcher wins over the
195
+ one in `$VIS_HOME/install`.
196
+ """
197
+ bin_dir = Path(command).resolve().parent
198
+ install_dir = _vis_home() / "install"
199
+ bundled = bin_dir / _NATIVE
200
+ if not ((bin_dir / _PINNED).is_file() and _executable(bundled)):
201
+ try:
202
+ track = (install_dir / "track").read_text(encoding="utf-8").strip()
203
+ except OSError:
204
+ track = ""
205
+ if track == "dev":
206
+ return False
207
+ for native in (bundled, install_dir / _NATIVE):
208
+ if _executable(native):
209
+ return _stamp_matches(native.with_name(f"{_NATIVE}.build"), tag)
210
+ return False
211
+
212
+
213
+ def _check_bundle(directory: Path, tag: str) -> None:
214
+ missing = [
215
+ name for name in (_COMMAND, _NATIVE) if not _executable(directory / name)
216
+ ]
217
+ python = directory / "vis-agent-python"
218
+ if not (python / "python").is_dir() or not list(python.glob("libvispython.*")):
219
+ missing.append("vis-agent-python")
220
+ if missing:
221
+ raise InstallError(f"the Vis {tag} bundle has no {', '.join(missing)}")
222
+ if not _stamp_matches(directory / f"{_NATIVE}.build", tag):
223
+ raise InstallError(
224
+ f"the Vis {tag} bundle has the build stamp of another release"
225
+ )
226
+
227
+
228
+ def _download(tag: str, directory: Path) -> None:
229
+ """Download, check and unpack the engine bundle of `tag` into `directory`."""
230
+ asset = _platform_asset()
231
+ base = f"https://github.com/{_repository()}/releases/download/{tag}"
232
+ expected = _expected_hash(_fetch(f"{base}/SHA256SUMS"), asset)
233
+ with tempfile.TemporaryDirectory(dir=directory.parent, prefix=f".{tag}-") as work:
234
+ archive = Path(work) / asset
235
+ if _fetch_file(f"{base}/{asset}", archive) != expected:
236
+ raise InstallError(f"the {asset} of {tag} does not match SHA256SUMS")
237
+ bundle = Path(work) / "bundle"
238
+ with tarfile.open(archive) as tar:
239
+ tar.extractall(bundle, filter="data")
240
+ _check_bundle(bundle, tag)
241
+ (bundle / _PINNED).write_text(f"{tag}\n", encoding="utf-8")
242
+ if directory.exists():
243
+ shutil.rmtree(directory)
244
+ bundle.replace(directory)
245
+
246
+
247
+ def _download_once(tag: str, *, force: bool = False) -> str:
248
+ root = _cache_root()
249
+ directory = root / tag
250
+ root.mkdir(parents=True, exist_ok=True)
251
+ with open(root / f"{tag}.lock", "w") as lock:
252
+ # A second process waits here and then uses the finished download.
253
+ fcntl.flock(lock, fcntl.LOCK_EX)
254
+ if force or not _complete(directory, tag):
255
+ print(f"vis-agent: downloading Vis {tag} into {directory}", file=sys.stderr)
256
+ try:
257
+ _download(tag, directory)
258
+ except urllib.error.HTTPError as error:
259
+ if error.code == 404:
260
+ raise InstallError(
261
+ f"Vis {tag} has no engine at {error.url}. "
262
+ "Install the SDK version of a published Vis release."
263
+ ) from None
264
+ raise InstallError(f"could not download Vis {tag}: {error}") from None
265
+ except (OSError, tarfile.TarError) as error:
266
+ raise InstallError(f"could not download Vis {tag}: {error}") from None
267
+ return str(directory / _COMMAND)
268
+
269
+
270
+ def install(*, force: bool = False) -> str:
271
+ """Download the Vis engine of this SDK now and return its `vis-agent` path.
272
+
273
+ Use it to prepare a computer or a container image before the first run. It
274
+ downloads into `$VIS_HOME/sdk/<tag>` even when another Vis is installed, and
275
+ reuses a complete download unless `force` is true. A development build of the
276
+ SDK installs the latest stable release. `VIS_NO_AUTO_INSTALL` does not apply.
277
+
278
+ Raises:
279
+ InstallError: The platform has no engine, the release has no bundle, or the
280
+ download fails its checks.
281
+ """
282
+ return _download_once(_sdk_tag() or _latest_tag(), force=force)
283
+
284
+
285
+ def ensure_installed() -> str:
286
+ """Return the `vis-agent` command that matches this SDK, and download it when missing.
287
+
288
+ An installed `vis-agent` is used only when it runs the same release as the SDK.
289
+ Otherwise the SDK uses, or downloads, its own copy in `$VIS_HOME/sdk/<tag>`. A
290
+ development build of the SDK uses any installed command, or the latest stable
291
+ release.
292
+
293
+ Raises:
294
+ InstallError: The download is turned off with `VIS_NO_AUTO_INSTALL`, or
295
+ `install()` fails.
296
+ """
297
+ tag = _sdk_tag()
298
+ installed = _installed()
299
+ if tag is None and installed:
300
+ return installed
301
+ if tag is not None:
302
+ if installed and _installed_matches(installed, tag):
303
+ return installed
304
+ if _complete(_cache_root() / tag, tag):
305
+ return str(_cache_root() / tag / _COMMAND)
306
+ if (os.environ.get("VIS_NO_AUTO_INSTALL") or "").strip().lower() in _OFF:
307
+ wanted = f"Vis {tag}" if tag else f"the {_COMMAND} command"
308
+ raise InstallError(
309
+ f"{wanted} is not installed, and VIS_NO_AUTO_INSTALL turns off the "
310
+ "download. Install it, call install(), or pass executable=..."
311
+ )
312
+ try:
313
+ return install()
314
+ except OSError as error:
315
+ raise InstallError(f"could not download Vis: {error}") from None
@@ -1,8 +1,9 @@
1
1
  """Owned local Vis subprocess, with the same session API and no HTTP listener.
2
2
 
3
- You select the executable explicitly. There is no download, gateway discovery or
4
- user-server shutdown. Each engine gets a temporary database and stderr log, and closing
5
- discards them.
3
+ Pass an executable, or keep the default `vis-agent`. The default runs the engine of
4
+ this SDK's release (see `ensure_installed`). There is no gateway discovery or
5
+ user-server shutdown. Each engine gets a temporary database and stderr log, and
6
+ closing discards them.
6
7
  Supports Linux and macOS. Instances use one calling thread, like GatewayClient.
7
8
  """
8
9
 
@@ -34,6 +35,7 @@ from ._client import (
34
35
  _PollingEvents,
35
36
  _SessionPollingEvents,
36
37
  )
38
+ from ._install import _COMMAND, InstallError, ensure_installed
37
39
 
38
40
  # Errors quote at most this much of the engine's stderr, starting at a line.
39
41
  _STDERR_TAIL = 4096
@@ -52,9 +54,12 @@ class LocalEngine(ExecutionLayer):
52
54
  """Own a private Vis process without starting an HTTP server.
53
55
 
54
56
  Use this layer when you need to choose an executable, share one local process
55
- between agents, or call the session API directly. The Vis executable must be
56
- installed separately. This class never downloads it or discovers a gateway.
57
- Supported platforms are Linux and macOS.
57
+ between agents, or call the session API directly. With the default `vis-agent`,
58
+ `connect` uses the installed command when it runs this SDK's release. Otherwise it
59
+ downloads that release once into `$VIS_HOME/sdk` and checks it against the release
60
+ checksums.
61
+ Set `VIS_NO_AUTO_INSTALL=1` to turn off the download. This class never discovers
62
+ a gateway. Supported platforms are Linux and macOS.
58
63
 
59
64
  Args:
60
65
  executable: Executable path/name or nonempty argument sequence. The SDK
@@ -160,6 +165,12 @@ class LocalEngine(ExecutionLayer):
160
165
  raise TransportError("client is closed")
161
166
  if self._process is not None:
162
167
  return self
168
+ if self._command == [_COMMAND]:
169
+ try:
170
+ self._command = [ensure_installed()]
171
+ except InstallError as error:
172
+ self.close()
173
+ raise TransportError(f"could not start local engine: {error}") from None
163
174
  self._home = tempfile.TemporaryDirectory(prefix="vis-sdk-")
164
175
  env = {
165
176
  **os.environ,