sbxloop-worker 0.3.0__tar.gz → 0.4.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.
Files changed (24) hide show
  1. {sbxloop_worker-0.3.0 → sbxloop_worker-0.4.1}/.gitignore +4 -0
  2. {sbxloop_worker-0.3.0 → sbxloop_worker-0.4.1}/PKG-INFO +1 -1
  3. {sbxloop_worker-0.3.0 → sbxloop_worker-0.4.1}/pyproject.toml +14 -2
  4. sbxloop_worker-0.4.1/src/sbxloop_worker/__init__.py +19 -0
  5. sbxloop_worker-0.4.1/src/sbxloop_worker/_version.py +24 -0
  6. {sbxloop_worker-0.3.0 → sbxloop_worker-0.4.1}/src/sbxloop_worker/backends/copilot.py +69 -5
  7. sbxloop_worker-0.3.0/src/sbxloop_worker/__init__.py +0 -10
  8. {sbxloop_worker-0.3.0 → sbxloop_worker-0.4.1}/README.md +0 -0
  9. {sbxloop_worker-0.3.0 → sbxloop_worker-0.4.1}/src/sbxloop_worker/__main__.py +0 -0
  10. {sbxloop_worker-0.3.0 → sbxloop_worker-0.4.1}/src/sbxloop_worker/_json.py +0 -0
  11. {sbxloop_worker-0.3.0 → sbxloop_worker-0.4.1}/src/sbxloop_worker/backends/__init__.py +0 -0
  12. {sbxloop_worker-0.3.0 → sbxloop_worker-0.4.1}/src/sbxloop_worker/backends/echo.py +0 -0
  13. {sbxloop_worker-0.3.0 → sbxloop_worker-0.4.1}/src/sbxloop_worker/events.py +0 -0
  14. {sbxloop_worker-0.3.0 → sbxloop_worker-0.4.1}/src/sbxloop_worker/githubops.py +0 -0
  15. {sbxloop_worker-0.3.0 → sbxloop_worker-0.4.1}/src/sbxloop_worker/protocol.py +0 -0
  16. {sbxloop_worker-0.3.0 → sbxloop_worker-0.4.1}/src/sbxloop_worker/py.typed +0 -0
  17. {sbxloop_worker-0.3.0 → sbxloop_worker-0.4.1}/src/sbxloop_worker/runner.py +0 -0
  18. {sbxloop_worker-0.3.0 → sbxloop_worker-0.4.1}/tests/conftest.py +0 -0
  19. {sbxloop_worker-0.3.0 → sbxloop_worker-0.4.1}/tests/test_githubops.py +0 -0
  20. {sbxloop_worker-0.3.0 → sbxloop_worker-0.4.1}/tests/test_json_extract.py +0 -0
  21. {sbxloop_worker-0.3.0 → sbxloop_worker-0.4.1}/tests/test_protocol.py +0 -0
  22. {sbxloop_worker-0.3.0 → sbxloop_worker-0.4.1}/tests/test_runner_unit.py +0 -0
  23. {sbxloop_worker-0.3.0 → sbxloop_worker-0.4.1}/tests/test_worker_contract.py +0 -0
  24. {sbxloop_worker-0.3.0 → sbxloop_worker-0.4.1}/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.3.0
3
+ Version: 0.4.1
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.3.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.1'
22
+ __version_tuple__ = version_tuple = (0, 4, 1)
23
+
24
+ __commit_id__ = commit_id = None
@@ -17,6 +17,7 @@ from unit coverage accordingly.
17
17
  from __future__ import annotations
18
18
 
19
19
  import asyncio
20
+ import json
20
21
  import os
21
22
  from typing import Any
22
23
 
@@ -52,6 +53,59 @@ def _auth_diagnostic() -> str:
52
53
  )
53
54
 
54
55
 
