ms-tau-sdk 2.0.9.dev48__tar.gz → 2.0.10.dev50__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-2.0.9.dev48 → ms_tau_sdk-2.0.10.dev50}/CHANGELOG.md +27 -1
- {ms_tau_sdk-2.0.9.dev48 → ms_tau_sdk-2.0.10.dev50}/PKG-INFO +1 -1
- {ms_tau_sdk-2.0.9.dev48 → ms_tau_sdk-2.0.10.dev50}/pyproject.toml +1 -1
- {ms_tau_sdk-2.0.9.dev48 → ms_tau_sdk-2.0.10.dev50}/src/ms_tau_sdk/agent_skills/tau_project_customization/SKILL.md +10 -0
- {ms_tau_sdk-2.0.9.dev48 → ms_tau_sdk-2.0.10.dev50}/src/ms_tau_sdk/backend/client.py +15 -0
- {ms_tau_sdk-2.0.9.dev48 → ms_tau_sdk-2.0.10.dev50}/src/ms_tau_sdk/backend/mcp.py +32 -10
- {ms_tau_sdk-2.0.9.dev48 → ms_tau_sdk-2.0.10.dev50}/src/ms_tau_sdk/errors.py +8 -0
- {ms_tau_sdk-2.0.9.dev48 → ms_tau_sdk-2.0.10.dev50}/src/ms_tau_sdk/runtime/manager.py +2 -0
- {ms_tau_sdk-2.0.9.dev48 → ms_tau_sdk-2.0.10.dev50}/src/ms_tau_sdk/runtime/requester.py +7 -3
- {ms_tau_sdk-2.0.9.dev48 → ms_tau_sdk-2.0.10.dev50}/src/ms_tau_sdk/settings.py +6 -0
- {ms_tau_sdk-2.0.9.dev48 → ms_tau_sdk-2.0.10.dev50}/src/ms_tau_sdk/tools/mcp_applications.py +179 -29
- {ms_tau_sdk-2.0.9.dev48 → ms_tau_sdk-2.0.10.dev50}/.gitignore +0 -0
- {ms_tau_sdk-2.0.9.dev48 → ms_tau_sdk-2.0.10.dev50}/README.md +0 -0
- {ms_tau_sdk-2.0.9.dev48 → ms_tau_sdk-2.0.10.dev50}/packages/tau-board/src/ms_tau_board/__init__.py +0 -0
- {ms_tau_sdk-2.0.9.dev48 → ms_tau_sdk-2.0.10.dev50}/packages/tau-board/src/ms_tau_board/app.py +0 -0
- {ms_tau_sdk-2.0.9.dev48 → ms_tau_sdk-2.0.10.dev50}/packages/tau-board/src/ms_tau_board/cli.py +0 -0
- {ms_tau_sdk-2.0.9.dev48 → ms_tau_sdk-2.0.10.dev50}/packages/tau-board/src/ms_tau_board/config.py +0 -0
- {ms_tau_sdk-2.0.9.dev48 → ms_tau_sdk-2.0.10.dev50}/packages/tau-board/src/ms_tau_board/env_file.py +0 -0
- {ms_tau_sdk-2.0.9.dev48 → ms_tau_sdk-2.0.10.dev50}/packages/tau-board/src/ms_tau_board/logs.py +0 -0
- {ms_tau_sdk-2.0.9.dev48 → ms_tau_sdk-2.0.10.dev50}/packages/tau-board/src/ms_tau_board/proxy.py +0 -0
- {ms_tau_sdk-2.0.9.dev48 → ms_tau_sdk-2.0.10.dev50}/packages/tau-board/src/ms_tau_board/state.py +0 -0
- {ms_tau_sdk-2.0.9.dev48 → ms_tau_sdk-2.0.10.dev50}/packages/tau-board/src/ms_tau_board/static/BULMA-LICENSE.txt +0 -0
- {ms_tau_sdk-2.0.9.dev48 → ms_tau_sdk-2.0.10.dev50}/packages/tau-board/src/ms_tau_board/static/app.js +0 -0
- {ms_tau_sdk-2.0.9.dev48 → ms_tau_sdk-2.0.10.dev50}/packages/tau-board/src/ms_tau_board/static/board.css +0 -0
- {ms_tau_sdk-2.0.9.dev48 → ms_tau_sdk-2.0.10.dev50}/packages/tau-board/src/ms_tau_board/static/bulma.min.css +0 -0
- {ms_tau_sdk-2.0.9.dev48 → ms_tau_sdk-2.0.10.dev50}/packages/tau-board/src/ms_tau_board/static/index.html +0 -0
- {ms_tau_sdk-2.0.9.dev48 → ms_tau_sdk-2.0.10.dev50}/src/ms_tau_sdk/__init__.py +0 -0
- {ms_tau_sdk-2.0.9.dev48 → ms_tau_sdk-2.0.10.dev50}/src/ms_tau_sdk/agent_skills/tau_a2a_runtime_adapter/SKILL.md +0 -0
- {ms_tau_sdk-2.0.9.dev48 → ms_tau_sdk-2.0.10.dev50}/src/ms_tau_sdk/agent_skills/tau_local_development/SKILL.md +0 -0
- {ms_tau_sdk-2.0.9.dev48 → ms_tau_sdk-2.0.10.dev50}/src/ms_tau_sdk/agent_skills/tau_repository_integration/SKILL.md +0 -0
- {ms_tau_sdk-2.0.9.dev48 → ms_tau_sdk-2.0.10.dev50}/src/ms_tau_sdk/agent_skills/tau_security_and_access/SKILL.md +0 -0
- {ms_tau_sdk-2.0.9.dev48 → ms_tau_sdk-2.0.10.dev50}/src/ms_tau_sdk/api/__init__.py +0 -0
- {ms_tau_sdk-2.0.9.dev48 → ms_tau_sdk-2.0.10.dev50}/src/ms_tau_sdk/api/a2a.py +0 -0
- {ms_tau_sdk-2.0.9.dev48 → ms_tau_sdk-2.0.10.dev50}/src/ms_tau_sdk/api/chat.py +0 -0
- {ms_tau_sdk-2.0.9.dev48 → ms_tau_sdk-2.0.10.dev50}/src/ms_tau_sdk/api/conversations.py +0 -0
- {ms_tau_sdk-2.0.9.dev48 → ms_tau_sdk-2.0.10.dev50}/src/ms_tau_sdk/api/dependencies.py +0 -0
- {ms_tau_sdk-2.0.9.dev48 → ms_tau_sdk-2.0.10.dev50}/src/ms_tau_sdk/api/health.py +0 -0
- {ms_tau_sdk-2.0.9.dev48 → ms_tau_sdk-2.0.10.dev50}/src/ms_tau_sdk/api/inspection.py +0 -0
- {ms_tau_sdk-2.0.9.dev48 → ms_tau_sdk-2.0.10.dev50}/src/ms_tau_sdk/api/local_chat.py +0 -0
- {ms_tau_sdk-2.0.9.dev48 → ms_tau_sdk-2.0.10.dev50}/src/ms_tau_sdk/api/models.py +0 -0
- {ms_tau_sdk-2.0.9.dev48 → ms_tau_sdk-2.0.10.dev50}/src/ms_tau_sdk/api/request_identity.py +0 -0
- {ms_tau_sdk-2.0.9.dev48 → ms_tau_sdk-2.0.10.dev50}/src/ms_tau_sdk/api/sessions.py +0 -0
- {ms_tau_sdk-2.0.9.dev48 → ms_tau_sdk-2.0.10.dev50}/src/ms_tau_sdk/app.py +0 -0
- {ms_tau_sdk-2.0.9.dev48 → ms_tau_sdk-2.0.10.dev50}/src/ms_tau_sdk/application.py +0 -0
- {ms_tau_sdk-2.0.9.dev48 → ms_tau_sdk-2.0.10.dev50}/src/ms_tau_sdk/backend/__init__.py +0 -0
- {ms_tau_sdk-2.0.9.dev48 → ms_tau_sdk-2.0.10.dev50}/src/ms_tau_sdk/backend/assertions.py +0 -0
- {ms_tau_sdk-2.0.9.dev48 → ms_tau_sdk-2.0.10.dev50}/src/ms_tau_sdk/backend/auth.py +0 -0
- {ms_tau_sdk-2.0.9.dev48 → ms_tau_sdk-2.0.10.dev50}/src/ms_tau_sdk/backend/local.py +0 -0
- {ms_tau_sdk-2.0.9.dev48 → ms_tau_sdk-2.0.10.dev50}/src/ms_tau_sdk/backend/models.py +0 -0
- {ms_tau_sdk-2.0.9.dev48 → ms_tau_sdk-2.0.10.dev50}/src/ms_tau_sdk/backend/routes.py +0 -0
- {ms_tau_sdk-2.0.9.dev48 → ms_tau_sdk-2.0.10.dev50}/src/ms_tau_sdk/cli.py +0 -0
- {ms_tau_sdk-2.0.9.dev48 → ms_tau_sdk-2.0.10.dev50}/src/ms_tau_sdk/logging.py +0 -0
- {ms_tau_sdk-2.0.9.dev48 → ms_tau_sdk-2.0.10.dev50}/src/ms_tau_sdk/protocols/__init__.py +0 -0
- {ms_tau_sdk-2.0.9.dev48 → ms_tau_sdk-2.0.10.dev50}/src/ms_tau_sdk/protocols/a2a_failure.py +0 -0
- {ms_tau_sdk-2.0.9.dev48 → ms_tau_sdk-2.0.10.dev50}/src/ms_tau_sdk/protocols/a2a_message.py +0 -0
- {ms_tau_sdk-2.0.9.dev48 → ms_tau_sdk-2.0.10.dev50}/src/ms_tau_sdk/protocols/a2a_roles.py +0 -0
- {ms_tau_sdk-2.0.9.dev48 → ms_tau_sdk-2.0.10.dev50}/src/ms_tau_sdk/protocols/assistant_ui.py +0 -0
- {ms_tau_sdk-2.0.9.dev48 → ms_tau_sdk-2.0.10.dev50}/src/ms_tau_sdk/protocols/chat_history.py +0 -0
- {ms_tau_sdk-2.0.9.dev48 → ms_tau_sdk-2.0.10.dev50}/src/ms_tau_sdk/protocols/strict_json.py +0 -0
- {ms_tau_sdk-2.0.9.dev48 → ms_tau_sdk-2.0.10.dev50}/src/ms_tau_sdk/providers/__init__.py +0 -0
- {ms_tau_sdk-2.0.9.dev48 → ms_tau_sdk-2.0.10.dev50}/src/ms_tau_sdk/providers/definitions.py +0 -0
- {ms_tau_sdk-2.0.9.dev48 → ms_tau_sdk-2.0.10.dev50}/src/ms_tau_sdk/providers/factory.py +0 -0
- {ms_tau_sdk-2.0.9.dev48 → ms_tau_sdk-2.0.10.dev50}/src/ms_tau_sdk/providers/tau_compat.py +0 -0
- {ms_tau_sdk-2.0.9.dev48 → ms_tau_sdk-2.0.10.dev50}/src/ms_tau_sdk/resources/SYSTEM.md +0 -0
- {ms_tau_sdk-2.0.9.dev48 → ms_tau_sdk-2.0.10.dev50}/src/ms_tau_sdk/resources/__init__.py +0 -0
- {ms_tau_sdk-2.0.9.dev48 → ms_tau_sdk-2.0.10.dev50}/src/ms_tau_sdk/resources/loader.py +0 -0
- {ms_tau_sdk-2.0.9.dev48 → ms_tau_sdk-2.0.10.dev50}/src/ms_tau_sdk/resources/prompts/review-code-repository.md +0 -0
- {ms_tau_sdk-2.0.9.dev48 → ms_tau_sdk-2.0.10.dev50}/src/ms_tau_sdk/runtime/__init__.py +0 -0
- {ms_tau_sdk-2.0.9.dev48 → ms_tau_sdk-2.0.10.dev50}/src/ms_tau_sdk/runtime/deployment_health.py +0 -0
- {ms_tau_sdk-2.0.9.dev48 → ms_tau_sdk-2.0.10.dev50}/src/ms_tau_sdk/runtime/events.py +0 -0
- {ms_tau_sdk-2.0.9.dev48 → ms_tau_sdk-2.0.10.dev50}/src/ms_tau_sdk/runtime/extensions.py +0 -0
- {ms_tau_sdk-2.0.9.dev48 → ms_tau_sdk-2.0.10.dev50}/src/ms_tau_sdk/runtime/failures.py +0 -0
- {ms_tau_sdk-2.0.9.dev48 → ms_tau_sdk-2.0.10.dev50}/src/ms_tau_sdk/runtime/live_turns.py +0 -0
- {ms_tau_sdk-2.0.9.dev48 → ms_tau_sdk-2.0.10.dev50}/src/ms_tau_sdk/runtime/observability.py +0 -0
- {ms_tau_sdk-2.0.9.dev48 → ms_tau_sdk-2.0.10.dev50}/src/ms_tau_sdk/runtime/provenance.py +0 -0
- {ms_tau_sdk-2.0.9.dev48 → ms_tau_sdk-2.0.10.dev50}/src/ms_tau_sdk/runtime/session.py +0 -0
- {ms_tau_sdk-2.0.9.dev48 → ms_tau_sdk-2.0.10.dev50}/src/ms_tau_sdk/runtime/snapshots.py +0 -0
- {ms_tau_sdk-2.0.9.dev48 → ms_tau_sdk-2.0.10.dev50}/src/ms_tau_sdk/runtime/task_context.py +0 -0
- {ms_tau_sdk-2.0.9.dev48 → ms_tau_sdk-2.0.10.dev50}/src/ms_tau_sdk/sessions/__init__.py +0 -0
- {ms_tau_sdk-2.0.9.dev48 → ms_tau_sdk-2.0.10.dev50}/src/ms_tau_sdk/sessions/storage.py +0 -0
- {ms_tau_sdk-2.0.9.dev48 → ms_tau_sdk-2.0.10.dev50}/src/ms_tau_sdk/skills.py +0 -0
- {ms_tau_sdk-2.0.9.dev48 → ms_tau_sdk-2.0.10.dev50}/src/ms_tau_sdk/tools/__init__.py +0 -0
- {ms_tau_sdk-2.0.9.dev48 → ms_tau_sdk-2.0.10.dev50}/src/ms_tau_sdk/tools/mainsequence_mcp.py +0 -0
- {ms_tau_sdk-2.0.9.dev48 → ms_tau_sdk-2.0.10.dev50}/src/ms_tau_sdk/tools/mcp_connection.py +0 -0
- {ms_tau_sdk-2.0.9.dev48 → ms_tau_sdk-2.0.10.dev50}/src/ms_tau_sdk/tools/skill_read.py +0 -0
- {ms_tau_sdk-2.0.9.dev48 → ms_tau_sdk-2.0.10.dev50}/src/ms_tau_sdk/tools/task_control.py +0 -0
|
@@ -1,6 +1,14 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
-
##
|
|
3
|
+
## 2.0.10 — 2026-10-09
|
|
4
|
+
|
|
5
|
+
- **Application access timeouts no longer become `access_unavailable`.** Platform transport
|
|
6
|
+
timeouts retain their classification and time limit when application access is obtained or
|
|
7
|
+
renewed. The tool reports `failure: timeout` with `phase: access`; HTTP 408/504 responses
|
|
8
|
+
also report a timeout with their status, without claiming the tool's configured limit elapsed.
|
|
9
|
+
Other access failures retain their classification. Tool calls are never replayed after failure.
|
|
10
|
+
|
|
11
|
+
## 2.0.9 — 2026-10-09
|
|
4
12
|
|
|
5
13
|
- **Turn logs name the session's Agent
|
|
6
14
|
([#85](https://github.com/mainsequence-sdk/ms-tau-sdk/issues/85)).** Every log line of a turn
|
|
@@ -9,6 +17,24 @@
|
|
|
9
17
|
the session loads, or, before the session has loaded, the one its Agent card names (one extra
|
|
10
18
|
request, only then). Local turns carry the workspace's local Agent,
|
|
11
19
|
`local-agent-<workspace digest>`.
|
|
20
|
+
- **Application tool calls get their own time limits, and failures say what happened
|
|
21
|
+
([#84](https://github.com/mainsequence-sdk/ms-tau-sdk/issues/84), ADR 0022).**
|
|
22
|
+
- A call that met an error status from the application or its gateway, such as 403, 502 or 503,
|
|
23
|
+
waited until the turn's 15-minute limit. It now ends at once with that status. The Main
|
|
24
|
+
Sequence MCP connection had the same defect and is fixed too.
|
|
25
|
+
- Each application tool declares how long it may run in its MCP `_meta` under
|
|
26
|
+
`mainsequence.ai/timeout-seconds/v1`, for example
|
|
27
|
+
`@mcp.tool(meta={"mainsequence.ai/timeout-seconds/v1": 300})`. A tool that declares nothing
|
|
28
|
+
gets the new `TAU_MCP_TOOL_TIMEOUT_SECONDS` (default `60`).
|
|
29
|
+
`MAINSEQUENCE_TAU_BACKEND_READ_TIMEOUT_SECONDS` no longer limits the call itself. To find the
|
|
30
|
+
limit, `<name>__call_tool` now reads the application's tool list before each call.
|
|
31
|
+
- The model reads what failed, for example "The orders tool rebuild_report did not answer
|
|
32
|
+
within 300 seconds." or "The orders application answered with a server error (502).", instead
|
|
33
|
+
of "could not be reached" for every failure. Details and the
|
|
34
|
+
`runtime.mcp_application.failed` log event carry `failure`, `status` and `timeout_seconds`.
|
|
35
|
+
- Opening the application's session is tried once more when it fails on the connection, a
|
|
36
|
+
time-out or a server error. A call that was sent is never sent again.
|
|
37
|
+
- See [Application tool time limits](docs/guides/application-tool-time-limits.md).
|
|
12
38
|
|
|
13
39
|
## 2.0.8 — 2026-10-08
|
|
14
40
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: ms-tau-sdk
|
|
3
|
-
Version: 2.0.
|
|
3
|
+
Version: 2.0.10.dev50
|
|
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
|
|
@@ -81,6 +81,16 @@ in the system prompt because a tool named `read` exists. Extensions must not reg
|
|
|
81
81
|
this mode.
|
|
82
82
|
Excluding coding tools does not sandbox extension Python code.
|
|
83
83
|
|
|
84
|
+
## Application tool time limits
|
|
85
|
+
|
|
86
|
+
A declared application's tool sets how long it may run, in seconds, in its MCP `_meta` under
|
|
87
|
+
`mainsequence.ai/timeout-seconds/v1`; with the MCP Python SDK that is
|
|
88
|
+
`@mcp.tool(meta={"mainsequence.ai/timeout-seconds/v1": 300})` in the application. Set the limit on
|
|
89
|
+
the application's tool, not in the Agent. A tool that declares nothing gets
|
|
90
|
+
`TAU_MCP_TOOL_TIMEOUT_SECONDS` (default `60`); set it like the tool composition settings above, in
|
|
91
|
+
`harness_agent.spec.env_vars`, the process environment, or
|
|
92
|
+
`TauSDKSettings(mcp_tool_timeout_seconds=...)`. No call outlives its turn.
|
|
93
|
+
|
|
84
94
|
## Invariants extensions must not replace
|
|
85
95
|
|
|
86
96
|
Extensions may change effective agent capabilities, but they must not replace or bypass:
|
|
@@ -19,6 +19,7 @@ from tau_coding.oauth import account_id_from_access_token
|
|
|
19
19
|
from ms_tau_sdk.errors import (
|
|
20
20
|
BackendConflictError,
|
|
21
21
|
BackendError,
|
|
22
|
+
BackendTimeoutError,
|
|
22
23
|
LocalModeUnsupportedError,
|
|
23
24
|
SessionNotFoundError,
|
|
24
25
|
)
|
|
@@ -372,6 +373,20 @@ class MainSequenceClient:
|
|
|
372
373
|
if transport_attempt >= attempts:
|
|
373
374
|
break
|
|
374
375
|
await asyncio.sleep(0.25 * (2 ** (transport_attempt - 1)))
|
|
376
|
+
if isinstance(last_error, httpx.TimeoutException):
|
|
377
|
+
timeout_phase = (
|
|
378
|
+
"connect"
|
|
379
|
+
if isinstance(last_error, httpx.ConnectTimeout)
|
|
380
|
+
else "write"
|
|
381
|
+
if isinstance(last_error, httpx.WriteTimeout)
|
|
382
|
+
else "pool"
|
|
383
|
+
if isinstance(last_error, httpx.PoolTimeout)
|
|
384
|
+
else "read"
|
|
385
|
+
)
|
|
386
|
+
raise BackendTimeoutError(
|
|
387
|
+
"Backend request timed out",
|
|
388
|
+
timeout_seconds=getattr(self._client.timeout, timeout_phase),
|
|
389
|
+
) from last_error
|
|
375
390
|
raise BackendError(f"Backend transport failed: {last_error}") from last_error
|
|
376
391
|
|
|
377
392
|
@staticmethod
|
|
@@ -7,7 +7,7 @@ import contextvars
|
|
|
7
7
|
from collections.abc import AsyncGenerator
|
|
8
8
|
from dataclasses import dataclass
|
|
9
9
|
from datetime import timedelta
|
|
10
|
-
from typing import Literal, Self
|
|
10
|
+
from typing import Any, Literal, Self
|
|
11
11
|
|
|
12
12
|
import httpx
|
|
13
13
|
from mcp import ClientSession, types
|
|
@@ -52,6 +52,7 @@ class _CallToolCommand:
|
|
|
52
52
|
name: str
|
|
53
53
|
arguments: dict[str, object]
|
|
54
54
|
meta: dict[str, object] | None
|
|
55
|
+
timeout_seconds: float | None
|
|
55
56
|
log_context: dict[str, object]
|
|
56
57
|
result: asyncio.Future[types.CallToolResult]
|
|
57
58
|
|
|
@@ -99,10 +100,8 @@ class MCPConnectionClient:
|
|
|
99
100
|
timeout: httpx.Timeout,
|
|
100
101
|
read_timeout_seconds: float,
|
|
101
102
|
read_concurrency: int,
|
|
102
|
-
read_catalog: bool = True,
|
|
103
103
|
) -> None:
|
|
104
104
|
self.name = name
|
|
105
|
-
self._read_catalog = read_catalog
|
|
106
105
|
self.display_name = display_name
|
|
107
106
|
self.url = url
|
|
108
107
|
self._http_auth = auth
|
|
@@ -113,6 +112,9 @@ class MCPConnectionClient:
|
|
|
113
112
|
self._owner_task: asyncio.Task[None] | None = None
|
|
114
113
|
self._ready: asyncio.Future[None] | None = None
|
|
115
114
|
self._failure: Exception | None = None
|
|
115
|
+
# The results of the calls already handed to the session, so that a failed connection
|
|
116
|
+
# ends them as well as the calls still queued.
|
|
117
|
+
self._in_flight: set[asyncio.Future[Any]] = set()
|
|
116
118
|
self.tools: tuple[types.Tool, ...] = ()
|
|
117
119
|
self.resources: tuple[types.Resource, ...] = ()
|
|
118
120
|
self._parallel_tool_names: frozenset[str] = frozenset()
|
|
@@ -198,8 +200,6 @@ class MCPConnectionClient:
|
|
|
198
200
|
session: ClientSession,
|
|
199
201
|
initialized: types.InitializeResult | None,
|
|
200
202
|
) -> None:
|
|
201
|
-
if not self._read_catalog:
|
|
202
|
-
return
|
|
203
203
|
# Read only what the server offers; a server without resources answers "not found".
|
|
204
204
|
capabilities = initialized.capabilities if initialized is not None else None
|
|
205
205
|
offers_tools = capabilities is None or capabilities.tools is not None
|
|
@@ -219,14 +219,23 @@ class MCPConnectionClient:
|
|
|
219
219
|
read_slots = asyncio.Semaphore(self._read_concurrency)
|
|
220
220
|
|
|
221
221
|
async def execute(command: _CallToolCommand | _ReadResourceCommand) -> None:
|
|
222
|
+
self._in_flight.add(command.result)
|
|
222
223
|
try:
|
|
223
224
|
with bound_contextvars(**command.log_context):
|
|
224
225
|
if isinstance(command, _CallToolCommand):
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
226
|
+
if command.timeout_seconds is None:
|
|
227
|
+
tool_result = await session.call_tool(
|
|
228
|
+
command.name,
|
|
229
|
+
command.arguments,
|
|
230
|
+
meta=command.meta,
|
|
231
|
+
)
|
|
232
|
+
else:
|
|
233
|
+
tool_result = await session.call_tool(
|
|
234
|
+
command.name,
|
|
235
|
+
command.arguments,
|
|
236
|
+
read_timeout_seconds=timedelta(seconds=command.timeout_seconds),
|
|
237
|
+
meta=command.meta,
|
|
238
|
+
)
|
|
230
239
|
if not command.result.done():
|
|
231
240
|
command.result.set_result(tool_result)
|
|
232
241
|
else:
|
|
@@ -236,6 +245,11 @@ class MCPConnectionClient:
|
|
|
236
245
|
except Exception as error:
|
|
237
246
|
if not command.result.done():
|
|
238
247
|
command.result.set_exception(error)
|
|
248
|
+
finally:
|
|
249
|
+
# A transport failure cancels the session's work before its cause is known; the
|
|
250
|
+
# owner task then ends this call with that cause.
|
|
251
|
+
if command.result.done():
|
|
252
|
+
self._in_flight.discard(command.result)
|
|
239
253
|
|
|
240
254
|
async def execute_read(
|
|
241
255
|
command: _CallToolCommand | _ReadResourceCommand,
|
|
@@ -271,6 +285,10 @@ class MCPConnectionClient:
|
|
|
271
285
|
await asyncio.gather(*tuple(active_reads), return_exceptions=True)
|
|
272
286
|
|
|
273
287
|
def _fail_pending_commands(self, error: Exception) -> None:
|
|
288
|
+
for result in self._in_flight:
|
|
289
|
+
if not result.done():
|
|
290
|
+
result.set_exception(error)
|
|
291
|
+
self._in_flight.clear()
|
|
274
292
|
if self._commands is None:
|
|
275
293
|
return
|
|
276
294
|
while not self._commands.empty():
|
|
@@ -293,7 +311,10 @@ class MCPConnectionClient:
|
|
|
293
311
|
arguments: dict[str, object],
|
|
294
312
|
*,
|
|
295
313
|
meta: dict[str, object] | None = None,
|
|
314
|
+
timeout_seconds: float | None = None,
|
|
296
315
|
) -> types.CallToolResult:
|
|
316
|
+
"""Call one tool; ``timeout_seconds`` replaces the session's wait for its answer."""
|
|
317
|
+
|
|
297
318
|
commands = self._require_commands()
|
|
298
319
|
result: asyncio.Future[types.CallToolResult] = asyncio.get_running_loop().create_future()
|
|
299
320
|
commands.put_nowait(
|
|
@@ -301,6 +322,7 @@ class MCPConnectionClient:
|
|
|
301
322
|
name=name,
|
|
302
323
|
arguments=arguments,
|
|
303
324
|
meta=meta,
|
|
325
|
+
timeout_seconds=timeout_seconds,
|
|
304
326
|
log_context=dict(get_contextvars()),
|
|
305
327
|
result=result,
|
|
306
328
|
)
|
|
@@ -37,6 +37,14 @@ class BackendConflictError(BackendError):
|
|
|
37
37
|
status_code = 409
|
|
38
38
|
|
|
39
39
|
|
|
40
|
+
class BackendTimeoutError(BackendError):
|
|
41
|
+
"""A platform request timed out; keep its limit without retaining the request."""
|
|
42
|
+
|
|
43
|
+
def __init__(self, message: str, *, timeout_seconds: float | None = None) -> None:
|
|
44
|
+
super().__init__(message)
|
|
45
|
+
self.timeout_seconds = timeout_seconds
|
|
46
|
+
|
|
47
|
+
|
|
40
48
|
class LeaseLostError(TauSDKError):
|
|
41
49
|
code = "runtime_lease_lost"
|
|
42
50
|
status_code = 409
|
|
@@ -1152,6 +1152,8 @@ class SessionRuntimeManager:
|
|
|
1152
1152
|
create_mcp_application_tools(
|
|
1153
1153
|
bootstrap.mcp_applications,
|
|
1154
1154
|
connect=application_connector(self.settings),
|
|
1155
|
+
tool_timeout_seconds=self.settings.mcp_tool_timeout_seconds,
|
|
1156
|
+
open_timeout_seconds=self.settings.backend_read_timeout_seconds,
|
|
1155
1157
|
)
|
|
1156
1158
|
)
|
|
1157
1159
|
tools.extend(create_task_control_tools())
|
|
@@ -30,7 +30,7 @@ import structlog
|
|
|
30
30
|
from ms_tau_sdk.backend.auth import AccessToken, _jwt_expiry
|
|
31
31
|
from ms_tau_sdk.backend.client import LeaseProof, answer_without_request_credentials
|
|
32
32
|
from ms_tau_sdk.backend.models import ReleaseRuntimeAccess
|
|
33
|
-
from ms_tau_sdk.errors import BackendError, TauSDKError
|
|
33
|
+
from ms_tau_sdk.errors import BackendError, BackendTimeoutError, TauSDKError
|
|
34
34
|
|
|
35
35
|
logger = structlog.get_logger(__name__)
|
|
36
36
|
|
|
@@ -276,8 +276,12 @@ class TurnRequesterBinding:
|
|
|
276
276
|
)
|
|
277
277
|
except BackendError as error:
|
|
278
278
|
# A transport failure keeps the request, and its credentials, as its cause, so
|
|
279
|
-
# only
|
|
280
|
-
failure =
|
|
279
|
+
# only safe metadata is carried on, including a timeout's classification.
|
|
280
|
+
failure = (
|
|
281
|
+
BackendTimeoutError(str(error), timeout_seconds=error.timeout_seconds)
|
|
282
|
+
if isinstance(error, BackendTimeoutError)
|
|
283
|
+
else BackendError(str(error), status_code=error.backend_status)
|
|
284
|
+
)
|
|
281
285
|
failure.detail = error.detail
|
|
282
286
|
if failure is not None:
|
|
283
287
|
if delegate and failure.backend_status == 403:
|
|
@@ -242,6 +242,12 @@ class TauSDKSettings(BaseSettings):
|
|
|
242
242
|
ge=1,
|
|
243
243
|
validation_alias="MAINSEQUENCE_TAU_MCP_READ_CONCURRENCY",
|
|
244
244
|
)
|
|
245
|
+
# How long an application tool may run when its tool declares no time limit (ADR 0022).
|
|
246
|
+
mcp_tool_timeout_seconds: float = Field(
|
|
247
|
+
default=60.0,
|
|
248
|
+
gt=0,
|
|
249
|
+
validation_alias="TAU_MCP_TOOL_TIMEOUT_SECONDS",
|
|
250
|
+
)
|
|
245
251
|
backend_max_response_bytes: int = Field(
|
|
246
252
|
default=10 * 1024 * 1024,
|
|
247
253
|
gt=0,
|
|
@@ -8,17 +8,22 @@ session for the call, and closes it afterwards. The token carries the delegation
|
|
|
8
8
|
turn serves, when it serves one, and is the Agent's own otherwise; the application decides what the
|
|
9
9
|
call may do. No session, token or catalog is shared between turns or people, and nothing is read
|
|
10
10
|
when the session loads, because no turn is running then.
|
|
11
|
+
|
|
12
|
+
Each tool declares how long it may run in its ``_meta`` (ADR 0022); a tool that declares nothing
|
|
13
|
+
gets ``TAU_MCP_TOOL_TIMEOUT_SECONDS``. A failure reaches the model in plain words, and
|
|
14
|
+
opening the session is tried once more when it fails before the tool call is sent.
|
|
11
15
|
"""
|
|
12
16
|
|
|
13
17
|
from __future__ import annotations
|
|
14
18
|
|
|
15
19
|
import json
|
|
20
|
+
import math
|
|
16
21
|
import re
|
|
17
22
|
from collections.abc import AsyncGenerator, Awaitable, Callable, Mapping, Sequence
|
|
18
23
|
|
|
19
24
|
import httpx
|
|
20
25
|
import structlog
|
|
21
|
-
from mcp import types
|
|
26
|
+
from mcp import McpError, types
|
|
22
27
|
from tau_agent.messages import TextContent
|
|
23
28
|
from tau_agent.tools import (
|
|
24
29
|
AgentTool,
|
|
@@ -30,7 +35,7 @@ from tau_agent.types import JSONValue
|
|
|
30
35
|
|
|
31
36
|
from ms_tau_sdk.backend.mcp import MCPConnectionClient
|
|
32
37
|
from ms_tau_sdk.backend.models import MCPApplication
|
|
33
|
-
from ms_tau_sdk.errors import BackendError, ConfigurationError
|
|
38
|
+
from ms_tau_sdk.errors import BackendError, BackendTimeoutError, ConfigurationError
|
|
34
39
|
from ms_tau_sdk.runtime.requester import (
|
|
35
40
|
RequesterBindingError,
|
|
36
41
|
_turn_application_access,
|
|
@@ -44,7 +49,10 @@ logger = structlog.get_logger(__name__)
|
|
|
44
49
|
_APPLICATION_NAME = re.compile(r"^[a-z][a-z0-9_]{0,39}$")
|
|
45
50
|
_RESERVED_NAMES = frozenset({"mainsequence"})
|
|
46
51
|
|
|
47
|
-
|
|
52
|
+
TIMEOUT_META_KEY = "mainsequence.ai/timeout-seconds/v1"
|
|
53
|
+
"""The ``_meta`` key under which an application tool declares how long it may run, in seconds."""
|
|
54
|
+
|
|
55
|
+
type ApplicationConnector = Callable[[MCPApplication], Awaitable[MCPConnectionClient]]
|
|
48
56
|
|
|
49
57
|
|
|
50
58
|
class _ApplicationTokenAuth(httpx.Auth):
|
|
@@ -69,33 +77,116 @@ class _ApplicationTokenAuth(httpx.Auth):
|
|
|
69
77
|
def application_connector(settings: TauSDKSettings) -> ApplicationConnector:
|
|
70
78
|
"""Open an MCP session to one application for the current turn."""
|
|
71
79
|
|
|
72
|
-
async def connect(application: MCPApplication
|
|
80
|
+
async def connect(application: MCPApplication) -> MCPConnectionClient:
|
|
73
81
|
access = await _turn_application_access(application.resource_release_uid)
|
|
74
82
|
return await MCPConnectionClient(
|
|
75
83
|
name=application.name,
|
|
76
84
|
display_name=application.name,
|
|
77
85
|
url=f"{str(access.rpc_url).rstrip('/')}/mcp",
|
|
78
86
|
auth=_ApplicationTokenAuth(access),
|
|
87
|
+
# The session's wait bounds each request: the backend read timeout while the session
|
|
88
|
+
# opens, the tool's own limit for the call. The HTTP read wait never cuts a call short.
|
|
79
89
|
timeout=httpx.Timeout(
|
|
80
90
|
connect=settings.backend_connect_timeout_seconds,
|
|
81
|
-
read=settings.
|
|
91
|
+
read=settings.turn_timeout_seconds,
|
|
82
92
|
write=settings.backend_write_timeout_seconds,
|
|
83
93
|
pool=settings.backend_pool_timeout_seconds,
|
|
84
94
|
),
|
|
85
95
|
read_timeout_seconds=settings.backend_read_timeout_seconds,
|
|
86
96
|
read_concurrency=settings.mcp_read_concurrency,
|
|
87
|
-
read_catalog=read_catalog,
|
|
88
97
|
).open()
|
|
89
98
|
|
|
90
99
|
return connect
|
|
91
100
|
|
|
92
101
|
|
|
93
|
-
def
|
|
102
|
+
def _timed_out(error: BaseException) -> bool:
|
|
103
|
+
if isinstance(error, McpError):
|
|
104
|
+
return error.error.code == httpx.codes.REQUEST_TIMEOUT
|
|
105
|
+
return isinstance(error, TimeoutError) or (
|
|
106
|
+
isinstance(error, httpx.TimeoutException) and not isinstance(error, httpx.ConnectTimeout)
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def _failure(
|
|
111
|
+
name: str,
|
|
112
|
+
error: BaseException,
|
|
113
|
+
*,
|
|
114
|
+
tool: str | None,
|
|
115
|
+
timeout_seconds: float,
|
|
116
|
+
) -> tuple[str, dict[str, JSONValue]]:
|
|
117
|
+
"""What the model reads about a failure, and its details; never the error's own text."""
|
|
118
|
+
|
|
94
119
|
if isinstance(error, RequesterBindingError):
|
|
95
|
-
return "Your access for this request ended."
|
|
96
|
-
if isinstance(error, BackendError)
|
|
97
|
-
|
|
98
|
-
|
|
120
|
+
return "Your access for this request ended.", {"failure": "access_ended"}
|
|
121
|
+
if isinstance(error, BackendError):
|
|
122
|
+
if isinstance(error, BackendTimeoutError) or error.backend_status in {408, 504}:
|
|
123
|
+
details: dict[str, JSONValue] = {"failure": "timeout", "phase": "access"}
|
|
124
|
+
if isinstance(error, BackendTimeoutError) and error.timeout_seconds is not None:
|
|
125
|
+
details["timeout_seconds"] = error.timeout_seconds
|
|
126
|
+
if error.backend_status is not None:
|
|
127
|
+
details["status"] = error.backend_status
|
|
128
|
+
return (
|
|
129
|
+
f"Main Sequence timed out while providing access to the {name} application.",
|
|
130
|
+
details,
|
|
131
|
+
)
|
|
132
|
+
if error.backend_status in {403, 404}:
|
|
133
|
+
return (
|
|
134
|
+
f"The {name} application is not available for this call.",
|
|
135
|
+
{"failure": "not_available"},
|
|
136
|
+
)
|
|
137
|
+
return (
|
|
138
|
+
f"Main Sequence could not provide access to the {name} application.",
|
|
139
|
+
{"failure": "access_unavailable"},
|
|
140
|
+
)
|
|
141
|
+
if _timed_out(error):
|
|
142
|
+
subject = f"The {name} tool {tool}" if tool is not None else f"The {name} application"
|
|
143
|
+
return (
|
|
144
|
+
f"{subject} did not answer within {timeout_seconds:g} seconds.",
|
|
145
|
+
{"failure": "timeout", "timeout_seconds": timeout_seconds},
|
|
146
|
+
)
|
|
147
|
+
if isinstance(error, httpx.HTTPStatusError):
|
|
148
|
+
status = error.response.status_code
|
|
149
|
+
if status in {408, 504}:
|
|
150
|
+
text = f"The {name} application or its gateway reported a timeout ({status})."
|
|
151
|
+
return text, {"failure": "timeout", "status": status}
|
|
152
|
+
if status >= 500:
|
|
153
|
+
text = f"The {name} application answered with a server error ({status})."
|
|
154
|
+
return text, {"failure": "server_error", "status": status}
|
|
155
|
+
if status in {401, 403}:
|
|
156
|
+
text = f"The {name} application refused this call ({status})."
|
|
157
|
+
return text, {"failure": "refused", "status": status}
|
|
158
|
+
text = f"The {name} application answered with an error ({status})."
|
|
159
|
+
return text, {"failure": "error_status", "status": status}
|
|
160
|
+
if isinstance(error, httpx.ConnectError | httpx.ConnectTimeout):
|
|
161
|
+
return f"The {name} application could not be reached.", {"failure": "unreachable"}
|
|
162
|
+
return f"The {name} application could not complete this call.", {"failure": "failed"}
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
def _retryable(error: BaseException) -> bool:
|
|
166
|
+
"""Whether opening the session failed in a way that a second attempt may not repeat."""
|
|
167
|
+
|
|
168
|
+
if isinstance(error, httpx.HTTPStatusError):
|
|
169
|
+
return error.response.status_code == 408 or error.response.status_code >= 500
|
|
170
|
+
return isinstance(error, httpx.TransportError) or _timed_out(error)
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
def _declared_timeout(name: str, tool: types.Tool) -> float | None:
|
|
174
|
+
value = (tool.meta or {}).get(TIMEOUT_META_KEY)
|
|
175
|
+
if value is None:
|
|
176
|
+
return None
|
|
177
|
+
if (
|
|
178
|
+
isinstance(value, bool)
|
|
179
|
+
or not isinstance(value, int | float)
|
|
180
|
+
or not math.isfinite(value)
|
|
181
|
+
or value <= 0
|
|
182
|
+
):
|
|
183
|
+
logger.warning(
|
|
184
|
+
"runtime.mcp_application.invalid_timeout",
|
|
185
|
+
application=name,
|
|
186
|
+
mcp_tool=tool.name,
|
|
187
|
+
)
|
|
188
|
+
return None
|
|
189
|
+
return float(value)
|
|
99
190
|
|
|
100
191
|
|
|
101
192
|
def _error(name: str, text: str, **details: JSONValue) -> AgentToolResult:
|
|
@@ -118,9 +209,53 @@ def _application_tools(
|
|
|
118
209
|
application: MCPApplication,
|
|
119
210
|
*,
|
|
120
211
|
connect: ApplicationConnector,
|
|
212
|
+
tool_timeout_seconds: float,
|
|
213
|
+
open_timeout_seconds: float,
|
|
121
214
|
) -> list[AgentTool]:
|
|
122
215
|
name = application.name
|
|
123
216
|
|
|
217
|
+
async def open_session(signal: ToolCancellationToken | None) -> MCPConnectionClient:
|
|
218
|
+
# Nothing has reached a tool while the session opens, so one more attempt is safe.
|
|
219
|
+
try:
|
|
220
|
+
return await connect(application)
|
|
221
|
+
except Exception as error:
|
|
222
|
+
if not _retryable(error) or (signal is not None and signal.is_cancelled()):
|
|
223
|
+
raise
|
|
224
|
+
logger.info(
|
|
225
|
+
"runtime.mcp_application.retried",
|
|
226
|
+
application=name,
|
|
227
|
+
error_type=type(error).__name__,
|
|
228
|
+
)
|
|
229
|
+
return await connect(application)
|
|
230
|
+
|
|
231
|
+
def failed(
|
|
232
|
+
operation: str,
|
|
233
|
+
error: BaseException,
|
|
234
|
+
*,
|
|
235
|
+
mcp_tool: str | None = None,
|
|
236
|
+
call_timeout_seconds: float | None = None,
|
|
237
|
+
) -> AgentToolResult:
|
|
238
|
+
# A failure after the call was sent, which has a time limit by then, is the tool's;
|
|
239
|
+
# a failure while the session opens is the application's.
|
|
240
|
+
text, failure = _failure(
|
|
241
|
+
name,
|
|
242
|
+
error,
|
|
243
|
+
tool=mcp_tool if call_timeout_seconds is not None else None,
|
|
244
|
+
timeout_seconds=(
|
|
245
|
+
call_timeout_seconds if call_timeout_seconds is not None else open_timeout_seconds
|
|
246
|
+
),
|
|
247
|
+
)
|
|
248
|
+
if mcp_tool is not None:
|
|
249
|
+
failure["mcp_tool"] = mcp_tool
|
|
250
|
+
logger.warning(
|
|
251
|
+
"runtime.mcp_application.failed",
|
|
252
|
+
application=name,
|
|
253
|
+
operation=operation,
|
|
254
|
+
error_type=type(error).__name__,
|
|
255
|
+
**failure,
|
|
256
|
+
)
|
|
257
|
+
return _error(name, text, **failure)
|
|
258
|
+
|
|
124
259
|
async def list_tools(
|
|
125
260
|
tool_call_id: str,
|
|
126
261
|
arguments: Mapping[str, JSONValue],
|
|
@@ -131,19 +266,13 @@ def _application_tools(
|
|
|
131
266
|
if signal is not None and signal.is_cancelled():
|
|
132
267
|
return _error(name, f"Listing the {name} tools was cancelled.", cancelled=True)
|
|
133
268
|
try:
|
|
134
|
-
client = await
|
|
269
|
+
client = await open_session(signal)
|
|
135
270
|
try:
|
|
136
271
|
tools = client.tools
|
|
137
272
|
finally:
|
|
138
273
|
await client.aclose()
|
|
139
274
|
except Exception as error:
|
|
140
|
-
|
|
141
|
-
"runtime.mcp_application.failed",
|
|
142
|
-
application=name,
|
|
143
|
-
operation="list_tools",
|
|
144
|
-
error_type=type(error).__name__,
|
|
145
|
-
)
|
|
146
|
-
return _error(name, _failure(name, error))
|
|
275
|
+
return failed("list_tools", error)
|
|
147
276
|
entries = [_tool_entry(tool) for tool in tools]
|
|
148
277
|
return AgentToolResult(
|
|
149
278
|
content=[
|
|
@@ -170,20 +299,28 @@ def _application_tools(
|
|
|
170
299
|
return _error(name, "`arguments` must be an object.")
|
|
171
300
|
if signal is not None and signal.is_cancelled():
|
|
172
301
|
return _error(name, f"The {name} tool call was cancelled.", cancelled=True)
|
|
302
|
+
call_timeout_seconds: float | None = None
|
|
173
303
|
try:
|
|
174
|
-
client = await
|
|
304
|
+
client = await open_session(signal)
|
|
175
305
|
try:
|
|
176
|
-
|
|
306
|
+
declared = next(
|
|
307
|
+
(
|
|
308
|
+
_declared_timeout(name, tool)
|
|
309
|
+
for tool in client.tools
|
|
310
|
+
if tool.name == tool_name
|
|
311
|
+
),
|
|
312
|
+
None,
|
|
313
|
+
)
|
|
314
|
+
call_timeout_seconds = declared if declared is not None else tool_timeout_seconds
|
|
315
|
+
result = await client.call_tool(
|
|
316
|
+
tool_name, dict(tool_arguments), timeout_seconds=call_timeout_seconds
|
|
317
|
+
)
|
|
177
318
|
finally:
|
|
178
319
|
await client.aclose()
|
|
179
320
|
except Exception as error:
|
|
180
|
-
|
|
181
|
-
"
|
|
182
|
-
application=name,
|
|
183
|
-
operation="call_tool",
|
|
184
|
-
error_type=type(error).__name__,
|
|
321
|
+
return failed(
|
|
322
|
+
"call_tool", error, mcp_tool=tool_name, call_timeout_seconds=call_timeout_seconds
|
|
185
323
|
)
|
|
186
|
-
return _error(name, _failure(name, error), mcp_tool=tool_name)
|
|
187
324
|
projected = mcp_tool_result(canonical_name=tool_name, result=result, display_name=name)
|
|
188
325
|
details = dict(projected.details) if isinstance(projected.details, dict) else {}
|
|
189
326
|
details["application"] = name
|
|
@@ -230,8 +367,14 @@ def create_mcp_application_tools(
|
|
|
230
367
|
applications: Sequence[MCPApplication],
|
|
231
368
|
*,
|
|
232
369
|
connect: ApplicationConnector,
|
|
370
|
+
tool_timeout_seconds: float,
|
|
371
|
+
open_timeout_seconds: float,
|
|
233
372
|
) -> list[AgentTool]:
|
|
234
|
-
"""Two tools for each declared application, in declaration order.
|
|
373
|
+
"""Two tools for each declared application, in declaration order.
|
|
374
|
+
|
|
375
|
+
``tool_timeout_seconds`` bounds a call to a tool that declares no limit of its own;
|
|
376
|
+
``open_timeout_seconds`` is the wait for each request that opens the session.
|
|
377
|
+
"""
|
|
235
378
|
|
|
236
379
|
tools: list[AgentTool] = []
|
|
237
380
|
seen: set[str] = set()
|
|
@@ -242,5 +385,12 @@ def create_mcp_application_tools(
|
|
|
242
385
|
if name in seen:
|
|
243
386
|
raise ConfigurationError(f"Duplicate MCP application name: {name!r}")
|
|
244
387
|
seen.add(name)
|
|
245
|
-
tools.extend(
|
|
388
|
+
tools.extend(
|
|
389
|
+
_application_tools(
|
|
390
|
+
application,
|
|
391
|
+
connect=connect,
|
|
392
|
+
tool_timeout_seconds=tool_timeout_seconds,
|
|
393
|
+
open_timeout_seconds=open_timeout_seconds,
|
|
394
|
+
)
|
|
395
|
+
)
|
|
246
396
|
return tools
|
|
File without changes
|
|
File without changes
|
{ms_tau_sdk-2.0.9.dev48 → ms_tau_sdk-2.0.10.dev50}/packages/tau-board/src/ms_tau_board/__init__.py
RENAMED
|
File without changes
|
{ms_tau_sdk-2.0.9.dev48 → ms_tau_sdk-2.0.10.dev50}/packages/tau-board/src/ms_tau_board/app.py
RENAMED
|
File without changes
|
{ms_tau_sdk-2.0.9.dev48 → ms_tau_sdk-2.0.10.dev50}/packages/tau-board/src/ms_tau_board/cli.py
RENAMED
|
File without changes
|
{ms_tau_sdk-2.0.9.dev48 → ms_tau_sdk-2.0.10.dev50}/packages/tau-board/src/ms_tau_board/config.py
RENAMED
|
File without changes
|
{ms_tau_sdk-2.0.9.dev48 → ms_tau_sdk-2.0.10.dev50}/packages/tau-board/src/ms_tau_board/env_file.py
RENAMED
|
File without changes
|
{ms_tau_sdk-2.0.9.dev48 → ms_tau_sdk-2.0.10.dev50}/packages/tau-board/src/ms_tau_board/logs.py
RENAMED
|
File without changes
|
{ms_tau_sdk-2.0.9.dev48 → ms_tau_sdk-2.0.10.dev50}/packages/tau-board/src/ms_tau_board/proxy.py
RENAMED
|
File without changes
|
{ms_tau_sdk-2.0.9.dev48 → ms_tau_sdk-2.0.10.dev50}/packages/tau-board/src/ms_tau_board/state.py
RENAMED
|
File without changes
|
|
File without changes
|
{ms_tau_sdk-2.0.9.dev48 → ms_tau_sdk-2.0.10.dev50}/packages/tau-board/src/ms_tau_board/static/app.js
RENAMED
|
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
|
{ms_tau_sdk-2.0.9.dev48 → ms_tau_sdk-2.0.10.dev50}/src/ms_tau_sdk/runtime/deployment_health.py
RENAMED
|
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
|