sbxloop-worker 0.4.0__tar.gz → 0.4.2__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 (24) hide show
  1. {sbxloop_worker-0.4.0 → sbxloop_worker-0.4.2}/.gitignore +4 -0
  2. {sbxloop_worker-0.4.0 → sbxloop_worker-0.4.2}/PKG-INFO +1 -1
  3. {sbxloop_worker-0.4.0 → sbxloop_worker-0.4.2}/pyproject.toml +14 -2
  4. sbxloop_worker-0.4.2/src/sbxloop_worker/__init__.py +19 -0
  5. sbxloop_worker-0.4.2/src/sbxloop_worker/_version.py +24 -0
  6. {sbxloop_worker-0.4.0 → sbxloop_worker-0.4.2}/src/sbxloop_worker/backends/copilot.py +26 -8
  7. {sbxloop_worker-0.4.0 → sbxloop_worker-0.4.2}/tests/test_runner_unit.py +30 -0
  8. sbxloop_worker-0.4.0/src/sbxloop_worker/__init__.py +0 -10
  9. {sbxloop_worker-0.4.0 → sbxloop_worker-0.4.2}/README.md +0 -0
  10. {sbxloop_worker-0.4.0 → sbxloop_worker-0.4.2}/src/sbxloop_worker/__main__.py +0 -0
  11. {sbxloop_worker-0.4.0 → sbxloop_worker-0.4.2}/src/sbxloop_worker/_json.py +0 -0
  12. {sbxloop_worker-0.4.0 → sbxloop_worker-0.4.2}/src/sbxloop_worker/backends/__init__.py +0 -0
  13. {sbxloop_worker-0.4.0 → sbxloop_worker-0.4.2}/src/sbxloop_worker/backends/echo.py +0 -0
  14. {sbxloop_worker-0.4.0 → sbxloop_worker-0.4.2}/src/sbxloop_worker/events.py +0 -0
  15. {sbxloop_worker-0.4.0 → sbxloop_worker-0.4.2}/src/sbxloop_worker/githubops.py +0 -0
  16. {sbxloop_worker-0.4.0 → sbxloop_worker-0.4.2}/src/sbxloop_worker/protocol.py +0 -0
  17. {sbxloop_worker-0.4.0 → sbxloop_worker-0.4.2}/src/sbxloop_worker/py.typed +0 -0
  18. {sbxloop_worker-0.4.0 → sbxloop_worker-0.4.2}/src/sbxloop_worker/runner.py +0 -0
  19. {sbxloop_worker-0.4.0 → sbxloop_worker-0.4.2}/tests/conftest.py +0 -0
  20. {sbxloop_worker-0.4.0 → sbxloop_worker-0.4.2}/tests/test_githubops.py +0 -0
  21. {sbxloop_worker-0.4.0 → sbxloop_worker-0.4.2}/tests/test_json_extract.py +0 -0
  22. {sbxloop_worker-0.4.0 → sbxloop_worker-0.4.2}/tests/test_protocol.py +0 -0
  23. {sbxloop_worker-0.4.0 → sbxloop_worker-0.4.2}/tests/test_worker_contract.py +0 -0
  24. {sbxloop_worker-0.4.0 → sbxloop_worker-0.4.2}/tests/worker_harness.py +0 -0
@@ -221,3 +221,7 @@ __marimo__/
221
221
  .sbxloop/
222
222
  .sdxloop/
223
223
  packages/sbxloop/src/sbxloop/_vendor/
224
+
225
+ # written at build time by hatch-vcs
226
+ packages/sbxloop/src/sbxloop/_version.py
227
+ packages/sbxloop-worker/src/sbxloop_worker/_version.py
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sbxloop-worker
3
- Version: 0.4.0
3
+ Version: 0.4.2
4
4
  Summary: In-sandbox worker runtime for sbxloop (protocol models, agent backends, job runner)
5
5
  Project-URL: Homepage, https://github.com/brettbergin/sbxloop
6
6
  Project-URL: Repository, https://github.com/brettbergin/sbxloop
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "sbxloop-worker"
3
- version = "0.4.0"
3
+ dynamic = ["version"] # derived from git tags by hatch-vcs (see [tool.hatch.version])
4
4
  description = "In-sandbox worker runtime for sbxloop (protocol models, agent backends, job runner)"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.11"
