vis-agent 0.1.43__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.
- vis_agent-0.1.43/.gitignore +106 -0
- vis_agent-0.1.43/LICENSE +190 -0
- vis_agent-0.1.43/PKG-INFO +380 -0
- vis_agent-0.1.43/README.md +360 -0
- vis_agent-0.1.43/contracts/activity.json +50 -0
- vis_agent-0.1.43/contracts/config.json +64 -0
- vis_agent-0.1.43/contracts/content.json +50 -0
- vis_agent-0.1.43/contracts/fixtures/activity-cases.json +2008 -0
- vis_agent-0.1.43/contracts/fixtures/activity.json +53 -0
- vis_agent-0.1.43/contracts/fixtures/view.json +79 -0
- vis_agent-0.1.43/contracts/gateway.json +1331 -0
- vis_agent-0.1.43/contracts/provider.json +51 -0
- vis_agent-0.1.43/contracts/python-host.json +137 -0
- vis_agent-0.1.43/contracts/schema/activity.json +935 -0
- vis_agent-0.1.43/contracts/schema/common.json +43 -0
- vis_agent-0.1.43/contracts/schema/config.json +1264 -0
- vis_agent-0.1.43/contracts/schema/content.json +444 -0
- vis_agent-0.1.43/contracts/schema/gateway.json +509 -0
- vis_agent-0.1.43/contracts/schema/provider.json +254 -0
- vis_agent-0.1.43/contracts/schema/python-host.json +126 -0
- vis_agent-0.1.43/contracts/schema/surface.json +484 -0
- vis_agent-0.1.43/contracts/schema/test-runner.json +193 -0
- vis_agent-0.1.43/contracts/schema/toggle.json +126 -0
- vis_agent-0.1.43/contracts/schema/view.json +2513 -0
- vis_agent-0.1.43/contracts/surface.json +8 -0
- vis_agent-0.1.43/contracts/test-runner.json +23 -0
- vis_agent-0.1.43/contracts/toggle.json +30 -0
- vis_agent-0.1.43/contracts/view.json +119 -0
- vis_agent-0.1.43/hatch_build.py +19 -0
- vis_agent-0.1.43/pyproject.toml +40 -0
- vis_agent-0.1.43/src/blockether/vis/__init__.py +6 -0
- vis_agent-0.1.43/src/blockether/vis/_contracts.py +142 -0
- vis_agent-0.1.43/src/blockether/vis/_outside.py +1099 -0
- vis_agent-0.1.43/src/blockether/vis/_wire.py +28 -0
- vis_agent-0.1.43/src/blockether/vis/activity.py +185 -0
- vis_agent-0.1.43/src/blockether/vis/engine/__init__.py +41 -0
- vis_agent-0.1.43/src/blockether/vis/engine/_client.py +2420 -0
- vis_agent-0.1.43/src/blockether/vis/engine/_local.py +258 -0
- vis_agent-0.1.43/src/blockether/vis/extension.py +3018 -0
- vis_agent-0.1.43/src/blockether/vis/py.typed +0 -0
- vis_agent-0.1.43/src/blockether/vis/views.py +154 -0
- vis_agent-0.1.43/tests/conftest.py +26 -0
- vis_agent-0.1.43/tests/test_activity.py +171 -0
- vis_agent-0.1.43/tests/test_client.py +577 -0
- vis_agent-0.1.43/tests/test_contracts.py +289 -0
- vis_agent-0.1.43/tests/test_declarations.py +251 -0
- vis_agent-0.1.43/tests/test_engine.py +389 -0
- vis_agent-0.1.43/tests/test_local.py +241 -0
- vis_agent-0.1.43/tests/test_namespace.py +111 -0
- vis_agent-0.1.43/tests/test_outside.py +632 -0
- vis_agent-0.1.43/tests/test_providers.py +323 -0
- vis_agent-0.1.43/tests/test_registration.py +132 -0
- vis_agent-0.1.43/tests/test_testing.py +136 -0
- vis_agent-0.1.43/tests/test_views.py +164 -0
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
# Build / cache - all globs are leading-slash-free on purpose so they
|
|
2
|
+
# match at any depth (root, packages/*, extensions/*, docs/, etc.).
|
|
3
|
+
.cpcache/
|
|
4
|
+
.cache/
|
|
5
|
+
# clj-kondo: the local cache is generated, but config.edn, hooks/ AND the
|
|
6
|
+
# configs imported from dependencies (`imports/`) are the lint rules — without
|
|
7
|
+
# them a fresh checkout resolves no lazytest/malli macro and `-M:lint`
|
|
8
|
+
# fails only on CI. Refresh them with:
|
|
9
|
+
# clojure -M:test:lint -m clj-kondo.main --copy-configs --dependencies \
|
|
10
|
+
# --lint "$(clojure -Spath -M:test)"
|
|
11
|
+
.clj-kondo/.cache/
|
|
12
|
+
.clj-kondo/inline-configs/
|
|
13
|
+
.lsp/
|
|
14
|
+
.shadow-cljs/
|
|
15
|
+
target/
|
|
16
|
+
.pi/extensions/*/node_modules/
|
|
17
|
+
node_modules/
|
|
18
|
+
.expo/
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
# IDE
|
|
22
|
+
.idea/
|
|
23
|
+
*.iml
|
|
24
|
+
.vscode/
|
|
25
|
+
.calva/
|
|
26
|
+
|
|
27
|
+
# OS
|
|
28
|
+
.DS_Store
|
|
29
|
+
Thumbs.db
|
|
30
|
+
|
|
31
|
+
# Project-specific docs (not tracked)
|
|
32
|
+
GOALS.md
|
|
33
|
+
HERMES_ARCHITECTURE.md
|
|
34
|
+
LANTERNA_3X_API_REFERENCE.md
|
|
35
|
+
|
|
36
|
+
# Test data (large / binary)
|
|
37
|
+
schema-therapy.pdf
|
|
38
|
+
schema-therapy.pageindex/
|
|
39
|
+
# Screen recordings / large media scratch — too big for GitHub (>100 MB),
|
|
40
|
+
# and `git add -A` should never stage them. Use Git LFS if any must be tracked.
|
|
41
|
+
*.mp4
|
|
42
|
+
*.mov
|
|
43
|
+
siema.mp4
|
|
44
|
+
|
|
45
|
+
# Bench trajectories / results / harness output (large, generated).
|
|
46
|
+
# Datasets under packages/vis-benchmark/data/ ARE tracked.
|
|
47
|
+
packages/vis-benchmark/results/
|
|
48
|
+
packages/vis-benchmark/trajectories/
|
|
49
|
+
packages/vis-benchmark/swebench-harness/
|
|
50
|
+
|
|
51
|
+
# Sessions / runtime data
|
|
52
|
+
.omc/
|
|
53
|
+
.zcode/
|
|
54
|
+
.nrepl-port
|
|
55
|
+
.nrepl-session
|
|
56
|
+
.nrepl.log
|
|
57
|
+
.nrepl.boot.log
|
|
58
|
+
.nrepl.pid
|
|
59
|
+
# The root docs/ folder is LOCAL SCRATCH only (e.g. mdbook-mermaid assets,
|
|
60
|
+
# regenerable via `mdbook-mermaid install docs`). Canonical documentation is
|
|
61
|
+
# tracked under resources/vis-docs/ — never the root docs/ tree. Leading slash
|
|
62
|
+
# scopes this to the repo root, so extension `*/docs/` dirs stay tracked.
|
|
63
|
+
/docs/
|
|
64
|
+
|
|
65
|
+
jobs/
|
|
66
|
+
# Legacy local verification logs may still exist after removing verify.sh.
|
|
67
|
+
.verification/
|
|
68
|
+
# rift/bridge per-directory ULID marker — regenerated at runtime, never source.
|
|
69
|
+
.rift
|
|
70
|
+
# Claude Code local tooling state (machine-local settings + run locks).
|
|
71
|
+
.claude/
|
|
72
|
+
hs_err_pid*.log
|
|
73
|
+
|
|
74
|
+
# python bytecode
|
|
75
|
+
__pycache__/
|
|
76
|
+
*.pyc
|
|
77
|
+
|
|
78
|
+
# transient dev logs
|
|
79
|
+
.docs-dev.log
|
|
80
|
+
|
|
81
|
+
# transient slack dump / agent scratch (never commit)
|
|
82
|
+
igor-dump/
|
|
83
|
+
channels-*.csv
|
|
84
|
+
.todos/
|
|
85
|
+
.sidecar/
|
|
86
|
+
.sidecar-agent
|
|
87
|
+
.sidecar-task
|
|
88
|
+
.sidecar-pr
|
|
89
|
+
.sidecar-start.sh
|
|
90
|
+
.sidecar-base
|
|
91
|
+
.td-root
|
|
92
|
+
|
|
93
|
+
# local git worktrees (CI native-build probes, etc.)
|
|
94
|
+
.gitworktrees/
|
|
95
|
+
|
|
96
|
+
# local talk deck — source pptx + generated HTML presentation (never committed)
|
|
97
|
+
own_your_harness.pptx
|
|
98
|
+
/presentation/
|
|
99
|
+
|
|
100
|
+
# Cloudflare Wrangler local dev state (KV/D1/DO simulators, cached account info).
|
|
101
|
+
# Glob is leading-slash-free so it also covers apps/vis-companion-relay/.wrangler/;
|
|
102
|
+
# the tracked wrangler.jsonc config is unaffected.
|
|
103
|
+
.wrangler/
|
|
104
|
+
|
|
105
|
+
# Voice assets built by bin/export-pocket-tts — release payloads, never sources.
|
|
106
|
+
/dist/
|
vis_agent-0.1.43/LICENSE
ADDED
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
Apache License
|
|
2
|
+
Version 2.0, January 2004
|
|
3
|
+
http://www.apache.org/licenses/
|
|
4
|
+
|
|
5
|
+
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
|
6
|
+
|
|
7
|
+
1. Definitions.
|
|
8
|
+
|
|
9
|
+
"License" shall mean the terms and conditions for use, reproduction,
|
|
10
|
+
and distribution as defined by Sections 1 through 9 of this document.
|
|
11
|
+
|
|
12
|
+
"Licensor" shall mean the copyright owner or entity authorized by
|
|
13
|
+
the copyright owner that is granting the License.
|
|
14
|
+
|
|
15
|
+
"Legal Entity" shall mean the union of the acting entity and all
|
|
16
|
+
other entities that control, are controlled by, or are under common
|
|
17
|
+
control with that entity. For the purposes of this definition,
|
|
18
|
+
"control" means (i) the power, direct or indirect, to cause the
|
|
19
|
+
direction or management of such entity, whether by contract or
|
|
20
|
+
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
|
21
|
+
outstanding shares, or (iii) beneficial ownership of such entity.
|
|
22
|
+
|
|
23
|
+
"You" (or "Your") shall mean an individual or Legal Entity
|
|
24
|
+
exercising permissions granted by this License.
|
|
25
|
+
|
|
26
|
+
"Source" form shall mean the preferred form for making modifications,
|
|
27
|
+
including but not limited to software source code, documentation
|
|
28
|
+
source, and configuration files.
|
|
29
|
+
|
|
30
|
+
"Object" form shall mean any form resulting from mechanical
|
|
31
|
+
transformation or translation of a Source form, including but
|
|
32
|
+
not limited to compiled object code, generated documentation,
|
|
33
|
+
and conversions to other media types.
|
|
34
|
+
|
|
35
|
+
"Work" shall mean the work of authorship, whether in Source or
|
|
36
|
+
Object form, made available under the License, as indicated by a
|
|
37
|
+
copyright notice that is included in or attached to the work
|
|
38
|
+
(an example is provided in the Appendix below).
|
|
39
|
+
|
|
40
|
+
"Derivative Works" shall mean any work, whether in Source or Object
|
|
41
|
+
form, that is based on (or derived from) the Work and for which the
|
|
42
|
+
editorial revisions, annotations, elaborations, or other modifications
|
|
43
|
+
represent, as a whole, an original work of authorship. For the purposes
|
|
44
|
+
of this License, Derivative Works shall not include works that remain
|
|
45
|
+
separable from, or merely link (or bind by name) to the interfaces of,
|
|
46
|
+
the Work and Derivative Works thereof.
|
|
47
|
+
|
|
48
|
+
"Contribution" shall mean any work of authorship, including
|
|
49
|
+
the original version of the Work and any modifications or additions
|
|
50
|
+
to that Work or Derivative Works thereof, that is intentionally
|
|
51
|
+
submitted to the Licensor for inclusion in the Work by the copyright owner
|
|
52
|
+
or by an individual or Legal Entity authorized to submit on behalf of
|
|
53
|
+
the copyright owner. For the purposes of this definition, "submitted"
|
|
54
|
+
means any form of electronic, verbal, or written communication sent
|
|
55
|
+
to the Licensor or its representatives, including but not limited to
|
|
56
|
+
communication on electronic mailing lists, source code control systems,
|
|
57
|
+
and issue tracking systems that are managed by, or on behalf of, the
|
|
58
|
+
Licensor for the purpose of discussing and improving the Work, but
|
|
59
|
+
excluding communication that is conspicuously marked or otherwise
|
|
60
|
+
designated in writing by the copyright owner as "Not a Contribution."
|
|
61
|
+
|
|
62
|
+
"Contributor" shall mean Licensor and any individual or Legal Entity
|
|
63
|
+
on behalf of whom a Contribution has been received by the Licensor and
|
|
64
|
+
subsequently incorporated within the Work.
|
|
65
|
+
|
|
66
|
+
2. Grant of Copyright License. Subject to the terms and conditions of
|
|
67
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
68
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
69
|
+
copyright license to reproduce, prepare Derivative Works of,
|
|
70
|
+
publicly display, publicly perform, sublicense, and distribute the
|
|
71
|
+
Work and such Derivative Works in Source or Object form.
|
|
72
|
+
|
|
73
|
+
3. Grant of Patent License. Subject to the terms and conditions of
|
|
74
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
75
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
76
|
+
(except as stated in this section) patent license to make, have made,
|
|
77
|
+
use, offer to sell, sell, import, and otherwise transfer the Work,
|
|
78
|
+
where such license applies only to those patent claims licensable
|
|
79
|
+
by such Contributor that are necessarily infringed by their
|
|
80
|
+
Contribution(s) alone or by combination of their Contribution(s)
|
|
81
|
+
with the Work to which such Contribution(s) was submitted. If You
|
|
82
|
+
institute patent litigation against any entity (including a
|
|
83
|
+
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
|
84
|
+
or a Contribution incorporated within the Work constitutes direct
|
|
85
|
+
or contributory patent infringement, then any patent licenses
|
|
86
|
+
granted to You under this License for that Work shall terminate
|
|
87
|
+
as of the date such litigation is filed.
|
|
88
|
+
|
|
89
|
+
4. Redistribution. You may reproduce and distribute copies of the
|
|
90
|
+
Work or Derivative Works thereof in any medium, with or without
|
|
91
|
+
modifications, and in Source or Object form, provided that You
|
|
92
|
+
meet the following conditions:
|
|
93
|
+
|
|
94
|
+
(a) You must give any other recipients of the Work or
|
|
95
|
+
Derivative Works a copy of this License; and
|
|
96
|
+
|
|
97
|
+
(b) You must cause any modified files to carry prominent notices
|
|
98
|
+
stating that You changed the files; and
|
|
99
|
+
|
|
100
|
+
(c) You must retain, in the Source form of any Derivative Works
|
|
101
|
+
that You distribute, all copyright, patent, trademark, and
|
|
102
|
+
attribution notices from the Source form of the Work,
|
|
103
|
+
excluding those notices that do not pertain to any part of
|
|
104
|
+
the Derivative Works; and
|
|
105
|
+
|
|
106
|
+
(d) If the Work includes a "NOTICE" text file as part of its
|
|
107
|
+
distribution, then any Derivative Works that You distribute must
|
|
108
|
+
include a readable copy of the attribution notices contained
|
|
109
|
+
within such NOTICE file, excluding any notices that do not
|
|
110
|
+
pertain to any part of the Derivative Works, in at least one
|
|
111
|
+
of the following places: within a NOTICE text file distributed
|
|
112
|
+
as part of the Derivative Works; within the Source form or
|
|
113
|
+
documentation, if provided along with the Derivative Works; or,
|
|
114
|
+
within a display generated by the Derivative Works, if and
|
|
115
|
+
wherever such third-party notices normally appear. The contents
|
|
116
|
+
of the NOTICE file are for informational purposes only and
|
|
117
|
+
do not modify the License. You may add Your own attribution
|
|
118
|
+
notices within Derivative Works that You distribute, alongside
|
|
119
|
+
or as an addendum to the NOTICE text from the Work, provided
|
|
120
|
+
that such additional attribution notices cannot be construed
|
|
121
|
+
as modifying the License.
|
|
122
|
+
|
|
123
|
+
You may add Your own copyright statement to Your modifications and
|
|
124
|
+
may provide additional or different license terms and conditions
|
|
125
|
+
for use, reproduction, or distribution of Your modifications, or
|
|
126
|
+
for any such Derivative Works as a whole, provided Your use,
|
|
127
|
+
reproduction, and distribution of the Work otherwise complies with
|
|
128
|
+
the conditions stated in this License.
|
|
129
|
+
|
|
130
|
+
5. Submission of Contributions. Unless You explicitly state otherwise,
|
|
131
|
+
any Contribution intentionally submitted for inclusion in the Work
|
|
132
|
+
by You to the Licensor shall be under the terms and conditions of
|
|
133
|
+
this License, without any additional terms or conditions.
|
|
134
|
+
Notwithstanding the above, nothing herein shall supersede or modify
|
|
135
|
+
the terms of any separate license agreement you may have executed
|
|
136
|
+
with Licensor regarding such Contributions.
|
|
137
|
+
|
|
138
|
+
6. Trademarks. This License does not grant permission to use the trade
|
|
139
|
+
names, trademarks, service marks, or product names of the Licensor,
|
|
140
|
+
except as required for reasonable and customary use in describing the
|
|
141
|
+
origin of the Work and reproducing the content of the NOTICE file.
|
|
142
|
+
|
|
143
|
+
7. Disclaimer of Warranty. Unless required by applicable law or
|
|
144
|
+
agreed to in writing, Licensor provides the Work (and each
|
|
145
|
+
Contributor provides its Contributions) on an "AS IS" BASIS,
|
|
146
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
|
147
|
+
implied, including, without limitation, any warranties or conditions
|
|
148
|
+
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
|
149
|
+
PARTICULAR PURPOSE. You are solely responsible for determining the
|
|
150
|
+
appropriateness of using or redistributing the Work and assume any
|
|
151
|
+
risks associated with Your exercise of permissions under this License.
|
|
152
|
+
|
|
153
|
+
8. Limitation of Liability. In no event and under no legal theory,
|
|
154
|
+
whether in tort (including negligence), contract, or otherwise,
|
|
155
|
+
unless required by applicable law (such as deliberate and grossly
|
|
156
|
+
negligent acts) or agreed to in writing, shall any Contributor be
|
|
157
|
+
liable to You for damages, including any direct, indirect, special,
|
|
158
|
+
incidental, or consequential damages of any character arising as a
|
|
159
|
+
result of this License or out of the use or inability to use the
|
|
160
|
+
Work (including but not limited to damages for loss of goodwill,
|
|
161
|
+
work stoppage, computer failure or malfunction, or any and all
|
|
162
|
+
other commercial damages or losses), even if such Contributor
|
|
163
|
+
has been advised of the possibility of such damages.
|
|
164
|
+
|
|
165
|
+
9. Accepting Warranty or Additional Liability. While redistributing
|
|
166
|
+
the Work or Derivative Works thereof, You may choose to offer,
|
|
167
|
+
and charge a fee for, acceptance of support, warranty, indemnity,
|
|
168
|
+
or other liability obligations and/or rights consistent with this
|
|
169
|
+
License. However, in accepting such obligations, You may act only
|
|
170
|
+
on Your own behalf and on Your sole responsibility, not on behalf
|
|
171
|
+
of any other Contributor, and only if You agree to indemnify,
|
|
172
|
+
defend, and hold each Contributor harmless for any liability
|
|
173
|
+
incurred by, or claims asserted against, such Contributor by reason
|
|
174
|
+
of your accepting any such warranty or additional liability.
|
|
175
|
+
|
|
176
|
+
END OF TERMS AND CONDITIONS
|
|
177
|
+
|
|
178
|
+
Copyright 2025-2026 Blockether
|
|
179
|
+
|
|
180
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
181
|
+
you may not use this file except in compliance with the License.
|
|
182
|
+
You may obtain a copy of the License at
|
|
183
|
+
|
|
184
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
185
|
+
|
|
186
|
+
Unless required by applicable law or agreed to in writing, software
|
|
187
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
188
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
189
|
+
See the License for the specific language governing permissions and
|
|
190
|
+
limitations under the License.
|
|
@@ -0,0 +1,380 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: vis-agent
|
|
3
|
+
Version: 0.1.43
|
|
4
|
+
Summary: The Vis Python SDK — author extensions and control local or remote engines.
|
|
5
|
+
Project-URL: Homepage, https://github.com/Blockether/vis
|
|
6
|
+
Project-URL: Source, https://github.com/Blockether/vis
|
|
7
|
+
Project-URL: Issues, https://github.com/Blockether/vis/issues
|
|
8
|
+
Author: Blockether
|
|
9
|
+
License-Expression: Apache-2.0
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Keywords: agent,automation,extension,human-in-the-loop,vis
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Topic :: Software Development :: Build Tools
|
|
16
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
17
|
+
Requires-Python: >=3.11
|
|
18
|
+
Requires-Dist: jsonschema<5,>=4.23
|
|
19
|
+
Description-Content-Type: text/markdown
|
|
20
|
+
|
|
21
|
+
# vis-agent
|
|
22
|
+
|
|
23
|
+
The Python SDK for [Vis](https://github.com/Blockether/vis): author extensions
|
|
24
|
+
and control local or remote engines.
|
|
25
|
+
|
|
26
|
+
One distribution, `vis-agent`, exposes two entry points: `blockether.vis.extension`
|
|
27
|
+
for extension authors and `blockether.vis.engine` for engine clients. Models live
|
|
28
|
+
in `blockether.vis.views` and `blockether.vis.activity`. The lightweight
|
|
29
|
+
`blockether.vis` root initializes no host and starts no process or connection.
|
|
30
|
+
`blockether` is an implicit PEP 420 namespace; the SDK does not own its `__init__.py`.
|
|
31
|
+
The SDK does not install or alias the unrelated top-level `vis` package.
|
|
32
|
+
|
|
33
|
+
SDK records, including `Response`, `Event`, `Session`, `Turn`, View and Activity
|
|
34
|
+
projections, are frozen, slotted dataclasses. Event envelope fields (`type`,
|
|
35
|
+
`session_id`, `seq`, `cursor`, `turn_id`) are named and validated on both transports.
|
|
36
|
+
`Event.view` and `Event.activity` expose validated records with immutable nested
|
|
37
|
+
collections. `Event.data` holds the remaining dynamic payload and remains mutable;
|
|
38
|
+
frozen records do not promise deep immutability for arbitrary endpoint data.
|
|
39
|
+
Extension tools can return frozen/slotted dataclasses; the runtime serializes
|
|
40
|
+
their declared fields, including nested records, without requiring `__dict__`.
|
|
41
|
+
The SDK ships PEP 561 `py.typed` metadata for its inline annotations.
|
|
42
|
+
The remaining endpoint dictionaries are not yet a fully typed model API.
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
pip install vis-agent
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
```python
|
|
49
|
+
from dataclasses import dataclass
|
|
50
|
+
|
|
51
|
+
import blockether.vis.extension as vis
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
@dataclass(frozen=True, slots=True)
|
|
55
|
+
class Greeting:
|
|
56
|
+
text: str
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def greet(name: str) -> Greeting:
|
|
60
|
+
"""Greet one person and return a typed result."""
|
|
61
|
+
vis.publish_activity(
|
|
62
|
+
vis.ActivityPresentation(
|
|
63
|
+
"Greeting", "Preparing reply", (vis.ActivityProgress("Working"),)
|
|
64
|
+
)
|
|
65
|
+
)
|
|
66
|
+
return Greeting(f"Hello, {name}!")
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def greeting_activity(phase, result, **_) -> vis.ActivityPresentation:
|
|
70
|
+
return vis.ActivityPresentation(
|
|
71
|
+
"Greeting",
|
|
72
|
+
phase,
|
|
73
|
+
(vis.ActivityText(result.text if phase == "success" else "Preparing reply"),),
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
vis.register(
|
|
78
|
+
vis.Extension(
|
|
79
|
+
name="greeter",
|
|
80
|
+
description="Greeting tools.",
|
|
81
|
+
alias="greeter",
|
|
82
|
+
symbols=[vis.Symbol(greet, activity=vis.Activity(render=greeting_activity))],
|
|
83
|
+
)
|
|
84
|
+
)
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
`Extension`, `Symbol`, `SlashCommand`, `OpHook`, `NetworkFilter` and `Provider`
|
|
88
|
+
are frozen, slotted declarations. Constructing them performs no host IO. Only
|
|
89
|
+
`vis.register(extension)` registers the file and resolves its declared environment;
|
|
90
|
+
call it once. Collections are snapshotted, and marker dictionaries are not accepted.
|
|
91
|
+
`vis` is the imported module, not a singleton or an extension base class.
|
|
92
|
+
|
|
93
|
+
| API | Purpose |
|
|
94
|
+
| --- | --- |
|
|
95
|
+
| `log(level, message)` | Diagnostics, not model context. |
|
|
96
|
+
| `notify(text)` | One-way notification to the human. |
|
|
97
|
+
| `ask(title, fields)` | Wait for a typed human-input result. |
|
|
98
|
+
| `Activity` / `publish_activity(presentation)` | This tool invocation's progress and presentation; the engine owns timing and outcome. |
|
|
99
|
+
## Providers
|
|
100
|
+
|
|
101
|
+
Provider presets and callback results use typed records too:
|
|
102
|
+
|
|
103
|
+
```python
|
|
104
|
+
import os
|
|
105
|
+
import blockether.vis.extension as vis
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def credential() -> vis.ProviderCredential | None:
|
|
109
|
+
token = os.environ.get("EXAMPLE_API_KEY")
|
|
110
|
+
return vis.ProviderCredential(token) if token else None
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def status() -> vis.ProviderStatus:
|
|
114
|
+
return vis.ProviderStatus(
|
|
115
|
+
is_authenticated=bool(os.environ.get("EXAMPLE_API_KEY")),
|
|
116
|
+
source="env-var",
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
vis.register(
|
|
121
|
+
vis.Extension(
|
|
122
|
+
name="provider-example",
|
|
123
|
+
description="An OpenAI-compatible provider.",
|
|
124
|
+
env=["EXAMPLE_API_KEY"],
|
|
125
|
+
providers=[
|
|
126
|
+
vis.Provider(
|
|
127
|
+
id="example",
|
|
128
|
+
label="Example AI",
|
|
129
|
+
preset=vis.ProviderPreset(
|
|
130
|
+
base_url="https://gateway.example.com/v1",
|
|
131
|
+
api_style="openai",
|
|
132
|
+
default_models=["example-model"],
|
|
133
|
+
),
|
|
134
|
+
get_token_fn=credential,
|
|
135
|
+
status_fn=status,
|
|
136
|
+
)
|
|
137
|
+
],
|
|
138
|
+
)
|
|
139
|
+
)
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
Load this file as an extension, then add/select `example` in Vis. Credentials are
|
|
143
|
+
read passively; return `None` when absent. Set `is_managed=True` only when the
|
|
144
|
+
extension owns automatic binding/configuration; it does not disable authentication.
|
|
145
|
+
Refresh, login/logout, limits and model enrichment use the same provider boundary.
|
|
146
|
+
See [provider callbacks and limits](../../resources/vis-docs/extending.md#llm-providers)
|
|
147
|
+
for their signatures and typed results. No constructor invokes a callback.
|
|
148
|
+
|
|
149
|
+
## One file, two hosts
|
|
150
|
+
|
|
151
|
+
Host operations are declared in
|
|
152
|
+
[`python-host.json`](https://github.com/Blockether/vis/blob/main/packages/vis-contract/resources/vis-contract/python-host.json)
|
|
153
|
+
and bundled with the other canonical documents in the SDK. Contract reading and
|
|
154
|
+
validation are private implementation details, not a separate Python package.
|
|
155
|
+
|
|
156
|
+
Inside a Vis session the engine seeds those ops and they reach the live agent:
|
|
157
|
+
state is the extension's durable state, `vis.ask` opens a dialog on whichever
|
|
158
|
+
surface the human is using, `vis.shell` runs in the agent's sandbox.
|
|
159
|
+
|
|
160
|
+
Installed in an ordinary Python environment there is no agent, so
|
|
161
|
+
`blockether.vis._outside` supplies the host operations:
|
|
162
|
+
|
|
163
|
+
| op | outside |
|
|
164
|
+
| --- | --- |
|
|
165
|
+
| `state`, `log`, `notify`, `shell`, secrets, `host_env` | done locally — a JSON file under `~/.vis/outside`, stderr lines, a real subprocess, a process-local vault |
|
|
166
|
+
| `ask` | prompted in the TERMINAL: the same field tree, the same validators, the same `Answer` |
|
|
167
|
+
| `jailed_shell`, `jailed_shell_session` | refused by name — a jail is a property of the agent's process boundary, and nothing out here can enforce one |
|
|
168
|
+
|
|
169
|
+
So an extension file imports, type-checks, unit-tests and runs on a laptop or in
|
|
170
|
+
CI, and the code that ships is the code that was tested.
|
|
171
|
+
|
|
172
|
+
## Answering without a human
|
|
173
|
+
|
|
174
|
+
```python
|
|
175
|
+
import blockether.vis.extension as vis
|
|
176
|
+
|
|
177
|
+
vis.outside.answer_with({"env": "staging", "token": "hunter2"})
|
|
178
|
+
answer = vis.ask("Deploy", [vis.select("env", ["staging", "prod"])])
|
|
179
|
+
assert answer["env"] == "staging"
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
`VIS_OUTSIDE_ANSWERS` (a JSON object) primes the same values from the
|
|
183
|
+
environment, and `VIS_OUTSIDE_NONINTERACTIVE=1` makes an unanswerable ask return
|
|
184
|
+
`undeliverable` — exactly what a session with no surface mounted returns — instead
|
|
185
|
+
of blocking a build.
|
|
186
|
+
|
|
187
|
+
Other environment knobs: `VIS_OUTSIDE_HOME` moves the state file and the shell
|
|
188
|
+
logs (default `~/.vis/outside`).
|
|
189
|
+
|
|
190
|
+
## Testing live extensions
|
|
191
|
+
|
|
192
|
+
`vis.testing.LiveRecorder` is the shared in-memory host for extension tests. It
|
|
193
|
+
records extension envelopes without publishing fixture views into a real session,
|
|
194
|
+
materializes open/patch/state/close, and exposes `focus` and `close` for simulated
|
|
195
|
+
surface actions. Provider-specific tests keep only their provider snapshots and
|
|
196
|
+
assertions; `vis.testing.assert_tree` compares terminal view goldens at the exact
|
|
197
|
+
leaf that changed.
|
|
198
|
+
|
|
199
|
+
## View and Activity are different contracts
|
|
200
|
+
|
|
201
|
+
**View** is a human interaction: an input form or a live surface with the existing
|
|
202
|
+
`view.open`, `view.patch` and `view.close` lifecycle. Extension authors keep using
|
|
203
|
+
`vis.ask` and the live builders. Remote and local clients use the same typed
|
|
204
|
+
`InputView`, `LiveView`, `LivePatch`, `InputResult` and `LiveResult` records from
|
|
205
|
+
`blockether.vis.views`. `session.input_views()` and `session.live_views()` return
|
|
206
|
+
records, not unvalidated dictionaries. `session.answer(view_id, values)` submits a
|
|
207
|
+
form; `session.view_action(view_id, action, **values)` validates the closed action
|
|
208
|
+
shape before transport IO. Malformed incoming views raise `ProtocolError`.
|
|
209
|
+
Input projections carry the engine's `created_at`, not channel routing or validator
|
|
210
|
+
callbacks. An `InputResult` contains only the close reason: submitted values and
|
|
211
|
+
secret handles go to the waiting extension, not the event stream. SSE omits the
|
|
212
|
+
already streamed live picture, so `LiveResult.view` is optional; retain the open
|
|
213
|
+
View and apply its patches if your application needs a materialized display.
|
|
214
|
+
|
|
215
|
+
**Activity** is engine-observed execution evidence, not an interactive View and not
|
|
216
|
+
model context. An extension can set `vis.Activity(presenter=..., label=...)` on a
|
|
217
|
+
symbol or `vis.method`; it cannot forge lifecycle identity, timing or outcomes.
|
|
218
|
+
The tool's observation/mutation tag still determines its effect classification.
|
|
219
|
+
Omitting Activity metadata preserves the engine's default presentation.
|
|
220
|
+
|
|
221
|
+
```python
|
|
222
|
+
# The same records are returned by GatewayClient and LocalEngine.
|
|
223
|
+
with session.events() as events:
|
|
224
|
+
for event in events:
|
|
225
|
+
if event.activity is not None:
|
|
226
|
+
print(event.activity.state, event.activity.counts)
|
|
227
|
+
if event.view is not None:
|
|
228
|
+
print(event.type, event.view.kind, event.view.view_id)
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
`blockether.vis.activity.ActivityProjection` represents the complete replacement
|
|
232
|
+
in a `block.activity` event; it is not a delta to merge. Rows, counts, omission
|
|
233
|
+
accounting, evidence and resources are named records. `from_wire` validates the
|
|
234
|
+
canonical schema and semantic bounds; `to_wire` produces plain JSON-compatible
|
|
235
|
+
data. View records provide the same conversion boundary.
|
|
236
|
+
|
|
237
|
+
The normative specifications are the packaged
|
|
238
|
+
[View contract](https://github.com/Blockether/vis/blob/main/packages/vis-contract/resources/vis-contract/view.json),
|
|
239
|
+
[View schema](https://github.com/Blockether/vis/blob/main/packages/vis-contract/resources/vis-contract/schema/view.json),
|
|
240
|
+
[Activity contract](https://github.com/Blockether/vis/blob/main/packages/vis-contract/resources/vis-contract/activity.json)
|
|
241
|
+
and [Activity schema](https://github.com/Blockether/vis/blob/main/packages/vis-contract/resources/vis-contract/schema/activity.json).
|
|
242
|
+
SDK, engine, TUI and Companion test against fixtures owned by that contract package,
|
|
243
|
+
not separately maintained examples. Retired Activity envelopes are rejected; there
|
|
244
|
+
is no compatibility renderer or alternate import alias.
|
|
245
|
+
|
|
246
|
+
## Remote gateway client
|
|
247
|
+
|
|
248
|
+
```python
|
|
249
|
+
import os
|
|
250
|
+
from blockether.vis.engine import GatewayClient
|
|
251
|
+
|
|
252
|
+
with GatewayClient(
|
|
253
|
+
"https://gateway.example.com", token=os.environ["VIS_TOKEN"]
|
|
254
|
+
) as client:
|
|
255
|
+
session = client.create_session(title="Python API")
|
|
256
|
+
turn = session.send("Describe this project")
|
|
257
|
+
result = turn.wait(timeout=120)
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
The client uses an explicit origin; it does not discover credentials, download Vis,
|
|
261
|
+
start a gateway or stop a user's server. Closing releases its client lease, not
|
|
262
|
+
sessions. Idle clients renew their lease every 30 seconds using the canonical
|
|
263
|
+
bounded keepalive request. A failed renewal invalidates the client; close it and
|
|
264
|
+
create a new instance explicitly. Mutations are never replayed to regain a lease.
|
|
265
|
+
This client is not an embedded/local engine.
|
|
266
|
+
|
|
267
|
+
- `create_session`, `list_sessions` (one cursor page), `session(id)`.
|
|
268
|
+
- Session: `read`, `update`, `delete`, `send`, `input_views`, `live_views`,
|
|
269
|
+
`answer`, `view_action`, `events`, `turns`, `artifacts`, `transcript`, `upload`,
|
|
270
|
+
`download_attachment`.
|
|
271
|
+
- Turn: `read`, `wait`, `cancel`. Failed/cancelled turns are returned with their
|
|
272
|
+
status; a wait timeout does not cancel the operation.
|
|
273
|
+
- Dedicated methods cover non-streaming SDK operations: for example `get_models()`,
|
|
274
|
+
`get_provider_status(provider_id)`, `get_projects()` and `get_settings()`.
|
|
275
|
+
There is no public `call`. Each method's docstring names its canonical operation;
|
|
276
|
+
JSON operations are annotated `JSONValue`; binary/text operations return
|
|
277
|
+
`Response(status, content, headers)`, and empty responses return `None`.
|
|
278
|
+
Endpoint payloads without a canonical schema remain JSON rather than invented models.
|
|
279
|
+
Session SSE uses `session.events()`. `speech_events(job_id)`, `voice_events(job_id)`
|
|
280
|
+
and their `session_speech_events(sid, job_id)` / `session_voice_events(sid, job_id)`
|
|
281
|
+
counterparts expose all four job streams as typed `JobEvent` snapshots.
|
|
282
|
+
Job streams have no cursor: reconnect rereads state, identical snapshots are
|
|
283
|
+
suppressed, and `is_done` terminates the iterator. Buffered methods refuse SSE.
|
|
284
|
+
- `events(cursor=0, reconnects=3, retry_delay=0.2)` is a context-managed SSE
|
|
285
|
+
iterator returning named `Event` records with `type` and `seq`. Replay suppresses
|
|
286
|
+
duplicates and honors a `subscription.ready` cursor reset after restart.
|
|
287
|
+
Exhausting the reconnect budget raises `TransportError`.
|
|
288
|
+
- `GatewayError` exposes HTTP `status` and `code`; `ProtocolError` reports
|
|
289
|
+
incompatible/malformed protocol data; `VisTimeout` is a `TransportError`.
|
|
290
|
+
Mutations are never retried automatically. Keep the idempotency key if you
|
|
291
|
+
explicitly retry a submission whose outcome is unknown.
|
|
292
|
+
|
|
293
|
+
Use one calling thread per client. Always close event iterators. Requests and
|
|
294
|
+
SSE idle reads use the client's timeout (30 seconds by default); turns have a
|
|
295
|
+
separate wait deadline. TLS verification stays enabled and redirects are refused.
|
|
296
|
+
|
|
297
|
+
## Owned local engine
|
|
298
|
+
|
|
299
|
+
```python
|
|
300
|
+
from blockether.vis.engine import LocalEngine
|
|
301
|
+
|
|
302
|
+
with LocalEngine(executable="/path/to/vis-agent", root="/path/to/project") as engine:
|
|
303
|
+
session = engine.create_session(title="Local Python API")
|
|
304
|
+
print(session.read())
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
Supply a compatible Vis executable that implements `sdk-stdio`. The wheel does not
|
|
308
|
+
bundle or download that executable, and older binaries without this command cannot
|
|
309
|
+
be used. `LocalEngine` starts a private engine process, not an HTTP gateway and not
|
|
310
|
+
an in-process JVM. Linux and macOS are supported by this transport; Windows is not.
|
|
311
|
+
It reuses the remote client's dedicated methods, Session, Turn and Event types.
|
|
312
|
+
Local session and speech/voice job events poll the same canonical resources over
|
|
313
|
+
stdio instead of opening SSE connections. Polling has a bounded idle timeout and
|
|
314
|
+
validates an event before advancing its cursor. Closing a stream never cancels a job.
|
|
315
|
+
Use one calling thread per engine. Request timeouts close the owned process to
|
|
316
|
+
prevent a late response being mistaken for a later request. `startup_timeout`
|
|
317
|
+
defaults to 120 seconds; individual requests default to 30 seconds.
|
|
318
|
+
|
|
319
|
+
The database is temporary and discarded on close. Engine sessions are not durable
|
|
320
|
+
across context-manager exits. Project configuration is inherited; agent execution
|
|
321
|
+
still needs a configured provider. No gateway discovery or user-server shutdown is
|
|
322
|
+
performed.
|
|
323
|
+
|
|
324
|
+
The integration suite runs both transports against real Vis processes and a
|
|
325
|
+
loopback model double, covering extension execution, input/live View, Activity,
|
|
326
|
+
completion, cancellation and cleanup. Select it with `VIS_TEST_LOCAL_COMMAND`.
|
|
327
|
+
No real-model credentials are required. Release verification and any remaining
|
|
328
|
+
external gates are recorded in `PLAN.md`; unit tests do not prove a linked binary.
|
|
329
|
+
|
|
330
|
+
## Repository SDK checks
|
|
331
|
+
|
|
332
|
+
This checkout ships `.vis/extensions/sdk_checks.py` as the `sdk` extension. After
|
|
333
|
+
`/reload`, call its namespaced tool from a Vis session:
|
|
334
|
+
|
|
335
|
+
```python
|
|
336
|
+
report = sdk.check(
|
|
337
|
+
root=".",
|
|
338
|
+
python="/path/to/verification-venv/bin/python",
|
|
339
|
+
engine_command="/path/to/staged/vis-agent",
|
|
340
|
+
)
|
|
341
|
+
print(report.is_pass, report.is_engine_checked)
|
|
342
|
+
for step in report.steps:
|
|
343
|
+
print(step.name, step.exit_code, step.duration_ms)
|
|
344
|
+
```
|
|
345
|
+
|
|
346
|
+
The selected Python needs `pytest`, `build`, `ruff` and `twine`. Checks cover source
|
|
347
|
+
lint/format/tests, direct versus sdist-rebuilt wheels, exact packaged source and
|
|
348
|
+
canonical contracts, strict distribution metadata, documented examples/file links,
|
|
349
|
+
and tests of a disposable wheel installation **outside the checkout**. The check
|
|
350
|
+
publishes ordered Activity progress and returns frozen `CheckReport`/`CheckResult`
|
|
351
|
+
records with bounded per-process output tails. It stops at the first failure and
|
|
352
|
+
cleans up its own processes and temporary environments, including on interruption.
|
|
353
|
+
|
|
354
|
+
Omit `engine_command` for local/package checks; then `is_engine_checked` is false
|
|
355
|
+
and opt-in engine tests are skipped. An explicit command exercises real HTTP/stdio
|
|
356
|
+
with an isolated engine and local model double—not your live gateway. A staged
|
|
357
|
+
native wrapper tests its bundled Python runtime as well. This tool never builds
|
|
358
|
+
native images, spends real-model tokens, commits, tags or publishes.
|
|
359
|
+
|
|
360
|
+
## Distribution and publishing
|
|
361
|
+
|
|
362
|
+
The SDK builds a wheel from its sdist and is tested after installation outside
|
|
363
|
+
the checkout. CI covers CPython 3.11–3.14 and PyPy 3.11 on Linux/macOS;
|
|
364
|
+
the engine's embedded interpreter is independently owned by `vis-python-runtime`.
|
|
365
|
+
The pinned runtime v0.5.0 is published on GitHub with Linux/macOS x64/arm64 assets.
|
|
366
|
+
It is not a second Python SDK or a PyPI alias.
|
|
367
|
+
|
|
368
|
+
`.github/workflows/python-publish.yml` is an explicit, version-checked publishing
|
|
369
|
+
gate, dependent on distribution and real-engine tests. Publishing requires the
|
|
370
|
+
protected `pypi` environment and a trusted publisher for `vis-agent`. Building
|
|
371
|
+
a wheel or adding this workflow does not publish the SDK to PyPI.
|
|
372
|
+
|
|
373
|
+
## Where the real documentation lives
|
|
374
|
+
|
|
375
|
+
`vis.ask`, the field builders, `vis.Extension`/`vis.register`, hooks, providers and network
|
|
376
|
+
filters are documented where they are defined, in `blockether/vis/extension.py`, and in the
|
|
377
|
+
Vis docs (`doc("extending")` inside a session). Canonical JSON documents live in
|
|
378
|
+
`vis-contract`; this package implements their host and gateway contracts.
|
|
379
|
+
|
|
380
|
+
Apache-2.0. Part of the Vis repository: `packages/vis-agent`.
|