vis-agent 0.2.54.dev7904__tar.gz → 0.3.1__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.2.54.dev7904 → vis_agent-0.3.1}/PKG-INFO +1 -1
- {vis_agent-0.2.54.dev7904 → vis_agent-0.3.1}/contracts/schema/agents.json +34 -0
- {vis_agent-0.2.54.dev7904 → vis_agent-0.3.1}/contracts/schema/config.json +0 -6
- {vis_agent-0.2.54.dev7904 → vis_agent-0.3.1}/pyproject.toml +1 -1
- {vis_agent-0.2.54.dev7904 → vis_agent-0.3.1}/src/blockether/vis/_outside.py +20 -0
- {vis_agent-0.2.54.dev7904 → vis_agent-0.3.1}/src/blockether/vis/engine/_agents.py +5 -1
- {vis_agent-0.2.54.dev7904 → vis_agent-0.3.1}/src/blockether/vis/extension.py +17 -0
- {vis_agent-0.2.54.dev7904 → vis_agent-0.3.1}/src/blockether/vis/extension_package.py +31 -0
- {vis_agent-0.2.54.dev7904 → vis_agent-0.3.1}/tests/test_agents.py +23 -0
- {vis_agent-0.2.54.dev7904 → vis_agent-0.3.1}/tests/test_extension_package.py +22 -0
- vis_agent-0.2.54.dev7904/release-tag +0 -1
- {vis_agent-0.2.54.dev7904 → vis_agent-0.3.1}/.gitignore +0 -0
- {vis_agent-0.2.54.dev7904 → vis_agent-0.3.1}/LICENSE +0 -0
- {vis_agent-0.2.54.dev7904 → vis_agent-0.3.1}/README.md +0 -0
- {vis_agent-0.2.54.dev7904 → vis_agent-0.3.1}/contracts/fixtures/activity-arguments.json +0 -0
- {vis_agent-0.2.54.dev7904 → vis_agent-0.3.1}/contracts/fixtures/activity-cases.json +0 -0
- {vis_agent-0.2.54.dev7904 → vis_agent-0.3.1}/contracts/fixtures/activity-copy.json +0 -0
- {vis_agent-0.2.54.dev7904 → vis_agent-0.3.1}/contracts/fixtures/activity-execution.json +0 -0
- {vis_agent-0.2.54.dev7904 → vis_agent-0.3.1}/contracts/fixtures/activity-groups.json +0 -0
- {vis_agent-0.2.54.dev7904 → vis_agent-0.3.1}/contracts/fixtures/activity-read-session.json +0 -0
- {vis_agent-0.2.54.dev7904 → vis_agent-0.3.1}/contracts/fixtures/activity-reads.json +0 -0
- {vis_agent-0.2.54.dev7904 → vis_agent-0.3.1}/contracts/fixtures/activity-results.json +0 -0
- {vis_agent-0.2.54.dev7904 → vis_agent-0.3.1}/contracts/fixtures/activity-stream.json +0 -0
- {vis_agent-0.2.54.dev7904 → vis_agent-0.3.1}/contracts/fixtures/activity-tables.json +0 -0
- {vis_agent-0.2.54.dev7904 → vis_agent-0.3.1}/contracts/fixtures/activity.json +0 -0
- {vis_agent-0.2.54.dev7904 → vis_agent-0.3.1}/contracts/fixtures/diff.json +0 -0
- {vis_agent-0.2.54.dev7904 → vis_agent-0.3.1}/contracts/fixtures/live-primitives.json +0 -0
- {vis_agent-0.2.54.dev7904 → vis_agent-0.3.1}/contracts/fixtures/session-health.json +0 -0
- {vis_agent-0.2.54.dev7904 → vis_agent-0.3.1}/contracts/fixtures/view.json +0 -0
- {vis_agent-0.2.54.dev7904 → vis_agent-0.3.1}/contracts/schema/activity.json +0 -0
- {vis_agent-0.2.54.dev7904 → vis_agent-0.3.1}/contracts/schema/automations.json +0 -0
- {vis_agent-0.2.54.dev7904 → vis_agent-0.3.1}/contracts/schema/common.json +0 -0
- {vis_agent-0.2.54.dev7904 → vis_agent-0.3.1}/contracts/schema/content.json +0 -0
- {vis_agent-0.2.54.dev7904 → vis_agent-0.3.1}/contracts/schema/council.json +0 -0
- {vis_agent-0.2.54.dev7904 → vis_agent-0.3.1}/contracts/schema/diff.json +0 -0
- {vis_agent-0.2.54.dev7904 → vis_agent-0.3.1}/contracts/schema/evals.json +0 -0
- {vis_agent-0.2.54.dev7904 → vis_agent-0.3.1}/contracts/schema/gateway.json +0 -0
- {vis_agent-0.2.54.dev7904 → vis_agent-0.3.1}/contracts/schema/improve.json +0 -0
- {vis_agent-0.2.54.dev7904 → vis_agent-0.3.1}/contracts/schema/provider.json +0 -0
- {vis_agent-0.2.54.dev7904 → vis_agent-0.3.1}/contracts/schema/rooms.json +0 -0
- {vis_agent-0.2.54.dev7904 → vis_agent-0.3.1}/contracts/schema/symbol.json +0 -0
- {vis_agent-0.2.54.dev7904 → vis_agent-0.3.1}/contracts/schema/toggle.json +0 -0
- {vis_agent-0.2.54.dev7904 → vis_agent-0.3.1}/contracts/schema/view.json +0 -0
- {vis_agent-0.2.54.dev7904 → vis_agent-0.3.1}/examples/greeter/tests/test_greeter.py +0 -0
- {vis_agent-0.2.54.dev7904 → vis_agent-0.3.1}/hatch_build.py +0 -0
- {vis_agent-0.2.54.dev7904 → vis_agent-0.3.1}/src/blockether/vis/__init__.py +0 -0
- {vis_agent-0.2.54.dev7904 → vis_agent-0.3.1}/src/blockether/vis/_contracts.py +0 -0
- {vis_agent-0.2.54.dev7904 → vis_agent-0.3.1}/src/blockether/vis/_wire.py +0 -0
- {vis_agent-0.2.54.dev7904 → vis_agent-0.3.1}/src/blockether/vis/activity.py +0 -0
- {vis_agent-0.2.54.dev7904 → vis_agent-0.3.1}/src/blockether/vis/engine/__init__.py +0 -0
- {vis_agent-0.2.54.dev7904 → vis_agent-0.3.1}/src/blockether/vis/engine/_agent.py +0 -0
- {vis_agent-0.2.54.dev7904 → vis_agent-0.3.1}/src/blockether/vis/engine/_client.py +0 -0
- {vis_agent-0.2.54.dev7904 → vis_agent-0.3.1}/src/blockether/vis/engine/_council.py +0 -0
- {vis_agent-0.2.54.dev7904 → vis_agent-0.3.1}/src/blockether/vis/engine/_extensions.py +0 -0
- {vis_agent-0.2.54.dev7904 → vis_agent-0.3.1}/src/blockether/vis/engine/_host.py +0 -0
- {vis_agent-0.2.54.dev7904 → vis_agent-0.3.1}/src/blockether/vis/engine/_install.py +0 -0
- {vis_agent-0.2.54.dev7904 → vis_agent-0.3.1}/src/blockether/vis/engine/_local.py +0 -0
- {vis_agent-0.2.54.dev7904 → vis_agent-0.3.1}/src/blockether/vis/engine/_models.py +0 -0
- {vis_agent-0.2.54.dev7904 → vis_agent-0.3.1}/src/blockether/vis/evals/__init__.py +0 -0
- {vis_agent-0.2.54.dev7904 → vis_agent-0.3.1}/src/blockether/vis/evals/_gateway.py +0 -0
- {vis_agent-0.2.54.dev7904 → vis_agent-0.3.1}/src/blockether/vis/evals/agent.py +0 -0
- {vis_agent-0.2.54.dev7904 → vis_agent-0.3.1}/src/blockether/vis/evals/bench.py +0 -0
- {vis_agent-0.2.54.dev7904 → vis_agent-0.3.1}/src/blockether/vis/evals/check.py +0 -0
- {vis_agent-0.2.54.dev7904 → vis_agent-0.3.1}/src/blockether/vis/evals/compare.py +0 -0
- {vis_agent-0.2.54.dev7904 → vis_agent-0.3.1}/src/blockether/vis/evals/environment.py +0 -0
- {vis_agent-0.2.54.dev7904 → vis_agent-0.3.1}/src/blockether/vis/evals/grade.py +0 -0
- {vis_agent-0.2.54.dev7904 → vis_agent-0.3.1}/src/blockether/vis/evals/harbor.py +0 -0
- {vis_agent-0.2.54.dev7904 → vis_agent-0.3.1}/src/blockether/vis/evals/judge-rubric.toml +0 -0
- {vis_agent-0.2.54.dev7904 → vis_agent-0.3.1}/src/blockether/vis/evals/judge.py +0 -0
- {vis_agent-0.2.54.dev7904 → vis_agent-0.3.1}/src/blockether/vis/evals/labels.py +0 -0
- {vis_agent-0.2.54.dev7904 → vis_agent-0.3.1}/src/blockether/vis/evals/leaderboard.py +0 -0
- {vis_agent-0.2.54.dev7904 → vis_agent-0.3.1}/src/blockether/vis/evals/repeats.py +0 -0
- {vis_agent-0.2.54.dev7904 → vis_agent-0.3.1}/src/blockether/vis/evals/rounds.py +0 -0
- {vis_agent-0.2.54.dev7904 → vis_agent-0.3.1}/src/blockether/vis/evals/stages.py +0 -0
- {vis_agent-0.2.54.dev7904 → vis_agent-0.3.1}/src/blockether/vis/evals/stats.py +0 -0
- {vis_agent-0.2.54.dev7904 → vis_agent-0.3.1}/src/blockether/vis/evals/trajectory.py +0 -0
- {vis_agent-0.2.54.dev7904 → vis_agent-0.3.1}/src/blockether/vis/py.typed +0 -0
- {vis_agent-0.2.54.dev7904 → vis_agent-0.3.1}/src/blockether/vis/rooms.py +0 -0
- {vis_agent-0.2.54.dev7904 → vis_agent-0.3.1}/src/blockether/vis/views.py +0 -0
- {vis_agent-0.2.54.dev7904 → vis_agent-0.3.1}/tests/conftest.py +0 -0
- {vis_agent-0.2.54.dev7904 → vis_agent-0.3.1}/tests/evals_atif.py +0 -0
- {vis_agent-0.2.54.dev7904 → vis_agent-0.3.1}/tests/test_activity.py +0 -0
- {vis_agent-0.2.54.dev7904 → vis_agent-0.3.1}/tests/test_agent.py +0 -0
- {vis_agent-0.2.54.dev7904 → vis_agent-0.3.1}/tests/test_agent_extension_lifecycle.py +0 -0
- {vis_agent-0.2.54.dev7904 → vis_agent-0.3.1}/tests/test_agent_extensions.py +0 -0
- {vis_agent-0.2.54.dev7904 → vis_agent-0.3.1}/tests/test_api_guides.py +0 -0
- {vis_agent-0.2.54.dev7904 → vis_agent-0.3.1}/tests/test_attachments.py +0 -0
- {vis_agent-0.2.54.dev7904 → vis_agent-0.3.1}/tests/test_authoring.py +0 -0
- {vis_agent-0.2.54.dev7904 → vis_agent-0.3.1}/tests/test_catalog.py +0 -0
- {vis_agent-0.2.54.dev7904 → vis_agent-0.3.1}/tests/test_catalog_recipe.py +0 -0
- {vis_agent-0.2.54.dev7904 → vis_agent-0.3.1}/tests/test_ci_report_recipe.py +0 -0
- {vis_agent-0.2.54.dev7904 → vis_agent-0.3.1}/tests/test_client.py +0 -0
- {vis_agent-0.2.54.dev7904 → vis_agent-0.3.1}/tests/test_client_extensions_engine.py +0 -0
- {vis_agent-0.2.54.dev7904 → vis_agent-0.3.1}/tests/test_code_quality_recipe.py +0 -0
- {vis_agent-0.2.54.dev7904 → vis_agent-0.3.1}/tests/test_contracts.py +0 -0
- {vis_agent-0.2.54.dev7904 → vis_agent-0.3.1}/tests/test_council.py +0 -0
- {vis_agent-0.2.54.dev7904 → vis_agent-0.3.1}/tests/test_declarations.py +0 -0
- {vis_agent-0.2.54.dev7904 → vis_agent-0.3.1}/tests/test_engine.py +0 -0
- {vis_agent-0.2.54.dev7904 → vis_agent-0.3.1}/tests/test_engine_docstrings.py +0 -0
- {vis_agent-0.2.54.dev7904 → vis_agent-0.3.1}/tests/test_evals_agent.py +0 -0
- {vis_agent-0.2.54.dev7904 → vis_agent-0.3.1}/tests/test_evals_bench.py +0 -0
- {vis_agent-0.2.54.dev7904 → vis_agent-0.3.1}/tests/test_evals_check.py +0 -0
- {vis_agent-0.2.54.dev7904 → vis_agent-0.3.1}/tests/test_evals_compare.py +0 -0
- {vis_agent-0.2.54.dev7904 → vis_agent-0.3.1}/tests/test_evals_environment.py +0 -0
- {vis_agent-0.2.54.dev7904 → vis_agent-0.3.1}/tests/test_evals_gateway.py +0 -0
- {vis_agent-0.2.54.dev7904 → vis_agent-0.3.1}/tests/test_evals_grade.py +0 -0
- {vis_agent-0.2.54.dev7904 → vis_agent-0.3.1}/tests/test_evals_guards.py +0 -0
- {vis_agent-0.2.54.dev7904 → vis_agent-0.3.1}/tests/test_evals_harbor.py +0 -0
- {vis_agent-0.2.54.dev7904 → vis_agent-0.3.1}/tests/test_evals_judge.py +0 -0
- {vis_agent-0.2.54.dev7904 → vis_agent-0.3.1}/tests/test_evals_labels.py +0 -0
- {vis_agent-0.2.54.dev7904 → vis_agent-0.3.1}/tests/test_evals_repeats.py +0 -0
- {vis_agent-0.2.54.dev7904 → vis_agent-0.3.1}/tests/test_evals_rounds.py +0 -0
- {vis_agent-0.2.54.dev7904 → vis_agent-0.3.1}/tests/test_evals_stages.py +0 -0
- {vis_agent-0.2.54.dev7904 → vis_agent-0.3.1}/tests/test_evals_stats.py +0 -0
- {vis_agent-0.2.54.dev7904 → vis_agent-0.3.1}/tests/test_evals_tasks.py +0 -0
- {vis_agent-0.2.54.dev7904 → vis_agent-0.3.1}/tests/test_evals_trajectory.py +0 -0
- {vis_agent-0.2.54.dev7904 → vis_agent-0.3.1}/tests/test_extension_reference.py +0 -0
- {vis_agent-0.2.54.dev7904 → vis_agent-0.3.1}/tests/test_gateway_performance.py +0 -0
- {vis_agent-0.2.54.dev7904 → vis_agent-0.3.1}/tests/test_goals.py +0 -0
- {vis_agent-0.2.54.dev7904 → vis_agent-0.3.1}/tests/test_install.py +0 -0
- {vis_agent-0.2.54.dev7904 → vis_agent-0.3.1}/tests/test_live_primitives.py +0 -0
- {vis_agent-0.2.54.dev7904 → vis_agent-0.3.1}/tests/test_live_wait.py +0 -0
- {vis_agent-0.2.54.dev7904 → vis_agent-0.3.1}/tests/test_local.py +0 -0
- {vis_agent-0.2.54.dev7904 → vis_agent-0.3.1}/tests/test_method_typing.py +0 -0
- {vis_agent-0.2.54.dev7904 → vis_agent-0.3.1}/tests/test_models.py +0 -0
- {vis_agent-0.2.54.dev7904 → vis_agent-0.3.1}/tests/test_monitor_recipe.py +0 -0
- {vis_agent-0.2.54.dev7904 → vis_agent-0.3.1}/tests/test_namespace.py +0 -0
- {vis_agent-0.2.54.dev7904 → vis_agent-0.3.1}/tests/test_outside.py +0 -0
- {vis_agent-0.2.54.dev7904 → vis_agent-0.3.1}/tests/test_providers.py +0 -0
- {vis_agent-0.2.54.dev7904 → vis_agent-0.3.1}/tests/test_read_session.py +0 -0
- {vis_agent-0.2.54.dev7904 → vis_agent-0.3.1}/tests/test_record_arguments.py +0 -0
- {vis_agent-0.2.54.dev7904 → vis_agent-0.3.1}/tests/test_registration.py +0 -0
- {vis_agent-0.2.54.dev7904 → vis_agent-0.3.1}/tests/test_repair_hooks.py +0 -0
- {vis_agent-0.2.54.dev7904 → vis_agent-0.3.1}/tests/test_result_sequence.py +0 -0
- {vis_agent-0.2.54.dev7904 → vis_agent-0.3.1}/tests/test_rooms.py +0 -0
- {vis_agent-0.2.54.dev7904 → vis_agent-0.3.1}/tests/test_scoped_settings.py +0 -0
- {vis_agent-0.2.54.dev7904 → vis_agent-0.3.1}/tests/test_sdk_bootstrap.py +0 -0
- {vis_agent-0.2.54.dev7904 → vis_agent-0.3.1}/tests/test_sdk_guide.py +0 -0
- {vis_agent-0.2.54.dev7904 → vis_agent-0.3.1}/tests/test_sdk_typing.py +0 -0
- {vis_agent-0.2.54.dev7904 → vis_agent-0.3.1}/tests/test_shortcuts_live.py +0 -0
- {vis_agent-0.2.54.dev7904 → vis_agent-0.3.1}/tests/test_structured_live.py +0 -0
- {vis_agent-0.2.54.dev7904 → vis_agent-0.3.1}/tests/test_symbol_contract.py +0 -0
- {vis_agent-0.2.54.dev7904 → vis_agent-0.3.1}/tests/test_testing.py +0 -0
- {vis_agent-0.2.54.dev7904 → vis_agent-0.3.1}/tests/test_text_list_arguments.py +0 -0
- {vis_agent-0.2.54.dev7904 → vis_agent-0.3.1}/tests/test_tools.py +0 -0
- {vis_agent-0.2.54.dev7904 → vis_agent-0.3.1}/tests/test_views.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: vis-agent
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.1
|
|
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
|
|
@@ -481,6 +481,40 @@
|
|
|
481
481
|
},
|
|
482
482
|
"warning": {
|
|
483
483
|
"type": "string"
|
|
484
|
+
},
|
|
485
|
+
"error": {
|
|
486
|
+
"description": "Why a failed child stopped. Code rate_limited means a 429 from its provider.",
|
|
487
|
+
"type": "object",
|
|
488
|
+
"properties": {
|
|
489
|
+
"code": {
|
|
490
|
+
"enum": [
|
|
491
|
+
"rate_limited"
|
|
492
|
+
]
|
|
493
|
+
},
|
|
494
|
+
"message": {
|
|
495
|
+
"type": "string"
|
|
496
|
+
},
|
|
497
|
+
"provider": {
|
|
498
|
+
"type": "string",
|
|
499
|
+
"minLength": 1
|
|
500
|
+
},
|
|
501
|
+
"model": {
|
|
502
|
+
"type": "string",
|
|
503
|
+
"minLength": 1
|
|
504
|
+
}
|
|
505
|
+
},
|
|
506
|
+
"required": [
|
|
507
|
+
"code",
|
|
508
|
+
"message"
|
|
509
|
+
],
|
|
510
|
+
"additionalProperties": false
|
|
511
|
+
},
|
|
512
|
+
"alternatives": {
|
|
513
|
+
"description": "Working provider and model routes for a new child, outside a rate-limit or authentication cooldown.",
|
|
514
|
+
"type": "array",
|
|
515
|
+
"items": {
|
|
516
|
+
"$ref": "#/$defs/model_pair"
|
|
517
|
+
}
|
|
484
518
|
}
|
|
485
519
|
},
|
|
486
520
|
"required": [
|
|
@@ -8,7 +8,7 @@ build-backend = "hatchling.build"
|
|
|
8
8
|
|
|
9
9
|
[project]
|
|
10
10
|
name = "vis-agent"
|
|
11
|
-
version = "0.
|
|
11
|
+
version = "0.3.1"
|
|
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"
|
|
@@ -111,6 +111,25 @@ def workspace_root():
|
|
|
111
111
|
return str(Path.cwd())
|
|
112
112
|
|
|
113
113
|
|
|
114
|
+
def workspace_files():
|
|
115
|
+
"""List the files that Git sees under the process directory, or None."""
|
|
116
|
+
root = Path.cwd()
|
|
117
|
+
try:
|
|
118
|
+
done = subprocess.run(
|
|
119
|
+
["git", "ls-files", "-z", "--cached", "--others", "--exclude-standard"],
|
|
120
|
+
cwd=root,
|
|
121
|
+
capture_output=True,
|
|
122
|
+
timeout=60,
|
|
123
|
+
check=False,
|
|
124
|
+
)
|
|
125
|
+
except (OSError, subprocess.TimeoutExpired):
|
|
126
|
+
return None
|
|
127
|
+
if done.returncode != 0:
|
|
128
|
+
return None
|
|
129
|
+
names = done.stdout.decode("utf-8", "surrogateescape").split("\0")
|
|
130
|
+
return [str(root / name) for name in names if name]
|
|
131
|
+
|
|
132
|
+
|
|
114
133
|
def python_environment():
|
|
115
134
|
"""Use the project environment when no Vis session owns this call."""
|
|
116
135
|
return "project"
|
|
@@ -1443,6 +1462,7 @@ _IMPLEMENTATIONS = {
|
|
|
1443
1462
|
"workspace_root": workspace_root,
|
|
1444
1463
|
"project_root": workspace_root,
|
|
1445
1464
|
"python_environment": python_environment,
|
|
1465
|
+
"workspace_files": workspace_files,
|
|
1446
1466
|
"setting_declaration": setting_declaration,
|
|
1447
1467
|
"setting": setting,
|
|
1448
1468
|
"state_get": state_get,
|
|
@@ -40,7 +40,9 @@ class SubagentRecord:
|
|
|
40
40
|
``status`` is ``asking`` when the child waits for its parent. Then ``question``,
|
|
41
41
|
``budget_request`` or ``files_request`` tells what it needs. ``held`` means that a
|
|
42
42
|
human holds the queue of the child; only a person can release it. ``unknown`` names a
|
|
43
|
-
session that is not a subagent of the caller. ``warning`` reports a file overlap.
|
|
43
|
+
session that is not a subagent of the caller. ``warning`` reports a file overlap. A child
|
|
44
|
+
that a 429 stopped is ``failed`` with ``error`` (code ``rate_limited``) and ``alternatives``:
|
|
45
|
+
the working provider and model routes for a new child.
|
|
44
46
|
"""
|
|
45
47
|
|
|
46
48
|
session_id: str
|
|
@@ -54,6 +56,8 @@ class SubagentRecord:
|
|
|
54
56
|
iteration_budget: int | None = None
|
|
55
57
|
iterations_used: int | None = None
|
|
56
58
|
warning: str | None = None
|
|
59
|
+
error: dict[str, Any] | None = None
|
|
60
|
+
alternatives: list[dict[str, str]] | None = None
|
|
57
61
|
|
|
58
62
|
@classmethod
|
|
59
63
|
def from_wire(cls, value: dict[str, Any]) -> SubagentRecord:
|
|
@@ -65,6 +65,10 @@ class Host(Protocol):
|
|
|
65
65
|
"""Read the project's `python.environment`: `project` or `shared`."""
|
|
66
66
|
...
|
|
67
67
|
|
|
68
|
+
def workspace_files(self) -> list[str] | None:
|
|
69
|
+
"""List the files that Git sees in the working copy, or None without Git."""
|
|
70
|
+
...
|
|
71
|
+
|
|
68
72
|
def setting_declaration(self, spec: Mapping[str, Any]) -> Mapping[str, Any]:
|
|
69
73
|
"""Validate a declaration and fill defaults from the contract schema."""
|
|
70
74
|
...
|
|
@@ -3497,6 +3501,19 @@ def project_root() -> Path:
|
|
|
3497
3501
|
return Path(_host.project_root())
|
|
3498
3502
|
|
|
3499
3503
|
|
|
3504
|
+
def workspace_files() -> list[Path] | None:
|
|
3505
|
+
"""Return the files under `workspace_root()` that Git sees.
|
|
3506
|
+
|
|
3507
|
+
The list holds tracked files and untracked files that Git does not ignore.
|
|
3508
|
+
The host lists the working copy once and gives the same list to each
|
|
3509
|
+
extension that asks within a short time. Use it instead of a child process
|
|
3510
|
+
for each extension. The result is None when the workspace is not in a Git
|
|
3511
|
+
working tree. Outside Vis, it lists the process directory.
|
|
3512
|
+
"""
|
|
3513
|
+
files = _host.workspace_files()
|
|
3514
|
+
return None if files is None else [Path(name) for name in files]
|
|
3515
|
+
|
|
3516
|
+
|
|
3500
3517
|
def python_environment() -> Literal["project", "shared"]:
|
|
3501
3518
|
"""Return the Python environment that the session's project selects.
|
|
3502
3519
|
|
|
@@ -1410,6 +1410,35 @@ def _sync_one(name, spec, directory, current, refresh, vis_version):
|
|
|
1410
1410
|
return {**result, "status": "updated" if exists else "installed"}
|
|
1411
1411
|
|
|
1412
1412
|
|
|
1413
|
+
def _sync_read_only(specs, directory):
|
|
1414
|
+
"""Reconcile a scope that cannot be written, without the sync lock or network.
|
|
1415
|
+
|
|
1416
|
+
A container image or a read-only mount holds packages that a writable sync prepared.
|
|
1417
|
+
Ready packages stay cached, also with `latest`. A needed change fails and names the step.
|
|
1418
|
+
"""
|
|
1419
|
+
records = _sync_records(directory)
|
|
1420
|
+
results = []
|
|
1421
|
+
for name in sorted(set(specs) | set(records)):
|
|
1422
|
+
spec, current = specs.get(name), records.get(name)
|
|
1423
|
+
if spec is None:
|
|
1424
|
+
results.append({"name": name, "status": "orphaned"})
|
|
1425
|
+
elif (
|
|
1426
|
+
current is not None
|
|
1427
|
+
and current["spec"] == spec
|
|
1428
|
+
and _sync_owned(_destination(directory, name), current)
|
|
1429
|
+
):
|
|
1430
|
+
results.append({**current["result"], "name": name, "status": "cached"})
|
|
1431
|
+
else:
|
|
1432
|
+
results.append(
|
|
1433
|
+
{
|
|
1434
|
+
"name": name,
|
|
1435
|
+
"status": "failed",
|
|
1436
|
+
"error": f"{directory} is read-only. Run the sync where it is writable",
|
|
1437
|
+
}
|
|
1438
|
+
)
|
|
1439
|
+
return results
|
|
1440
|
+
|
|
1441
|
+
|
|
1413
1442
|
def sync(
|
|
1414
1443
|
configured,
|
|
1415
1444
|
directory,
|
|
@@ -1435,6 +1464,8 @@ def sync(
|
|
|
1435
1464
|
directory = Path(directory).expanduser().resolve()
|
|
1436
1465
|
if not directory.exists() and not specs:
|
|
1437
1466
|
return []
|
|
1467
|
+
if not dry_run and directory.is_dir() and not os.access(directory, os.W_OK):
|
|
1468
|
+
return _sync_read_only(specs, directory)
|
|
1438
1469
|
if not dry_run:
|
|
1439
1470
|
directory.mkdir(parents=True, exist_ok=True)
|
|
1440
1471
|
lock = directory / ".sync-lock"
|
|
@@ -180,6 +180,11 @@ def test_child_controls_answer_records_and_wait_longer_than_the_gateway():
|
|
|
180
180
|
{"status": "completed"},
|
|
181
181
|
{"session_id": "child", "status": "invented"},
|
|
182
182
|
{"session_id": "child", "status": "completed", "future_field": 1},
|
|
183
|
+
{
|
|
184
|
+
"session_id": "child",
|
|
185
|
+
"status": "failed",
|
|
186
|
+
"error": {"code": "other", "message": "x"},
|
|
187
|
+
},
|
|
183
188
|
],
|
|
184
189
|
)
|
|
185
190
|
def test_child_record_response_is_validated(record):
|
|
@@ -187,6 +192,24 @@ def test_child_record_response_is_validated(record):
|
|
|
187
192
|
SubagentRecord.from_wire(record)
|
|
188
193
|
|
|
189
194
|
|
|
195
|
+
def test_rate_limited_child_record_names_working_routes():
|
|
196
|
+
record = SubagentRecord.from_wire(
|
|
197
|
+
{
|
|
198
|
+
"session_id": "child",
|
|
199
|
+
"status": "failed",
|
|
200
|
+
"error": {
|
|
201
|
+
"code": "rate_limited",
|
|
202
|
+
"message": "Provider rate-limited",
|
|
203
|
+
"provider": "busy",
|
|
204
|
+
"model": "large",
|
|
205
|
+
},
|
|
206
|
+
"alternatives": [{"provider": "spare", "model": "small"}],
|
|
207
|
+
}
|
|
208
|
+
)
|
|
209
|
+
assert record.error["code"] == "rate_limited"
|
|
210
|
+
assert record.alternatives == [{"provider": "spare", "model": "small"}]
|
|
211
|
+
|
|
212
|
+
|
|
190
213
|
@pytest.mark.parametrize("code", ["disabled", "group-not-found"])
|
|
191
214
|
def test_unavailable_council_binding_does_not_block_team_controls(code):
|
|
192
215
|
from blockether.vis.engine import GatewayError
|
|
@@ -1022,6 +1022,28 @@ def test_sync_local_install_cache_and_explicit_prune(tmp_path):
|
|
|
1022
1022
|
assert not (target / "vis-greeter/current").exists()
|
|
1023
1023
|
|
|
1024
1024
|
|
|
1025
|
+
# A read-only image holds prepared packages; loading them must not need the sync lock.
|
|
1026
|
+
@pytest.mark.skipif(os.geteuid() == 0, reason="root ignores directory permissions")
|
|
1027
|
+
def test_sync_reads_a_read_only_scope_without_lock_or_changes(tmp_path):
|
|
1028
|
+
source = project(tmp_path / "source")
|
|
1029
|
+
target = tmp_path / "extensions"
|
|
1030
|
+
configured = {"vis-greeter": {"source": str(source)}}
|
|
1031
|
+
installed = package.sync(configured, target, trust=True)[0]
|
|
1032
|
+
target.chmod(0o555)
|
|
1033
|
+
try:
|
|
1034
|
+
cached = package.sync(configured, target, trust=True)[0]
|
|
1035
|
+
assert cached["status"] == "cached"
|
|
1036
|
+
assert cached["path"] == installed["path"]
|
|
1037
|
+
assert package.sync({}, target, trust=True)[0]["status"] == "orphaned"
|
|
1038
|
+
changed = {"vis-other": {"source": str(source)}}
|
|
1039
|
+
failed = {r["name"]: r for r in package.sync(changed, target, trust=True)}
|
|
1040
|
+
assert failed["vis-other"]["status"] == "failed"
|
|
1041
|
+
assert "read-only" in failed["vis-other"]["error"]
|
|
1042
|
+
assert not (target / ".sync-lock").exists()
|
|
1043
|
+
finally:
|
|
1044
|
+
target.chmod(0o755)
|
|
1045
|
+
|
|
1046
|
+
|
|
1025
1047
|
def test_sync_github_warm_path_does_not_fetch_or_check_catalog(releases, monkeypatch):
|
|
1026
1048
|
_, target, commands = releases
|
|
1027
1049
|
configured = {
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
beta-317ff6130bb5a97928fab5a74225d812ace95bff
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|