@@ -25,8 +25,20 @@ Homepage = "https://github.com/brettbergin/sbxloop"
25
25
  Repository = "https://github.com/brettbergin/sbxloop"
26
26
 
27
27
  [build-system]
28
- requires = ["hatchling>=1.27"]
28
+ requires = ["hatchling>=1.27", "hatch-vcs"]
29
29
  build-backend = "hatchling.build"
30
30
 
31
+ # Both workspace packages version from the same repo-root git tag, keeping the
32
+ # lockstep invariant by construction. `fallback_root` lets a wheel built from
33
+ # an extracted sdist (no git) pick the version up from the sdist's PKG-INFO.
34
+ [tool.hatch.version]
35
+ source = "vcs"
36
+ raw-options = { root = "../..", fallback_root = ".", local_scheme = "no-local-version", fallback_version = "0.0.0" }
37
+
38
+ # Write the resolved version into the package so `sbxloop_worker.__version__`
39
+ # reports it at runtime (inside the sandbox, where dist metadata may be absent).
40
+ [tool.hatch.build.hooks.vcs]
41
+ version-file = "src/sbxloop_worker/_version.py"
42
+
31
43
  [tool.hatch.build.targets.wheel]
32
44
  packages = ["src/sbxloop_worker"]
@@ -0,0 +1,19 @@
1
+ """sbxloop-worker — the in-sandbox runtime for sbxloop.
2
+
3
+ Runs inside a Docker Sandbox microVM. Hosts the shared host/worker protocol
4
+ models, the agent backends (GitHub Copilot SDK), and the job runner invoked
5
+ via ``python -m sbxloop_worker``.
6
+ """
7
+
8
+ try:
9
+ # written at build time by hatch-vcs (see pyproject [tool.hatch.build.hooks.vcs])
10
+ from sbxloop_worker._version import __version__
11
+ except ImportError: # pragma: no cover - raw source tree that was never built
12
+ try:
13
+ from importlib.metadata import version as _pkg_version
14
+
15
+ __version__ = _pkg_version("sbxloop-worker")
16
+ except Exception:
17
+ __version__ = "0.0.0"
18
+
19
+ __all__ = ["__version__"]
@@ -0,0 +1,24 @@
1
+ # file generated by vcs-versioning
2
+ # don't change, don't track in version control
3
+ from __future__ import annotations
4
+
5
+ __all__ = [
6
+ "__version__",
7
+ "__version_tuple__",
8
+ "version",
9
+ "version_tuple",
10
+ "__commit_id__",
11
+ "commit_id",
12
+ ]
13
+
14
+ version: str
15
+ __version__: str
16
+ __version_tuple__: tuple[int | str, ...]
17
+ version_tuple: tuple[int | str, ...]
18
+ commit_id: str | None
19
+ __commit_id__: str | None
20
+
21
+ __version__ = version = '0.4.2'
22
+ __version_tuple__ = version_tuple = (0, 4, 2)
23
+
24
+ __commit_id__ = commit_id = None
@@ -69,7 +69,7 @@ def _tool_args(arguments: Any) -> str | None:
69
69
  if arguments is None:
70
70
  return None
71
71
  if isinstance(arguments, dict):
72
- for key in ("command", "cmd", "input", "query", "path"):
72
+ for key in ("command", "cmd", "input", "query", "pattern", "path"):
73
73
  value = arguments.get(key)
74
74
  if isinstance(value, str) and value.strip():
75
75
  return value[:TOOL_ARGS_CLIP]
@@ -106,6 +106,20 @@ def _tool_output(data: Any) -> str | None:
106
106
  return content[-TOOL_OUTPUT_CLIP:]
107
107
 
108
108
 
109
+ def _tool_error(data: Any) -> str | None:
110
+ """The failure reason from a ToolExecutionComplete, when present.
111
+
112
+ On failed executions the SDK leaves ``result`` unset (it is documented
113
+ as "tool execution result on success") and reports the reason in
114
+ ``error.message`` instead, so this is the only failure text available.
115
+ """
116
+ error = getattr(data, "error", None)
117
+ message = getattr(error, "message", None)
118
+ if not isinstance(message, str) or not message.strip():
119
+ return None
120
+ return message[-TOOL_OUTPUT_CLIP:]
121
+
122
+
109
123
  class CopilotBackend:
110
124
  name = "copilot"
111
125
 
@@ -129,9 +143,9 @@ class CopilotBackend:
129
143
 