56
+ # Transcript-facing clips: the event stream is telemetry, not the artifact
57
+ # channel, so payloads are bounded aggressively.
58
+ TOOL_ARGS_CLIP = 400
59
+ TOOL_OUTPUT_CLIP = 1_000
60
+
61
+
62
+ def _tool_args(arguments: Any) -> str | None:
63
+ """The tool's arguments as one displayable string (best-effort).
64
+
65
+ ``ToolExecutionStartData.arguments`` is typed ``Any`` by the SDK: shell
66
+ tools carry ``{"command": ...}``-shaped dicts, others arbitrary JSON.
67
+ Prefer the human-relevant fields, fall back to compact JSON.
68
+ """
69
+ if arguments is None:
70
+ return None
71
+ if isinstance(arguments, dict):
72
+ for key in ("command", "cmd", "input", "query", "path"):
73
+ value = arguments.get(key)
74
+ if isinstance(value, str) and value.strip():
75
+ return value[:TOOL_ARGS_CLIP]
76
+ try:
77
+ return json.dumps(arguments, separators=(",", ":"))[:TOOL_ARGS_CLIP]
78
+ except (TypeError, ValueError):
79
+ return str(arguments)[:TOOL_ARGS_CLIP]
80
+ text = str(arguments)
81
+ return text[:TOOL_ARGS_CLIP] if text.strip() else None
82
+
83
+
84
+ def _tool_exit_code(data: Any) -> int | None:
85
+ """Shell exit code from a ToolExecutionComplete, when present.
86
+
87
+ Shell completions carry a ShellExit entry in ``result.contents``
88
+ (verified against github-copilot-sdk: ``exit_code: int``).
89
+ """
90
+ result = getattr(data, "result", None)
91
+ for entry in getattr(result, "contents", None) or []:
92
+ code = getattr(entry, "exit_code", None)
93
+ if isinstance(code, int):
94
+ return code
95
+ return None
96
+
97
+
98
+ def _tool_output(data: Any) -> str | None:
99
+ """A bounded tail of the tool's output, for transcript display."""
100
+ result = getattr(data, "result", None)
101
+ content = getattr(result, "content", None)
102
+ if not content and result is not None:
103
+ content = getattr(result, "detailed_content", None)
104
+ if not isinstance(content, str) or not content.strip():
105
+ return None
106
+ return content[-TOOL_OUTPUT_CLIP:]
107
+
108
+
55
109
  class CopilotBackend:
56
110
  name = "copilot"
57
111
 
@@ -75,6 +129,9 @@ class CopilotBackend:
75
129
 
76
130
  usage = Usage()
77
131
  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] = {}
78
135
 
79
136
  def on_event(event: Any) -> None:
80
137
  nonlocal usage
@@ -91,18 +148,25 @@ class CopilotBackend:
91
148
  final_text.append(content)
92
149
  emit(EventTypes.AGENT_MESSAGE, content=content)
93
150
  elif type_name.startswith("ToolExecutionStart"):
151
+ tool = getattr(data, "tool_name", None) or getattr(data, "toolName", None)
152
+ 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)
94
155
  emit(
95
156
  EventTypes.AGENT_TOOL_START,
96
- tool=getattr(data, "tool_name", None) or getattr(data, "toolName", None),
97
- tool_call_id=getattr(data, "tool_call_id", None)
98
- or getattr(data, "toolCallId", None),
157
+ tool=tool,
158
+ tool_call_id=call_id,
159
+ args=_tool_args(getattr(data, "arguments", None)),
99
160
  )
100
161
  elif type_name.startswith("ToolExecutionComplete"):
162
+ call_id = getattr(data, "tool_call_id", None) or getattr(data, "toolCallId", None)
101
163
  emit(
102
164
  EventTypes.AGENT_TOOL_END,
103
- tool_call_id=getattr(data, "tool_call_id", None)
104
- or getattr(data, "toolCallId", None),
165
+ tool_call_id=call_id,
166
+ tool=tool_names.get(str(call_id)),
105
167
  success=getattr(data, "success", None),
168
+ exit_code=_tool_exit_code(data),
169
+ output=_tool_output(data),
106
170
  )
107
171
  elif type_name == "AssistantUsageData":
108
172
  sample = Usage(
@@ -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.3.0"
9
-
10
- __all__ = ["__version__"]
File without changes