sbxloop-worker 0.3.0__tar.gz → 0.4.0__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.
- {sbxloop_worker-0.3.0 → sbxloop_worker-0.4.0}/PKG-INFO +1 -1
- {sbxloop_worker-0.3.0 → sbxloop_worker-0.4.0}/pyproject.toml +1 -1
- {sbxloop_worker-0.3.0 → sbxloop_worker-0.4.0}/src/sbxloop_worker/__init__.py +1 -1
- {sbxloop_worker-0.3.0 → sbxloop_worker-0.4.0}/src/sbxloop_worker/backends/copilot.py +69 -5
- {sbxloop_worker-0.3.0 → sbxloop_worker-0.4.0}/.gitignore +0 -0
- {sbxloop_worker-0.3.0 → sbxloop_worker-0.4.0}/README.md +0 -0
- {sbxloop_worker-0.3.0 → sbxloop_worker-0.4.0}/src/sbxloop_worker/__main__.py +0 -0
- {sbxloop_worker-0.3.0 → sbxloop_worker-0.4.0}/src/sbxloop_worker/_json.py +0 -0
- {sbxloop_worker-0.3.0 → sbxloop_worker-0.4.0}/src/sbxloop_worker/backends/__init__.py +0 -0
- {sbxloop_worker-0.3.0 → sbxloop_worker-0.4.0}/src/sbxloop_worker/backends/echo.py +0 -0
- {sbxloop_worker-0.3.0 → sbxloop_worker-0.4.0}/src/sbxloop_worker/events.py +0 -0
- {sbxloop_worker-0.3.0 → sbxloop_worker-0.4.0}/src/sbxloop_worker/githubops.py +0 -0
- {sbxloop_worker-0.3.0 → sbxloop_worker-0.4.0}/src/sbxloop_worker/protocol.py +0 -0
- {sbxloop_worker-0.3.0 → sbxloop_worker-0.4.0}/src/sbxloop_worker/py.typed +0 -0
- {sbxloop_worker-0.3.0 → sbxloop_worker-0.4.0}/src/sbxloop_worker/runner.py +0 -0
- {sbxloop_worker-0.3.0 → sbxloop_worker-0.4.0}/tests/conftest.py +0 -0
- {sbxloop_worker-0.3.0 → sbxloop_worker-0.4.0}/tests/test_githubops.py +0 -0
- {sbxloop_worker-0.3.0 → sbxloop_worker-0.4.0}/tests/test_json_extract.py +0 -0
- {sbxloop_worker-0.3.0 → sbxloop_worker-0.4.0}/tests/test_protocol.py +0 -0
- {sbxloop_worker-0.3.0 → sbxloop_worker-0.4.0}/tests/test_runner_unit.py +0 -0
- {sbxloop_worker-0.3.0 → sbxloop_worker-0.4.0}/tests/test_worker_contract.py +0 -0
- {sbxloop_worker-0.3.0 → sbxloop_worker-0.4.0}/tests/worker_harness.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: sbxloop-worker
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.4.0
|
|
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
|
|
@@ -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=
|
|
97
|
-
tool_call_id=
|
|
98
|
-
|
|
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=
|
|
104
|
-
|
|
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(
|
|
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
|