130
144
  usage = Usage()
131
145
  final_text: list[str] = []
132
- # tool_call_id -> tool name, so completion events can name the tool
133
- # (the SDK's Complete event carries only the call id).
134
- tool_names: dict[str, str] = {}
146
+ # tool_call_id -> (tool name, displayed args), so completion events
147
+ # can say what ran (the SDK's Complete event carries only the call id).
148
+ tool_calls: dict[str, tuple[str | None, str | None]] = {}
135
149
 
136
150
  def on_event(event: Any) -> None:
137
151
  nonlocal usage
@@ -150,23 +164,27 @@ class CopilotBackend:
150
164
  elif type_name.startswith("ToolExecutionStart"):
151
165
  tool = getattr(data, "tool_name", None) or getattr(data, "toolName", None)
152
166
  call_id = getattr(data, "tool_call_id", None) or getattr(data, "toolCallId", None)
153
- if tool and call_id:
154
- tool_names[str(call_id)] = str(tool)
167
+ args = _tool_args(getattr(data, "arguments", None))
168
+ if call_id:
169
+ tool_calls[str(call_id)] = (str(tool) if tool else None, args)
155
170
  emit(
156
171
  EventTypes.AGENT_TOOL_START,
157
172
  tool=tool,
158
173
  tool_call_id=call_id,
159
- args=_tool_args(getattr(data, "arguments", None)),
174
+ args=args,
160
175
  )
161
176
  elif type_name.startswith("ToolExecutionComplete"):
162
177
  call_id = getattr(data, "tool_call_id", None) or getattr(data, "toolCallId", None)
178
+ tool, args = tool_calls.get(str(call_id), (None, None))
163
179
  emit(
164
180
  EventTypes.AGENT_TOOL_END,
165
181
  tool_call_id=call_id,
166
- tool=tool_names.get(str(call_id)),
182
+ tool=tool,
183
+ args=args,
167
184
  success=getattr(data, "success", None),
168
185
  exit_code=_tool_exit_code(data),
169
186
  output=_tool_output(data),
187
+ error=_tool_error(data),
170
188
  )
171
189
  elif type_name == "AssistantUsageData":
172
190
  sample = Usage(
@@ -109,6 +109,36 @@ class TestBackendRegistry:
109
109
  backend.run_session(agent_job(), lambda *a, **k: None) # type: ignore[arg-type,return-value]
110
110
 
111
111
 
112
+ class TestCopilotEventHelpers:
113
+ """Pure helpers from the copilot backend (importable without the SDK)."""
114
+
115
+ def test_tool_args_prefers_glob_pattern(self) -> None:
116
+ from sbxloop_worker.backends.copilot import _tool_args
117
+
118
+ assert _tool_args({"pattern": "**/*.py"}) == "**/*.py"
119
+
120
+ def test_tool_error_reads_failure_message(self) -> None:
121
+ from types import SimpleNamespace
122
+
123
+ from sbxloop_worker.backends.copilot import _tool_error
124
+
125
+ # A failed ToolExecutionComplete: result is None, error carries the
126
+ # reason (the SDK documents `result` as success-only).
127
+ data = SimpleNamespace(
128
+ success=False,
129
+ result=None,
130
+ error=SimpleNamespace(message="permission denied", code=None),
131
+ )
132
+ assert _tool_error(data) == "permission denied"
133
+
134
+ def test_tool_error_none_when_absent(self) -> None:
135
+ from types import SimpleNamespace
136
+
137
+ from sbxloop_worker.backends.copilot import _tool_error
138
+
139
+ assert _tool_error(SimpleNamespace(success=True, result=None, error=None)) is None
140
+
141
+
112
142
  class TestEchoScriptEdgeCases:
113
143
  def test_script_entry_must_be_object(
114
144
  self, tmp_path: Path, monkeypatch: pytest.MonkeyPatch
@@ -1,10 +0,0 @@
1
- """sbxloop-worker — the in-sandbox runtime for sbxloop.
2
-
3
- Runs inside a Docker Sandbox microVM. Hosts the shared host/worker protocol
4
- models, the agent backends (GitHub Copilot SDK), and the job runner invoked
5
- via ``python -m sbxloop_worker``.
6
- """
7
-
8
- __version__ = "0.4.0"
9
-
10
- __all__ = ["__version__"]
File without changes