ms-tau-sdk 1.2.7.dev7__tar.gz → 1.2.7.dev8__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.
- {ms_tau_sdk-1.2.7.dev7 → ms_tau_sdk-1.2.7.dev8}/CHANGELOG.md +9 -0
- {ms_tau_sdk-1.2.7.dev7 → ms_tau_sdk-1.2.7.dev8}/PKG-INFO +1 -1
- {ms_tau_sdk-1.2.7.dev7 → ms_tau_sdk-1.2.7.dev8}/pyproject.toml +1 -1
- {ms_tau_sdk-1.2.7.dev7 → ms_tau_sdk-1.2.7.dev8}/src/ms_tau_sdk/protocols/assistant_ui.py +49 -5
- {ms_tau_sdk-1.2.7.dev7 → ms_tau_sdk-1.2.7.dev8}/src/ms_tau_sdk/runtime/failures.py +33 -16
- {ms_tau_sdk-1.2.7.dev7 → ms_tau_sdk-1.2.7.dev8}/src/ms_tau_sdk/runtime/observability.py +1 -0
- {ms_tau_sdk-1.2.7.dev7 → ms_tau_sdk-1.2.7.dev8}/.gitignore +0 -0
- {ms_tau_sdk-1.2.7.dev7 → ms_tau_sdk-1.2.7.dev8}/README.md +0 -0
- {ms_tau_sdk-1.2.7.dev7 → ms_tau_sdk-1.2.7.dev8}/src/ms_tau_sdk/__init__.py +0 -0
- {ms_tau_sdk-1.2.7.dev7 → ms_tau_sdk-1.2.7.dev8}/src/ms_tau_sdk/agent_skills/tau_a2a_runtime_adapter/SKILL.md +0 -0
- {ms_tau_sdk-1.2.7.dev7 → ms_tau_sdk-1.2.7.dev8}/src/ms_tau_sdk/agent_skills/tau_local_development/SKILL.md +0 -0
- {ms_tau_sdk-1.2.7.dev7 → ms_tau_sdk-1.2.7.dev8}/src/ms_tau_sdk/agent_skills/tau_project_customization/SKILL.md +0 -0
- {ms_tau_sdk-1.2.7.dev7 → ms_tau_sdk-1.2.7.dev8}/src/ms_tau_sdk/agent_skills/tau_repository_integration/SKILL.md +0 -0
- {ms_tau_sdk-1.2.7.dev7 → ms_tau_sdk-1.2.7.dev8}/src/ms_tau_sdk/api/__init__.py +0 -0
- {ms_tau_sdk-1.2.7.dev7 → ms_tau_sdk-1.2.7.dev8}/src/ms_tau_sdk/api/a2a.py +0 -0
- {ms_tau_sdk-1.2.7.dev7 → ms_tau_sdk-1.2.7.dev8}/src/ms_tau_sdk/api/chat.py +0 -0
- {ms_tau_sdk-1.2.7.dev7 → ms_tau_sdk-1.2.7.dev8}/src/ms_tau_sdk/api/dependencies.py +0 -0
- {ms_tau_sdk-1.2.7.dev7 → ms_tau_sdk-1.2.7.dev8}/src/ms_tau_sdk/api/health.py +0 -0
- {ms_tau_sdk-1.2.7.dev7 → ms_tau_sdk-1.2.7.dev8}/src/ms_tau_sdk/api/models.py +0 -0
- {ms_tau_sdk-1.2.7.dev7 → ms_tau_sdk-1.2.7.dev8}/src/ms_tau_sdk/api/sessions.py +0 -0
- {ms_tau_sdk-1.2.7.dev7 → ms_tau_sdk-1.2.7.dev8}/src/ms_tau_sdk/app.py +0 -0
- {ms_tau_sdk-1.2.7.dev7 → ms_tau_sdk-1.2.7.dev8}/src/ms_tau_sdk/application.py +0 -0
- {ms_tau_sdk-1.2.7.dev7 → ms_tau_sdk-1.2.7.dev8}/src/ms_tau_sdk/backend/__init__.py +0 -0
- {ms_tau_sdk-1.2.7.dev7 → ms_tau_sdk-1.2.7.dev8}/src/ms_tau_sdk/backend/auth.py +0 -0
- {ms_tau_sdk-1.2.7.dev7 → ms_tau_sdk-1.2.7.dev8}/src/ms_tau_sdk/backend/client.py +0 -0
- {ms_tau_sdk-1.2.7.dev7 → ms_tau_sdk-1.2.7.dev8}/src/ms_tau_sdk/backend/local.py +0 -0
- {ms_tau_sdk-1.2.7.dev7 → ms_tau_sdk-1.2.7.dev8}/src/ms_tau_sdk/backend/mcp.py +0 -0
- {ms_tau_sdk-1.2.7.dev7 → ms_tau_sdk-1.2.7.dev8}/src/ms_tau_sdk/backend/models.py +0 -0
- {ms_tau_sdk-1.2.7.dev7 → ms_tau_sdk-1.2.7.dev8}/src/ms_tau_sdk/backend/routes.py +0 -0
- {ms_tau_sdk-1.2.7.dev7 → ms_tau_sdk-1.2.7.dev8}/src/ms_tau_sdk/cli.py +0 -0
- {ms_tau_sdk-1.2.7.dev7 → ms_tau_sdk-1.2.7.dev8}/src/ms_tau_sdk/errors.py +0 -0
- {ms_tau_sdk-1.2.7.dev7 → ms_tau_sdk-1.2.7.dev8}/src/ms_tau_sdk/logging.py +0 -0
- {ms_tau_sdk-1.2.7.dev7 → ms_tau_sdk-1.2.7.dev8}/src/ms_tau_sdk/protocols/__init__.py +0 -0
- {ms_tau_sdk-1.2.7.dev7 → ms_tau_sdk-1.2.7.dev8}/src/ms_tau_sdk/protocols/a2a_message.py +0 -0
- {ms_tau_sdk-1.2.7.dev7 → ms_tau_sdk-1.2.7.dev8}/src/ms_tau_sdk/protocols/a2a_roles.py +0 -0
- {ms_tau_sdk-1.2.7.dev7 → ms_tau_sdk-1.2.7.dev8}/src/ms_tau_sdk/protocols/strict_json.py +0 -0
- {ms_tau_sdk-1.2.7.dev7 → ms_tau_sdk-1.2.7.dev8}/src/ms_tau_sdk/providers/__init__.py +0 -0
- {ms_tau_sdk-1.2.7.dev7 → ms_tau_sdk-1.2.7.dev8}/src/ms_tau_sdk/providers/definitions.py +0 -0
- {ms_tau_sdk-1.2.7.dev7 → ms_tau_sdk-1.2.7.dev8}/src/ms_tau_sdk/providers/factory.py +0 -0
- {ms_tau_sdk-1.2.7.dev7 → ms_tau_sdk-1.2.7.dev8}/src/ms_tau_sdk/resources/SYSTEM.md +0 -0
- {ms_tau_sdk-1.2.7.dev7 → ms_tau_sdk-1.2.7.dev8}/src/ms_tau_sdk/resources/__init__.py +0 -0
- {ms_tau_sdk-1.2.7.dev7 → ms_tau_sdk-1.2.7.dev8}/src/ms_tau_sdk/resources/loader.py +0 -0
- {ms_tau_sdk-1.2.7.dev7 → ms_tau_sdk-1.2.7.dev8}/src/ms_tau_sdk/resources/prompts/review-code-repository.md +0 -0
- {ms_tau_sdk-1.2.7.dev7 → ms_tau_sdk-1.2.7.dev8}/src/ms_tau_sdk/runtime/__init__.py +0 -0
- {ms_tau_sdk-1.2.7.dev7 → ms_tau_sdk-1.2.7.dev8}/src/ms_tau_sdk/runtime/deployment_health.py +0 -0
- {ms_tau_sdk-1.2.7.dev7 → ms_tau_sdk-1.2.7.dev8}/src/ms_tau_sdk/runtime/events.py +0 -0
- {ms_tau_sdk-1.2.7.dev7 → ms_tau_sdk-1.2.7.dev8}/src/ms_tau_sdk/runtime/extensions.py +0 -0
- {ms_tau_sdk-1.2.7.dev7 → ms_tau_sdk-1.2.7.dev8}/src/ms_tau_sdk/runtime/manager.py +0 -0
- {ms_tau_sdk-1.2.7.dev7 → ms_tau_sdk-1.2.7.dev8}/src/ms_tau_sdk/runtime/provenance.py +0 -0
- {ms_tau_sdk-1.2.7.dev7 → ms_tau_sdk-1.2.7.dev8}/src/ms_tau_sdk/runtime/session.py +0 -0
- {ms_tau_sdk-1.2.7.dev7 → ms_tau_sdk-1.2.7.dev8}/src/ms_tau_sdk/runtime/snapshots.py +0 -0
- {ms_tau_sdk-1.2.7.dev7 → ms_tau_sdk-1.2.7.dev8}/src/ms_tau_sdk/runtime/task_context.py +0 -0
- {ms_tau_sdk-1.2.7.dev7 → ms_tau_sdk-1.2.7.dev8}/src/ms_tau_sdk/sessions/__init__.py +0 -0
- {ms_tau_sdk-1.2.7.dev7 → ms_tau_sdk-1.2.7.dev8}/src/ms_tau_sdk/sessions/storage.py +0 -0
- {ms_tau_sdk-1.2.7.dev7 → ms_tau_sdk-1.2.7.dev8}/src/ms_tau_sdk/settings.py +0 -0
- {ms_tau_sdk-1.2.7.dev7 → ms_tau_sdk-1.2.7.dev8}/src/ms_tau_sdk/skills.py +0 -0
- {ms_tau_sdk-1.2.7.dev7 → ms_tau_sdk-1.2.7.dev8}/src/ms_tau_sdk/tools/__init__.py +0 -0
- {ms_tau_sdk-1.2.7.dev7 → ms_tau_sdk-1.2.7.dev8}/src/ms_tau_sdk/tools/mainsequence_mcp.py +0 -0
- {ms_tau_sdk-1.2.7.dev7 → ms_tau_sdk-1.2.7.dev8}/src/ms_tau_sdk/tools/task_control.py +0 -0
|
@@ -2,6 +2,15 @@
|
|
|
2
2
|
|
|
3
3
|
## Unreleased
|
|
4
4
|
|
|
5
|
+
- Stopped discarding the only diagnostic a message-less provider failure carries. When Tau ends a
|
|
6
|
+
turn with `stopReason: "error"` and no `errorMessage`, the status code from the `provider_error`
|
|
7
|
+
diagnostic is composed into the message the user sees (`Provider error (HTTP 402)`) instead of
|
|
8
|
+
the bare `Provider error` constant, `agent.model.failed` now emits `status_code`, and the
|
|
9
|
+
assistant-ui `error` frame carries `status` and `error_code`. An unpaid account and a
|
|
10
|
+
misconfigured credential are distinguishable again, in the stream and in the logs. The
|
|
11
|
+
message/body split is unchanged: the provider's message is still forwarded verbatim and its raw
|
|
12
|
+
response body still never leaves the process.
|
|
13
|
+
|
|
5
14
|
## 1.2.6 — 2026-09-21
|
|
6
15
|
|
|
7
16
|
- Made `pyproject.toml` the only source of the version. `development` declares the release being
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: ms-tau-sdk
|
|
3
|
-
Version: 1.2.7.
|
|
3
|
+
Version: 1.2.7.dev8
|
|
4
4
|
Summary: Workspace-bound Tau application primitives for Main Sequence projects
|
|
5
5
|
Project-URL: Changelog, https://github.com/mainsequence-sdk/ms-tau-sdk/blob/development/CHANGELOG.md
|
|
6
6
|
Project-URL: Documentation, https://github.com/mainsequence-sdk/ms-tau-sdk/tree/development/docs
|
|
@@ -8,7 +8,9 @@ Chunk vocabulary (what a ``ui-message-stream`` consumer receives):
|
|
|
8
8
|
- ``tool-call-start`` / ``tool-call-delta`` (``argsText``) / ``tool-call-end``
|
|
9
9
|
when the model has finished a tool call, then ``tool-result`` (``result``,
|
|
10
10
|
``isError``) when the tool has run;
|
|
11
|
-
- ``data-runtime-lifecycle``, ``error`` and ``finish``.
|
|
11
|
+
- ``data-runtime-lifecycle``, ``error`` and ``finish``. An ``error`` chunk
|
|
12
|
+
carries ``errorText`` plus, when the runtime diagnosed them, the ``status``
|
|
13
|
+
and ``error_code`` fields the Command Center stream consumer reads.
|
|
12
14
|
|
|
13
15
|
The tool chunk names are the ones assistant-stream's ``UIMessageStreamDecoder``
|
|
14
16
|
understands; the earlier ``tool-input-available`` / ``tool-output-available``
|
|
@@ -34,6 +36,8 @@ class AssistantUiEncoder:
|
|
|
34
36
|
tool_names: dict[str, str] = field(default_factory=dict)
|
|
35
37
|
finished: bool = False
|
|
36
38
|
terminal_error_text: str | None = None
|
|
39
|
+
terminal_error_status: int | None = None
|
|
40
|
+
terminal_error_code: str | None = None
|
|
37
41
|
|
|
38
42
|
def _tool_call_chunks(
|
|
39
43
|
self,
|
|
@@ -161,15 +165,25 @@ class AssistantUiEncoder:
|
|
|
161
165
|
if event_type == "message_end":
|
|
162
166
|
failure = terminal_assistant_failure(data)
|
|
163
167
|
if failure is not None:
|
|
164
|
-
self.
|
|
168
|
+
self._set_terminal_error(
|
|
169
|
+
failure.message,
|
|
170
|
+
status_code=failure.status_code,
|
|
171
|
+
error_code=failure.error_type,
|
|
172
|
+
)
|
|
165
173
|
elif is_terminal_assistant_message(data):
|
|
166
174
|
# A later successful terminal message means Tau recovered from
|
|
167
175
|
# an earlier error, for example through overflow compaction.
|
|
168
|
-
self.
|
|
176
|
+
self._set_terminal_error(None)
|
|
169
177
|
return []
|
|
170
178
|
if event_type == "error":
|
|
171
179
|
if self.terminal_error_text is None:
|
|
172
|
-
|
|
180
|
+
error = data.get("error")
|
|
181
|
+
error = error if isinstance(error, dict) else {}
|
|
182
|
+
self._set_terminal_error(
|
|
183
|
+
_assistant_error_message(data),
|
|
184
|
+
status_code=_error_status_code(error),
|
|
185
|
+
error_code=_error_code(error),
|
|
186
|
+
)
|
|
173
187
|
return []
|
|
174
188
|
if event_type == "agent_settled" and not self.finished:
|
|
175
189
|
if self.terminal_error_text is not None:
|
|
@@ -178,13 +192,29 @@ class AssistantUiEncoder:
|
|
|
178
192
|
return [{"type": "finish", "finishReason": "stop"}]
|
|
179
193
|
return []
|
|
180
194
|
|
|
195
|
+
def _set_terminal_error(
|
|
196
|
+
self,
|
|
197
|
+
text: str | None,
|
|
198
|
+
*,
|
|
199
|
+
status_code: int | None = None,
|
|
200
|
+
error_code: str | None = None,
|
|
201
|
+
) -> None:
|
|
202
|
+
self.terminal_error_text = text
|
|
203
|
+
self.terminal_error_status = status_code if text is not None else None
|
|
204
|
+
self.terminal_error_code = error_code if text is not None else None
|
|
205
|
+
|
|
181
206
|
def finalize(self) -> list[dict[str, Any]]:
|
|
182
207
|
"""Emit the one terminal frame after the runtime has finished settling."""
|
|
183
208
|
if self.finished:
|
|
184
209
|
return []
|
|
185
210
|
self.finished = True
|
|
186
211
|
if self.terminal_error_text is not None:
|
|
187
|
-
|
|
212
|
+
frame: dict[str, Any] = {"type": "error", "errorText": self.terminal_error_text}
|
|
213
|
+
if self.terminal_error_status is not None:
|
|
214
|
+
frame["status"] = self.terminal_error_status
|
|
215
|
+
if self.terminal_error_code is not None:
|
|
216
|
+
frame["error_code"] = self.terminal_error_code
|
|
217
|
+
return [frame]
|
|
188
218
|
return [{"type": "finish", "finishReason": "stop"}]
|
|
189
219
|
|
|
190
220
|
@staticmethod
|
|
@@ -211,6 +241,20 @@ def _step_finish_reason(data: dict[str, Any]) -> str:
|
|
|
211
241
|
return _STEP_FINISH_REASONS.get(str(stop_reason or ""), "stop")
|
|
212
242
|
|
|
213
243
|
|
|
244
|
+
def _error_status_code(error: dict[str, Any]) -> int | None:
|
|
245
|
+
candidate = error.get("status_code", error.get("statusCode", error.get("status")))
|
|
246
|
+
if isinstance(candidate, bool) or not isinstance(candidate, int):
|
|
247
|
+
return None
|
|
248
|
+
return candidate
|
|
249
|
+
|
|
250
|
+
|
|
251
|
+
def _error_code(error: dict[str, Any]) -> str | None:
|
|
252
|
+
candidate = error.get("errorType", error.get("error_type"))
|
|
253
|
+
if not isinstance(candidate, str) or not candidate.strip():
|
|
254
|
+
return None
|
|
255
|
+
return candidate
|
|
256
|
+
|
|
257
|
+
|
|
214
258
|
def _assistant_error_message(data: dict[str, Any]) -> str:
|
|
215
259
|
error = data.get("error", {})
|
|
216
260
|
if isinstance(error, dict):
|
|
@@ -30,24 +30,14 @@ def terminal_assistant_failure(
|
|
|
30
30
|
if reason not in {"error", "aborted"}:
|
|
31
31
|
return None
|
|
32
32
|
|
|
33
|
+
provider_error, status_code = _provider_error(message.get("diagnostics"))
|
|
34
|
+
|
|
33
35
|
error_message = _nonempty_text(message.get("errorMessage", message.get("error_message")))
|
|
34
36
|
if error_message is None:
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
diagnostics = message.get("diagnostics")
|
|
40
|
-
if isinstance(diagnostics, list):
|
|
41
|
-
for diagnostic in diagnostics:
|
|
42
|
-
if not isinstance(diagnostic, Mapping) or diagnostic.get("type") != "provider_error":
|
|
43
|
-
continue
|
|
44
|
-
provider_error = True
|
|
45
|
-
details = diagnostic.get("details")
|
|
46
|
-
if isinstance(details, Mapping):
|
|
47
|
-
candidate = details.get("status_code")
|
|
48
|
-
if isinstance(candidate, int) and not isinstance(candidate, bool):
|
|
49
|
-
status_code = candidate
|
|
50
|
-
break
|
|
37
|
+
# Tau can end a turn with ``stopReason: "error"`` and no message at all.
|
|
38
|
+
# Returning the bare constant would discard the only diagnostic left, so
|
|
39
|
+
# the status code is composed into the message the caller sees.
|
|
40
|
+
error_message = _composed_message(reason=reason, status_code=status_code)
|
|
51
41
|
|
|
52
42
|
return TerminalAssistantFailure(
|
|
53
43
|
reason=reason,
|
|
@@ -72,6 +62,33 @@ def is_terminal_assistant_message(data: Mapping[str, Any]) -> bool:
|
|
|
72
62
|
return isinstance(reason, str) and bool(reason)
|
|
73
63
|
|
|
74
64
|
|
|
65
|
+
def _provider_error(diagnostics: object) -> tuple[bool, int | None]:
|
|
66
|
+
"""Return whether a provider error was diagnosed, and its status code.
|
|
67
|
+
|
|
68
|
+
Only the status code is read out of ``details``; the raw provider response
|
|
69
|
+
body that sits beside it never leaves this function.
|
|
70
|
+
"""
|
|
71
|
+
if not isinstance(diagnostics, list):
|
|
72
|
+
return False, None
|
|
73
|
+
for diagnostic in diagnostics:
|
|
74
|
+
if not isinstance(diagnostic, Mapping) or diagnostic.get("type") != "provider_error":
|
|
75
|
+
continue
|
|
76
|
+
details = diagnostic.get("details")
|
|
77
|
+
if isinstance(details, Mapping):
|
|
78
|
+
candidate = details.get("status_code")
|
|
79
|
+
if isinstance(candidate, int) and not isinstance(candidate, bool):
|
|
80
|
+
return True, candidate
|
|
81
|
+
return True, None
|
|
82
|
+
return False, None
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def _composed_message(*, reason: str, status_code: int | None) -> str:
|
|
86
|
+
fallback = "Agent turn was aborted" if reason == "aborted" else "Provider error"
|
|
87
|
+
if status_code is None:
|
|
88
|
+
return fallback
|
|
89
|
+
return f"{fallback} (HTTP {status_code})"
|
|
90
|
+
|
|
91
|
+
|
|
75
92
|
def _nonempty_text(value: object) -> str | None:
|
|
76
93
|
if not isinstance(value, str) or not value.strip():
|
|
77
94
|
return None
|
|
@@ -229,6 +229,7 @@ class TauTurnObserver:
|
|
|
229
229
|
model_attempt=attempt,
|
|
230
230
|
model_duration_ms=round((time.monotonic() - started) * 1000, 3),
|
|
231
231
|
model_error_type=error_type,
|
|
232
|
+
status_code=status_code,
|
|
232
233
|
provider_request_id=_bounded(_first(data, "provider_request_id", "response_id")),
|
|
233
234
|
rate_limited=rate_limited,
|
|
234
235
|
retryable=retryable,
|
|
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
|