ms-tau-sdk 2.0.11.dev52__tar.gz → 2.0.15.dev62__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.11.dev52 → ms_tau_sdk-2.0.15.dev62}/CHANGELOG.md +76 -0
- {ms_tau_sdk-2.0.11.dev52 → ms_tau_sdk-2.0.15.dev62}/PKG-INFO +7 -13
- {ms_tau_sdk-2.0.11.dev52 → ms_tau_sdk-2.0.15.dev62}/README.md +5 -11
- {ms_tau_sdk-2.0.11.dev52 → ms_tau_sdk-2.0.15.dev62}/pyproject.toml +2 -2
- {ms_tau_sdk-2.0.11.dev52 → ms_tau_sdk-2.0.15.dev62}/src/ms_tau_sdk/agent_skills/tau_a2a_runtime_adapter/SKILL.md +5 -0
- {ms_tau_sdk-2.0.11.dev52 → ms_tau_sdk-2.0.15.dev62}/src/ms_tau_sdk/agent_skills/tau_local_development/SKILL.md +8 -3
- {ms_tau_sdk-2.0.11.dev52 → ms_tau_sdk-2.0.15.dev62}/src/ms_tau_sdk/agent_skills/tau_project_customization/SKILL.md +18 -5
- {ms_tau_sdk-2.0.11.dev52 → ms_tau_sdk-2.0.15.dev62}/src/ms_tau_sdk/agent_skills/tau_security_and_access/SKILL.md +7 -1
- {ms_tau_sdk-2.0.11.dev52 → ms_tau_sdk-2.0.15.dev62}/src/ms_tau_sdk/api/a2a.py +156 -22
- {ms_tau_sdk-2.0.11.dev52 → ms_tau_sdk-2.0.15.dev62}/src/ms_tau_sdk/backend/mcp.py +220 -76
- ms_tau_sdk-2.0.15.dev62/src/ms_tau_sdk/resources/loader.py +76 -0
- {ms_tau_sdk-2.0.11.dev52 → ms_tau_sdk-2.0.15.dev62}/src/ms_tau_sdk/runtime/manager.py +18 -2
- {ms_tau_sdk-2.0.11.dev52 → ms_tau_sdk-2.0.15.dev62}/src/ms_tau_sdk/settings.py +20 -0
- {ms_tau_sdk-2.0.11.dev52 → ms_tau_sdk-2.0.15.dev62}/src/ms_tau_sdk/tools/mainsequence_mcp.py +1 -1
- {ms_tau_sdk-2.0.11.dev52 → ms_tau_sdk-2.0.15.dev62}/src/ms_tau_sdk/tools/mcp_applications.py +288 -51
- {ms_tau_sdk-2.0.11.dev52 → ms_tau_sdk-2.0.15.dev62}/src/ms_tau_sdk/tools/mcp_connection.py +60 -21
- ms_tau_sdk-2.0.11.dev52/src/ms_tau_sdk/resources/loader.py +0 -30
- {ms_tau_sdk-2.0.11.dev52 → ms_tau_sdk-2.0.15.dev62}/.gitignore +0 -0
- {ms_tau_sdk-2.0.11.dev52 → ms_tau_sdk-2.0.15.dev62}/packages/tau-board/src/ms_tau_board/__init__.py +0 -0
- {ms_tau_sdk-2.0.11.dev52 → ms_tau_sdk-2.0.15.dev62}/packages/tau-board/src/ms_tau_board/app.py +0 -0
- {ms_tau_sdk-2.0.11.dev52 → ms_tau_sdk-2.0.15.dev62}/packages/tau-board/src/ms_tau_board/cli.py +0 -0
- {ms_tau_sdk-2.0.11.dev52 → ms_tau_sdk-2.0.15.dev62}/packages/tau-board/src/ms_tau_board/config.py +0 -0
- {ms_tau_sdk-2.0.11.dev52 → ms_tau_sdk-2.0.15.dev62}/packages/tau-board/src/ms_tau_board/env_file.py +0 -0
- {ms_tau_sdk-2.0.11.dev52 → ms_tau_sdk-2.0.15.dev62}/packages/tau-board/src/ms_tau_board/logs.py +0 -0
- {ms_tau_sdk-2.0.11.dev52 → ms_tau_sdk-2.0.15.dev62}/packages/tau-board/src/ms_tau_board/proxy.py +0 -0
- {ms_tau_sdk-2.0.11.dev52 → ms_tau_sdk-2.0.15.dev62}/packages/tau-board/src/ms_tau_board/state.py +0 -0
- {ms_tau_sdk-2.0.11.dev52 → ms_tau_sdk-2.0.15.dev62}/packages/tau-board/src/ms_tau_board/static/BULMA-LICENSE.txt +0 -0
- {ms_tau_sdk-2.0.11.dev52 → ms_tau_sdk-2.0.15.dev62}/packages/tau-board/src/ms_tau_board/static/app.js +0 -0
- {ms_tau_sdk-2.0.11.dev52 → ms_tau_sdk-2.0.15.dev62}/packages/tau-board/src/ms_tau_board/static/board.css +0 -0
- {ms_tau_sdk-2.0.11.dev52 → ms_tau_sdk-2.0.15.dev62}/packages/tau-board/src/ms_tau_board/static/bulma.min.css +0 -0
- {ms_tau_sdk-2.0.11.dev52 → ms_tau_sdk-2.0.15.dev62}/packages/tau-board/src/ms_tau_board/static/index.html +0 -0
- {ms_tau_sdk-2.0.11.dev52 → ms_tau_sdk-2.0.15.dev62}/src/ms_tau_sdk/__init__.py +0 -0
- {ms_tau_sdk-2.0.11.dev52 → ms_tau_sdk-2.0.15.dev62}/src/ms_tau_sdk/agent_skills/tau_repository_integration/SKILL.md +0 -0
- {ms_tau_sdk-2.0.11.dev52 → ms_tau_sdk-2.0.15.dev62}/src/ms_tau_sdk/api/__init__.py +0 -0
- {ms_tau_sdk-2.0.11.dev52 → ms_tau_sdk-2.0.15.dev62}/src/ms_tau_sdk/api/chat.py +0 -0
- {ms_tau_sdk-2.0.11.dev52 → ms_tau_sdk-2.0.15.dev62}/src/ms_tau_sdk/api/conversations.py +0 -0
- {ms_tau_sdk-2.0.11.dev52 → ms_tau_sdk-2.0.15.dev62}/src/ms_tau_sdk/api/dependencies.py +0 -0
- {ms_tau_sdk-2.0.11.dev52 → ms_tau_sdk-2.0.15.dev62}/src/ms_tau_sdk/api/health.py +0 -0
- {ms_tau_sdk-2.0.11.dev52 → ms_tau_sdk-2.0.15.dev62}/src/ms_tau_sdk/api/inspection.py +0 -0
- {ms_tau_sdk-2.0.11.dev52 → ms_tau_sdk-2.0.15.dev62}/src/ms_tau_sdk/api/local_chat.py +0 -0
- {ms_tau_sdk-2.0.11.dev52 → ms_tau_sdk-2.0.15.dev62}/src/ms_tau_sdk/api/models.py +0 -0
- {ms_tau_sdk-2.0.11.dev52 → ms_tau_sdk-2.0.15.dev62}/src/ms_tau_sdk/api/request_identity.py +0 -0
- {ms_tau_sdk-2.0.11.dev52 → ms_tau_sdk-2.0.15.dev62}/src/ms_tau_sdk/api/sessions.py +0 -0
- {ms_tau_sdk-2.0.11.dev52 → ms_tau_sdk-2.0.15.dev62}/src/ms_tau_sdk/app.py +0 -0
- {ms_tau_sdk-2.0.11.dev52 → ms_tau_sdk-2.0.15.dev62}/src/ms_tau_sdk/application.py +0 -0
- {ms_tau_sdk-2.0.11.dev52 → ms_tau_sdk-2.0.15.dev62}/src/ms_tau_sdk/backend/__init__.py +0 -0
- {ms_tau_sdk-2.0.11.dev52 → ms_tau_sdk-2.0.15.dev62}/src/ms_tau_sdk/backend/assertions.py +0 -0
- {ms_tau_sdk-2.0.11.dev52 → ms_tau_sdk-2.0.15.dev62}/src/ms_tau_sdk/backend/auth.py +0 -0
- {ms_tau_sdk-2.0.11.dev52 → ms_tau_sdk-2.0.15.dev62}/src/ms_tau_sdk/backend/client.py +0 -0
- {ms_tau_sdk-2.0.11.dev52 → ms_tau_sdk-2.0.15.dev62}/src/ms_tau_sdk/backend/local.py +0 -0
- {ms_tau_sdk-2.0.11.dev52 → ms_tau_sdk-2.0.15.dev62}/src/ms_tau_sdk/backend/models.py +0 -0
- {ms_tau_sdk-2.0.11.dev52 → ms_tau_sdk-2.0.15.dev62}/src/ms_tau_sdk/backend/routes.py +0 -0
- {ms_tau_sdk-2.0.11.dev52 → ms_tau_sdk-2.0.15.dev62}/src/ms_tau_sdk/cli.py +0 -0
- {ms_tau_sdk-2.0.11.dev52 → ms_tau_sdk-2.0.15.dev62}/src/ms_tau_sdk/errors.py +0 -0
- {ms_tau_sdk-2.0.11.dev52 → ms_tau_sdk-2.0.15.dev62}/src/ms_tau_sdk/logging.py +0 -0
- {ms_tau_sdk-2.0.11.dev52 → ms_tau_sdk-2.0.15.dev62}/src/ms_tau_sdk/protocols/__init__.py +0 -0
- {ms_tau_sdk-2.0.11.dev52 → ms_tau_sdk-2.0.15.dev62}/src/ms_tau_sdk/protocols/a2a_failure.py +0 -0
- {ms_tau_sdk-2.0.11.dev52 → ms_tau_sdk-2.0.15.dev62}/src/ms_tau_sdk/protocols/a2a_message.py +0 -0
- {ms_tau_sdk-2.0.11.dev52 → ms_tau_sdk-2.0.15.dev62}/src/ms_tau_sdk/protocols/a2a_roles.py +0 -0
- {ms_tau_sdk-2.0.11.dev52 → ms_tau_sdk-2.0.15.dev62}/src/ms_tau_sdk/protocols/assistant_ui.py +0 -0
- {ms_tau_sdk-2.0.11.dev52 → ms_tau_sdk-2.0.15.dev62}/src/ms_tau_sdk/protocols/chat_history.py +0 -0
- {ms_tau_sdk-2.0.11.dev52 → ms_tau_sdk-2.0.15.dev62}/src/ms_tau_sdk/protocols/strict_json.py +0 -0
- {ms_tau_sdk-2.0.11.dev52 → ms_tau_sdk-2.0.15.dev62}/src/ms_tau_sdk/providers/__init__.py +0 -0
- {ms_tau_sdk-2.0.11.dev52 → ms_tau_sdk-2.0.15.dev62}/src/ms_tau_sdk/providers/definitions.py +0 -0
- {ms_tau_sdk-2.0.11.dev52 → ms_tau_sdk-2.0.15.dev62}/src/ms_tau_sdk/providers/factory.py +0 -0
- {ms_tau_sdk-2.0.11.dev52 → ms_tau_sdk-2.0.15.dev62}/src/ms_tau_sdk/providers/tau_compat.py +0 -0
- {ms_tau_sdk-2.0.11.dev52 → ms_tau_sdk-2.0.15.dev62}/src/ms_tau_sdk/resources/SYSTEM.md +0 -0
- {ms_tau_sdk-2.0.11.dev52 → ms_tau_sdk-2.0.15.dev62}/src/ms_tau_sdk/resources/__init__.py +0 -0
- {ms_tau_sdk-2.0.11.dev52 → ms_tau_sdk-2.0.15.dev62}/src/ms_tau_sdk/resources/prompts/review-code-repository.md +0 -0
- {ms_tau_sdk-2.0.11.dev52 → ms_tau_sdk-2.0.15.dev62}/src/ms_tau_sdk/runtime/__init__.py +0 -0
- {ms_tau_sdk-2.0.11.dev52 → ms_tau_sdk-2.0.15.dev62}/src/ms_tau_sdk/runtime/deployment_health.py +0 -0
- {ms_tau_sdk-2.0.11.dev52 → ms_tau_sdk-2.0.15.dev62}/src/ms_tau_sdk/runtime/events.py +0 -0
- {ms_tau_sdk-2.0.11.dev52 → ms_tau_sdk-2.0.15.dev62}/src/ms_tau_sdk/runtime/extensions.py +0 -0
- {ms_tau_sdk-2.0.11.dev52 → ms_tau_sdk-2.0.15.dev62}/src/ms_tau_sdk/runtime/failures.py +0 -0
- {ms_tau_sdk-2.0.11.dev52 → ms_tau_sdk-2.0.15.dev62}/src/ms_tau_sdk/runtime/live_turns.py +0 -0
- {ms_tau_sdk-2.0.11.dev52 → ms_tau_sdk-2.0.15.dev62}/src/ms_tau_sdk/runtime/observability.py +0 -0
- {ms_tau_sdk-2.0.11.dev52 → ms_tau_sdk-2.0.15.dev62}/src/ms_tau_sdk/runtime/provenance.py +0 -0
- {ms_tau_sdk-2.0.11.dev52 → ms_tau_sdk-2.0.15.dev62}/src/ms_tau_sdk/runtime/requester.py +0 -0
- {ms_tau_sdk-2.0.11.dev52 → ms_tau_sdk-2.0.15.dev62}/src/ms_tau_sdk/runtime/session.py +0 -0
- {ms_tau_sdk-2.0.11.dev52 → ms_tau_sdk-2.0.15.dev62}/src/ms_tau_sdk/runtime/snapshots.py +0 -0
- {ms_tau_sdk-2.0.11.dev52 → ms_tau_sdk-2.0.15.dev62}/src/ms_tau_sdk/runtime/task_context.py +0 -0
- {ms_tau_sdk-2.0.11.dev52 → ms_tau_sdk-2.0.15.dev62}/src/ms_tau_sdk/sessions/__init__.py +0 -0
- {ms_tau_sdk-2.0.11.dev52 → ms_tau_sdk-2.0.15.dev62}/src/ms_tau_sdk/sessions/storage.py +0 -0
- {ms_tau_sdk-2.0.11.dev52 → ms_tau_sdk-2.0.15.dev62}/src/ms_tau_sdk/skills.py +0 -0
- {ms_tau_sdk-2.0.11.dev52 → ms_tau_sdk-2.0.15.dev62}/src/ms_tau_sdk/tools/__init__.py +0 -0
- {ms_tau_sdk-2.0.11.dev52 → ms_tau_sdk-2.0.15.dev62}/src/ms_tau_sdk/tools/skill_read.py +0 -0
- {ms_tau_sdk-2.0.11.dev52 → ms_tau_sdk-2.0.15.dev62}/src/ms_tau_sdk/tools/task_control.py +0 -0
|
@@ -1,5 +1,81 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## Unreleased
|
|
4
|
+
|
|
5
|
+
- **Tau reads skills nested in folders
|
|
6
|
+
([#99](https://github.com/mainsequence-sdk/ms-tau-sdk/issues/99)).** Tau read only the skill
|
|
7
|
+
folders directly in `.tau/skills` and `.agents/skills`, so a skill one folder deeper, such as
|
|
8
|
+
`.agents/skills/mcp_tools/<skill>/SKILL.md`, the folder an application publishes with the
|
|
9
|
+
mainsequence SDK's skills publisher, was skipped without a diagnostic. A folder there without a
|
|
10
|
+
`SKILL.md` now groups skills, at any depth, and its skills are read, listed in the system prompt,
|
|
11
|
+
reloaded and opened by the skill-scoped `read` like the others. This includes the managed
|
|
12
|
+
development skills in `.agents/skills/ms_tau_sdk` and `.agents/skills/mainsequence`. A skill
|
|
13
|
+
name found twice in the same folder keeps the first in path order, with Tau's duplicate diagnostic; a folder inside
|
|
14
|
+
a skill is not a skill, and a linked group folder is not followed.
|
|
15
|
+
|
|
16
|
+
## 2.0.14 — 2026-10-13
|
|
17
|
+
|
|
18
|
+
- **An Agent reads the skills and resources its declared applications publish
|
|
19
|
+
([#98](https://github.com/mainsequence-sdk/ms-tau-sdk/issues/98), ADR 0023).** An application
|
|
20
|
+
that published skills through the MCP Skills extension, as the mainsequence SDK's skills
|
|
21
|
+
publisher does, reached its Agent only through `<name>__list_tools` and `<name>__call_tool`: the
|
|
22
|
+
Agent could not discover or read the skills. Each declared application now also gets
|
|
23
|
+
`<name>__list_resources`, which lists its skills, each with its files, and its other resources,
|
|
24
|
+
and `<name>__read_resource`, which reads one of them by URI. `<name>__list_tools` says when the
|
|
25
|
+
application publishes them. An application's session first asks MCP's `server/discover` on
|
|
26
|
+
protocol `2026-07-28`, where the extension is declared, and opens an application that does not
|
|
27
|
+
answer it with `initialize`, as before; the platform's own MCP is unchanged. Only what the
|
|
28
|
+
application lists is read, and a skill file is returned only when its bytes match the size and
|
|
29
|
+
SHA-256 digest of the skill's list. Every read names the application. Published skills never
|
|
30
|
+
become the project's skills and grant nothing; with the coding tools, a model that follows one
|
|
31
|
+
can run commands, which the [Security and access guide](docs/reference/security-model.md#calling-other-applications)
|
|
32
|
+
now states.
|
|
33
|
+
|
|
34
|
+
## 2.0.13 — 2026-10-12
|
|
35
|
+
|
|
36
|
+
- **ms-tau-sdk installs the MCP Python SDK 2.x.** The `mcp` dependency moves from `mcp>=1.27,<2`
|
|
37
|
+
to `mcp>=2.3,<3`, which brings `httpx2`, the HTTP client MCP 2.x uses. A repository that runs a
|
|
38
|
+
Harness Agent and also serves an application MCP with `mainsequence[mcp]`, which requires MCP 2.x
|
|
39
|
+
for the MCP Skills extension, could not lock both packages; it now can. Agent behaviour does not
|
|
40
|
+
change: platform and application MCP calls keep their time limits, cancellation, the one token
|
|
41
|
+
renewal on 401, and the same failure texts and details, including the refused, server-error and
|
|
42
|
+
timeout statuses that MCP 2.x no longer reports by itself. A project that pins `mcp<2` itself,
|
|
43
|
+
or uses the MCP 1.x API in its own code (`FastMCP`, `McpError`, camelCase fields), must move to
|
|
44
|
+
MCP 2.x. The time-limits guide shows `MCPServer`, MCP 2.x's name for `FastMCP`.
|
|
45
|
+
|
|
46
|
+
## 2.0.12 — 2026-10-11
|
|
47
|
+
|
|
48
|
+
- **A direct Message that runs out of time gets a typed answer
|
|
49
|
+
([#94](https://github.com/mainsequence-sdk/ms-tau-sdk/issues/94)).** A `message:send` answered
|
|
50
|
+
with a Message, or JSON-RPC `message/send`, whose turn reached
|
|
51
|
+
`MAINSEQUENCE_TAU_TURN_TIMEOUT_SECONDS` got a plain-text HTTP 500 "Internal Server Error". It now
|
|
52
|
+
gets HTTP 504 `application/a2a+json` with status `DEADLINE_EXCEEDED` and an `ErrorInfo` with
|
|
53
|
+
reason `MESSAGE_RESPONSE_LIMIT_EXCEEDED`, domain `mainsequence.ai` and `metadata.limitSeconds`,
|
|
54
|
+
the limit that expired. JSON-RPC returns the same `ErrorInfo` in a JSON-RPC error (`-32000`).
|
|
55
|
+
The turn is stopped as before; its work is neither rolled back nor replayed. Use Task mode for
|
|
56
|
+
longer work.
|
|
57
|
+
- **A direct Message has the platform's 90-second limit.** The platform gives a direct A2A
|
|
58
|
+
Message one strict limit, 90 seconds, and passes it to the runtime it hosts in
|
|
59
|
+
`MAINSEQUENCE_A2A_MESSAGE_LIMIT_SECONDS`, a platform-set variable that users do not set. The
|
|
60
|
+
runtime enforces it for every caller, from when it receives the request and over the whole
|
|
61
|
+
request, including output-contract repair turns: the turn is stopped and the request gets the
|
|
62
|
+
504 above with the platform's limit. A shorter `MAINSEQUENCE_TAU_TURN_TIMEOUT_SECONDS` still ends
|
|
63
|
+
the turn first; no setting extends the platform's limit. A Task-mode `message:send` with
|
|
64
|
+
`returnImmediately: false` waits at most until the same limit and returns the Task, which keeps
|
|
65
|
+
running. Streams, chat, caller delivery, Task dispatch and Task turns are unchanged. Without the
|
|
66
|
+
variable, as in local mode (which refuses it), a direct Message has only the turn limit.
|
|
67
|
+
- **Platform MCP tools wait for the limit the platform declares
|
|
68
|
+
([#93](https://github.com/mainsequence-sdk/ms-tau-sdk/issues/93), ADR 0022 amendment).** Every
|
|
69
|
+
Main Sequence MCP tool call waited `MAINSEQUENCE_TAU_BACKEND_READ_TIMEOUT_SECONDS` (60 seconds by
|
|
70
|
+
default), so an `a2a.send_message` in Message mode whose target answered after 60 seconds was
|
|
71
|
+
abandoned by the calling Agent while the platform still waited and completed the send. A
|
|
72
|
+
platform tool whose `_meta` declares its limit under `mainsequence.ai/timeout-seconds/v1`, the
|
|
73
|
+
key application tools use, is now waited for that long. A tool that declares nothing, or an
|
|
74
|
+
invalid value, keeps the session default; an invalid value is logged as
|
|
75
|
+
`runtime.mcp.invalid_timeout`. `MAINSEQUENCE_TAU_BACKEND_READ_TIMEOUT_SECONDS` now covers only
|
|
76
|
+
ordinary backend reads and platform tools without a declared limit: it has no effect on a Message
|
|
77
|
+
or any platform limit and can only shorten a wait.
|
|
78
|
+
|
|
3
79
|
## 2.0.11 — 2026-10-11
|
|
4
80
|
|
|
5
81
|
- **Every MCP connection honors per-call response deadlines
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: ms-tau-sdk
|
|
3
|
-
Version: 2.0.
|
|
3
|
+
Version: 2.0.15.dev62
|
|
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
|
|
@@ -18,7 +18,7 @@ Requires-Dist: cryptography>=42
|
|
|
18
18
|
Requires-Dist: fastapi>=0.116
|
|
19
19
|
Requires-Dist: httpx>=0.28
|
|
20
20
|
Requires-Dist: jsonschema<5,>=4.25
|
|
21
|
-
Requires-Dist: mcp<2
|
|
21
|
+
Requires-Dist: mcp<3,>=2.3
|
|
22
22
|
Requires-Dist: pydantic-settings>=2.10
|
|
23
23
|
Requires-Dist: pydantic>=2.11
|
|
24
24
|
Requires-Dist: pyjwt[crypto]<3,>=2.10.1
|
|
@@ -139,12 +139,6 @@ and call the platform or another platform application with `platform_client()`,
|
|
|
139
139
|
person's delegation by default while the turn serves one; neither exposes a proof or a token. See
|
|
140
140
|
the [public API](docs/reference/public-api.md#current_requester-and-platform_client).
|
|
141
141
|
|
|
142
|
-
Job-hosted batch execution is an [accepted design](docs/adrs/0015-job-hosted-batch-execution.md)
|
|
143
|
-
with implementation pending. It will run this SDK's configured Tau composition for one assignment
|
|
144
|
-
inside a project Job, close it, and exit without starting Uvicorn. The Job continues to own its
|
|
145
|
-
status and logs. See the [batch guide](docs/guides/job-hosted-batch-execution.md) for the target
|
|
146
|
-
Python integration; the current release does not expose that batch API.
|
|
147
|
-
|
|
148
142
|
## Local development without platform sessions
|
|
149
143
|
|
|
150
144
|
Local mode runs the same workspace Tau runtime without registering an Agent or AgentSession. The
|
|
@@ -305,8 +299,8 @@ Kubernetes manifest, runtime image, executor bundle, or container-publication pi
|
|
|
305
299
|
consuming project owns its dependency lock, deployable artifact, system dependencies, project
|
|
306
300
|
code, prompts, skills, hooks, and extensions.
|
|
307
301
|
|
|
308
|
-
The project identity
|
|
309
|
-
[ADR
|
|
302
|
+
The project identity is defined by
|
|
303
|
+
[ADR 0001](./docs/adrs/0001-workspace-bound-python-sdk.md). See the
|
|
310
304
|
[quickstart](./docs/getting-started/quickstart.md), [documentation index](./docs/README.md), and
|
|
311
305
|
[release guide](./docs/reference/releasing.md) for the complete contracts.
|
|
312
306
|
|
|
@@ -315,12 +309,12 @@ The project identity and migration are defined by
|
|
|
315
309
|
The repository uses Python 3.13 and `uv`:
|
|
316
310
|
|
|
317
311
|
```bash
|
|
318
|
-
uv sync --frozen
|
|
312
|
+
uv sync --frozen --extra tau-board
|
|
319
313
|
uv run pytest
|
|
320
|
-
uv run ruff check .
|
|
321
|
-
uv run mypy
|
|
322
314
|
```
|
|
323
315
|
|
|
316
|
+
[Testing and release gates](docs/reference/testing.md) lists every check CI runs.
|
|
317
|
+
|
|
324
318
|
Releases are immutable, and a merge to `main` is the release. Merging `development` into `main`
|
|
325
319
|
builds and verifies the wheel and source distribution of the version `pyproject.toml` declares,
|
|
326
320
|
publishes them to PyPI through OIDC trusted publishing, and tags the merge—for example, `v1.0.0`.
|
|
@@ -106,12 +106,6 @@ and call the platform or another platform application with `platform_client()`,
|
|
|
106
106
|
person's delegation by default while the turn serves one; neither exposes a proof or a token. See
|
|
107
107
|
the [public API](docs/reference/public-api.md#current_requester-and-platform_client).
|
|
108
108
|
|
|
109
|
-
Job-hosted batch execution is an [accepted design](docs/adrs/0015-job-hosted-batch-execution.md)
|
|
110
|
-
with implementation pending. It will run this SDK's configured Tau composition for one assignment
|
|
111
|
-
inside a project Job, close it, and exit without starting Uvicorn. The Job continues to own its
|
|
112
|
-
status and logs. See the [batch guide](docs/guides/job-hosted-batch-execution.md) for the target
|
|
113
|
-
Python integration; the current release does not expose that batch API.
|
|
114
|
-
|
|
115
109
|
## Local development without platform sessions
|
|
116
110
|
|
|
117
111
|
Local mode runs the same workspace Tau runtime without registering an Agent or AgentSession. The
|
|
@@ -272,8 +266,8 @@ Kubernetes manifest, runtime image, executor bundle, or container-publication pi
|
|
|
272
266
|
consuming project owns its dependency lock, deployable artifact, system dependencies, project
|
|
273
267
|
code, prompts, skills, hooks, and extensions.
|
|
274
268
|
|
|
275
|
-
The project identity
|
|
276
|
-
[ADR
|
|
269
|
+
The project identity is defined by
|
|
270
|
+
[ADR 0001](./docs/adrs/0001-workspace-bound-python-sdk.md). See the
|
|
277
271
|
[quickstart](./docs/getting-started/quickstart.md), [documentation index](./docs/README.md), and
|
|
278
272
|
[release guide](./docs/reference/releasing.md) for the complete contracts.
|
|
279
273
|
|
|
@@ -282,12 +276,12 @@ The project identity and migration are defined by
|
|
|
282
276
|
The repository uses Python 3.13 and `uv`:
|
|
283
277
|
|
|
284
278
|
```bash
|
|
285
|
-
uv sync --frozen
|
|
279
|
+
uv sync --frozen --extra tau-board
|
|
286
280
|
uv run pytest
|
|
287
|
-
uv run ruff check .
|
|
288
|
-
uv run mypy
|
|
289
281
|
```
|
|
290
282
|
|
|
283
|
+
[Testing and release gates](docs/reference/testing.md) lists every check CI runs.
|
|
284
|
+
|
|
291
285
|
Releases are immutable, and a merge to `main` is the release. Merging `development` into `main`
|
|
292
286
|
builds and verifies the wheel and source distribution of the version `pyproject.toml` declares,
|
|
293
287
|
publishes them to PyPI through OIDC trusted publishing, and tags the merge—for example, `v1.0.0`.
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "ms-tau-sdk"
|
|
7
|
-
version = "2.0.
|
|
7
|
+
version = "2.0.15.dev62"
|
|
8
8
|
description = "Workspace-bound Tau application primitives for Main Sequence projects"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.13"
|
|
@@ -22,7 +22,7 @@ dependencies = [
|
|
|
22
22
|
"fastapi>=0.116",
|
|
23
23
|
"httpx>=0.28",
|
|
24
24
|
"jsonschema>=4.25,<5",
|
|
25
|
-
"mcp>=
|
|
25
|
+
"mcp>=2.3,<3",
|
|
26
26
|
"pydantic>=2.11",
|
|
27
27
|
"pydantic-settings>=2.10",
|
|
28
28
|
"pyjwt[crypto]>=2.10.1,<3",
|
|
@@ -54,6 +54,11 @@ For a deployed Agent, use the projected `a2a.send_message` MCP operation. The pl
|
|
|
54
54
|
resolves the target `AgentSession`, waits for its runtime to become ready, and dispatches the
|
|
55
55
|
message. For asynchronous work, use the returned Task handle with `a2a.wait_task`.
|
|
56
56
|
|
|
57
|
+
The platform gives a direct Message (`response_kind: "message"`) 90 seconds, and no setting
|
|
58
|
+
extends that limit. A target that has not answered by then stops its turn, and the send fails
|
|
59
|
+
because of the limit. Send work that may take longer with `response_kind: "task"`, and do not
|
|
60
|
+
resend a Message that ran out of time: the work it already did is not rolled back.
|
|
61
|
+
|
|
57
62
|
For local Tau development, start `ms-tau` and wait for its local `/ready` result. The public A2A
|
|
58
63
|
REST and JSON-RPC surfaces support Message and Task execution against the workspace-local SQLite
|
|
59
64
|
store, including streaming, list/get/cancel/subscribe, and continuation. An A2A protocol Task does
|
|
@@ -35,9 +35,9 @@ The SDK looks for the CLI in `MAINSEQUENCE_CLI`, then beside the Python interpre
|
|
|
35
35
|
CLI. Do not add token lines to `.env`. A pair found there still works, but it is deprecated and
|
|
36
36
|
startup logs a warning. `mainsequence refresh-token` run in that directory removes the lines.
|
|
37
37
|
|
|
38
|
-
Do not set `APP_NAME`, `FASTAPI_PUBLIC_BASE_URL`, `MAINSEQUENCE_CALLER_AUTH_MODE=assertion`,
|
|
39
|
-
`MAINSEQUENCE_CALLER_ASSERTION_*` variables
|
|
40
|
-
local mode refuses to start with them.
|
|
38
|
+
Do not set `APP_NAME`, `FASTAPI_PUBLIC_BASE_URL`, `MAINSEQUENCE_CALLER_AUTH_MODE=assertion`, the
|
|
39
|
+
`MAINSEQUENCE_CALLER_ASSERTION_*` variables or `MAINSEQUENCE_A2A_MESSAGE_LIMIT_SECONDS`. The
|
|
40
|
+
platform sets them when it hosts a runtime, and local mode refuses to start with them.
|
|
41
41
|
|
|
42
42
|
Set `MAINSEQUENCE_ENDPOINT` only for a non-default platform endpoint. The CLI session must be for
|
|
43
43
|
the same endpoint. Local mode binds to `127.0.0.1:8787` by default. Treat an explicit public bind
|
|
@@ -118,6 +118,11 @@ To request a Task from `message:send`, send the response-kind extension header
|
|
|
118
118
|
response. `message:stream` is always Task-oriented and does not accept `responseKind` or
|
|
119
119
|
`returnImmediately`.
|
|
120
120
|
|
|
121
|
+
Local mode has no platform Message limit: a direct Message ends only at
|
|
122
|
+
`MAINSEQUENCE_TAU_TURN_TIMEOUT_SECONDS`. A hosted Agent answers a direct Message within the
|
|
123
|
+
platform's 90 seconds or with HTTP 504 `DEADLINE_EXCEEDED`, so design work that may take longer as
|
|
124
|
+
a Task.
|
|
125
|
+
|
|
121
126
|
Task terminality is derived only from its A2A state. `completed`, `failed`, `canceled`, and
|
|
122
127
|
`rejected` are terminal; `input_required` and `auth_required` are resumable. A normal execution
|
|
123
128
|
failure produces a durable failed Task and a standard responder `status.message`. A
|
|
@@ -26,8 +26,13 @@ second SDK-specific prompt override or companion append file. Other resources fo
|
|
|
26
26
|
version pinned by the installed SDK.
|
|
27
27
|
|
|
28
28
|
`.agents/skills/ms_tau_sdk/` is different: it contains managed development instructions for coding
|
|
29
|
-
agents working on the repository. It is not
|
|
30
|
-
|
|
29
|
+
agents working on the repository. It is not the runtime's `.tau` behavior and must not be used as
|
|
30
|
+
application configuration.
|
|
31
|
+
|
|
32
|
+
Tau reads skills in `.tau/skills/` and `.agents/skills/` at any depth: a folder without a
|
|
33
|
+
`SKILL.md` groups skills, such as `.agents/skills/mcp_tools/<skill>/SKILL.md`, the folder an
|
|
34
|
+
application can publish with the mainsequence SDK's skills publisher. The managed skills in
|
|
35
|
+
`.agents/skills/ms_tau_sdk/` are read too.
|
|
31
36
|
|
|
32
37
|
## Extension ownership
|
|
33
38
|
|
|
@@ -74,7 +79,7 @@ Task controls requires an actual Task context and is specified by ADR 0015.
|
|
|
74
79
|
The A2A Task controls are `task_request_input` and `task_request_authorization`. They remain
|
|
75
80
|
available in every mode for the agent's own Task; extensions must not register those names.
|
|
76
81
|
Register project tools under `.tau/extensions/` and verify the effective catalog after loading.
|
|
77
|
-
With base tools excluded, the skill-scoped `read` keeps
|
|
82
|
+
With base tools excluded, the skill-scoped `read` keeps the project skills available: it opens a
|
|
78
83
|
discovered skill's `SKILL.md` and the files in its directory, such as `references/*.md`, and
|
|
79
84
|
rejects every other path, including symlinks that resolve outside the skill. Tau lists the skills
|
|
80
85
|
in the system prompt because a tool named `read` exists. Extensions must not register `read` in
|
|
@@ -96,6 +101,16 @@ retry guidance for up to `TAU_MCP_APPLICATION_READY_TIMEOUT_SECONDS` (default `1
|
|
|
96
101
|
turn time. This startup budget is separate from the tool's execution limit; an expired readiness
|
|
97
102
|
wait reports `readiness_timeout`. Cancellation, ended delegation and terminal refusals stop the wait.
|
|
98
103
|
|
|
104
|
+
## Skills that applications publish
|
|
105
|
+
|
|
106
|
+
A declared application can publish Agent Skills through the MCP Skills extension, as the
|
|
107
|
+
mainsequence SDK's skills publisher does. The Agent lists them with `<name>__list_resources` and
|
|
108
|
+
reads one file with `<name>__read_resource`; a skill file is returned only when its bytes match the
|
|
109
|
+
size and SHA-256 digest the skill lists. Published skills stay the application's: they are not
|
|
110
|
+
project skills under `.tau/skills/`, and fields such as `allowed-tools` grant nothing. With the
|
|
111
|
+
coding tools, a model that follows a published skill can run commands; set
|
|
112
|
+
`TAU_EXCLUDE_BASE_TOOLS=true` when that must not happen.
|
|
113
|
+
|
|
99
114
|
## Invariants extensions must not replace
|
|
100
115
|
|
|
101
116
|
Extensions may change effective agent capabilities, but they must not replace or bypass:
|
|
@@ -167,8 +182,6 @@ Rules for tools that act for the requester:
|
|
|
167
182
|
- A delegated call uses the person's ordinary permissions, including administrative ones; any other
|
|
168
183
|
call uses the Agent's own. Each operation decides what it needs. Never infer write permission
|
|
169
184
|
from view access.
|
|
170
|
-
- This needs the platform's matching change, deployed in the same cutover; check the deployed
|
|
171
|
-
platform and the installed SDK before depending on it.
|
|
172
185
|
- Return to the model only business results, such as rows, numbers, and names. Never return the
|
|
173
186
|
response object, its headers, a token, a proof, or a raw error body.
|
|
174
187
|
- Keep nothing for another turn or another person. The delegation ends with the turn, and a task
|
|
@@ -178,7 +178,13 @@ What makes it safe:
|
|
|
178
178
|
|
|
179
179
|
A delegated read returns anything the person can view in the Agent's Environment: what they
|
|
180
180
|
created and what others shared with them, directly or through a team. This holds for Secrets and
|
|
181
|
-
|
|
181
|
+
other shared resources. Ordinary Teams use explicit membership: the creator is added once and
|
|
182
|
+
can be removed; current Organization admins are protected writer members. Removing the admin
|
|
183
|
+
role keeps membership and writer access but ends protection. Only Organization admins manage
|
|
184
|
+
membership, and managing a workload that created a Team does not grant Team control. A Team's
|
|
185
|
+
members still receive the resources shared with it. The Full directory remains unchanged.
|
|
186
|
+
See the SDK Security and access guide's **Teams and membership** section for platform rollout.
|
|
187
|
+
The platform enforces sharing; which of those objects a tool uses is part of
|
|
182
188
|
the tool's design. For Secrets, the naming convention states that design:
|
|
183
189
|
|
|
184
190
|
- **A personal credential:** a name tied to the person, such as `QUOTES_API_KEY__<person uid>`.
|
|
@@ -382,6 +382,72 @@ def _unsupported_operation_json_rpc_error(
|
|
|
382
382
|
}
|
|
383
383
|
|
|
384
384
|
|
|
385
|
+
class _MessageLimitExceeded(Exception):
|
|
386
|
+
"""A Message-mode request reached its time limit before the Agent answered."""
|
|
387
|
+
|
|
388
|
+
def __init__(self, limit_seconds: float) -> None:
|
|
389
|
+
super().__init__(f"The Message time limit of {limit_seconds} seconds expired")
|
|
390
|
+
self.limit_seconds = limit_seconds
|
|
391
|
+
|
|
392
|
+
|
|
393
|
+
def _message_limit_exceeded(context_id: str, limit_seconds: float) -> _MessageLimitExceeded:
|
|
394
|
+
logger.warning(
|
|
395
|
+
"a2a.message.limit_exceeded",
|
|
396
|
+
message="A Message-mode request reached its time limit before the Agent answered",
|
|
397
|
+
context_id=context_id,
|
|
398
|
+
limit_seconds=limit_seconds,
|
|
399
|
+
)
|
|
400
|
+
return _MessageLimitExceeded(limit_seconds)
|
|
401
|
+
|
|
402
|
+
|
|
403
|
+
def _seconds_text(value: float) -> str:
|
|
404
|
+
return str(int(value)) if float(value).is_integer() else str(value)
|
|
405
|
+
|
|
406
|
+
|
|
407
|
+
def _message_limit_text(limit_seconds: float) -> str:
|
|
408
|
+
return (
|
|
409
|
+
f"The Agent did not answer the Message within {_seconds_text(limit_seconds)} seconds. "
|
|
410
|
+
"Use Task mode for longer work."
|
|
411
|
+
)
|
|
412
|
+
|
|
413
|
+
|
|
414
|
+
def _message_limit_error_info(limit_seconds: float) -> dict[str, Any]:
|
|
415
|
+
return {
|
|
416
|
+
"@type": "type.googleapis.com/google.rpc.ErrorInfo",
|
|
417
|
+
"reason": "MESSAGE_RESPONSE_LIMIT_EXCEEDED",
|
|
418
|
+
"domain": "mainsequence.ai",
|
|
419
|
+
# ErrorInfo metadata values are strings.
|
|
420
|
+
"metadata": {"limitSeconds": _seconds_text(limit_seconds)},
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
|
|
424
|
+
def _message_limit_rest_error(limit_seconds: float) -> JSONResponse:
|
|
425
|
+
return JSONResponse(
|
|
426
|
+
status_code=504,
|
|
427
|
+
media_type="application/a2a+json",
|
|
428
|
+
content={
|
|
429
|
+
"error": {
|
|
430
|
+
"code": 504,
|
|
431
|
+
"status": "DEADLINE_EXCEEDED",
|
|
432
|
+
"message": _message_limit_text(limit_seconds),
|
|
433
|
+
"details": [_message_limit_error_info(limit_seconds)],
|
|
434
|
+
}
|
|
435
|
+
},
|
|
436
|
+
)
|
|
437
|
+
|
|
438
|
+
|
|
439
|
+
def _message_limit_json_rpc_error(request_id: object, limit_seconds: float) -> dict[str, Any]:
|
|
440
|
+
return {
|
|
441
|
+
"jsonrpc": "2.0",
|
|
442
|
+
"id": request_id,
|
|
443
|
+
"error": {
|
|
444
|
+
"code": -32000,
|
|
445
|
+
"message": _message_limit_text(limit_seconds),
|
|
446
|
+
"data": [_message_limit_error_info(limit_seconds)],
|
|
447
|
+
},
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
|
|
385
451
|
def _without_push_notification_capability(
|
|
386
452
|
agent_card: dict[str, Any] | None,
|
|
387
453
|
) -> dict[str, Any] | None:
|
|
@@ -744,6 +810,7 @@ async def _collect_turn(
|
|
|
744
810
|
provenance: TurnProvenance,
|
|
745
811
|
turn_uid: str | None = None,
|
|
746
812
|
caller_assertion: CallerAssertion | None = None,
|
|
813
|
+
deadline: float | None = None,
|
|
747
814
|
) -> str:
|
|
748
815
|
accumulator = _TurnAccumulator(max_output_bytes=max_output_bytes)
|
|
749
816
|
prompt_options: dict[str, Any] = {"provenance": provenance}
|
|
@@ -751,6 +818,8 @@ async def _collect_turn(
|
|
|
751
818
|
prompt_options["turn_uid"] = turn_uid
|
|
752
819
|
if caller_assertion is not None:
|
|
753
820
|
prompt_options["caller_assertion"] = caller_assertion
|
|
821
|
+
if deadline is not None:
|
|
822
|
+
prompt_options["deadline"] = deadline
|
|
754
823
|
async for event in manager.prompt(context_id, prompt, **prompt_options):
|
|
755
824
|
accumulator.consume(event)
|
|
756
825
|
context = active_task_execution()
|
|
@@ -779,6 +848,7 @@ async def _collect_validated_turn(
|
|
|
779
848
|
provenance: TurnProvenance,
|
|
780
849
|
turn_uid: str | None = None,
|
|
781
850
|
caller_assertion: CallerAssertion | None = None,
|
|
851
|
+
deadline: float | None = None,
|
|
782
852
|
) -> str:
|
|
783
853
|
text = await _collect_turn(
|
|
784
854
|
manager,
|
|
@@ -788,6 +858,7 @@ async def _collect_validated_turn(
|
|
|
788
858
|
provenance=provenance,
|
|
789
859
|
turn_uid=turn_uid,
|
|
790
860
|
caller_assertion=caller_assertion,
|
|
861
|
+
deadline=deadline,
|
|
791
862
|
)
|
|
792
863
|
if not contract.enabled:
|
|
793
864
|
return text
|
|
@@ -818,6 +889,7 @@ async def _collect_validated_turn(
|
|
|
818
889
|
max_output_bytes=max_output_bytes,
|
|
819
890
|
provenance=provenance,
|
|
820
891
|
caller_assertion=caller_assertion,
|
|
892
|
+
deadline=deadline,
|
|
821
893
|
)
|
|
822
894
|
raise AssertionError("strict JSON repair loop did not terminate")
|
|
823
895
|
|
|
@@ -948,6 +1020,16 @@ async def _wait_for_task_return_state(
|
|
|
948
1020
|
return current
|
|
949
1021
|
|
|
950
1022
|
|
|
1023
|
+
def _task_wait_seconds(config: TauSDKSettings, deadline: float | None) -> float:
|
|
1024
|
+
"""How long a request waits for its Task: the configured wait, ended by the platform's
|
|
1025
|
+
Message limit when the platform sets one. Either way the Task keeps running."""
|
|
1026
|
+
|
|
1027
|
+
if deadline is None:
|
|
1028
|
+
return config.a2a_task_wait_timeout_seconds
|
|
1029
|
+
remaining = deadline - asyncio.get_running_loop().time()
|
|
1030
|
+
return min(config.a2a_task_wait_timeout_seconds, remaining)
|
|
1031
|
+
|
|
1032
|
+
|
|
951
1033
|
@dataclass(frozen=True, slots=True)
|
|
952
1034
|
class _ClaimedTask:
|
|
953
1035
|
attempt_uid: str
|
|
@@ -1621,6 +1703,7 @@ async def _execute_message(
|
|
|
1621
1703
|
max_output_bytes: int,
|
|
1622
1704
|
provenance: TurnProvenance,
|
|
1623
1705
|
caller_assertion: CallerAssertion | None = None,
|
|
1706
|
+
deadline: float | None = None,
|
|
1624
1707
|
) -> dict[str, Any]:
|
|
1625
1708
|
text = await _collect_validated_turn(
|
|
1626
1709
|
manager,
|
|
@@ -1630,6 +1713,7 @@ async def _execute_message(
|
|
|
1630
1713
|
max_output_bytes=max_output_bytes,
|
|
1631
1714
|
provenance=provenance,
|
|
1632
1715
|
caller_assertion=caller_assertion,
|
|
1716
|
+
deadline=deadline,
|
|
1633
1717
|
)
|
|
1634
1718
|
message = _agent_message(
|
|
1635
1719
|
context_id=context_id,
|
|
@@ -2288,6 +2372,32 @@ async def message_send(
|
|
|
2288
2372
|
Header(alias="A2A-Extensions"),
|
|
2289
2373
|
] = None,
|
|
2290
2374
|
) -> dict[str, Any]:
|
|
2375
|
+
try:
|
|
2376
|
+
return await _message_send(body, client, manager, config, request, a2a_extensions)
|
|
2377
|
+
except _MessageLimitExceeded as error:
|
|
2378
|
+
return _message_limit_rest_error(error.limit_seconds) # type: ignore[return-value]
|
|
2379
|
+
|
|
2380
|
+
|
|
2381
|
+
async def _message_send(
|
|
2382
|
+
body: dict[str, Any],
|
|
2383
|
+
client: MainSequenceClient,
|
|
2384
|
+
manager: SessionRuntimeManager,
|
|
2385
|
+
config: TauSDKSettings,
|
|
2386
|
+
request: Request,
|
|
2387
|
+
a2a_extensions: str | None,
|
|
2388
|
+
) -> dict[str, Any]:
|
|
2389
|
+
"""Answer one ``message:send``, for REST and JSON-RPC alike.
|
|
2390
|
+
|
|
2391
|
+
When the platform sets a Message limit, it runs from the moment the request arrives. It
|
|
2392
|
+
bounds a Message-mode request as a whole, including output-contract repair turns, and it ends
|
|
2393
|
+
the wait of a Task-mode request that does not return immediately. A Message-mode request that
|
|
2394
|
+
reaches its limit, or its turn's own limit, raises ``_MessageLimitExceeded``, which each
|
|
2395
|
+
transport answers in its own form.
|
|
2396
|
+
"""
|
|
2397
|
+
platform_limit = config.a2a_message_limit_seconds
|
|
2398
|
+
deadline = (
|
|
2399
|
+
None if platform_limit is None else asyncio.get_running_loop().time() + platform_limit
|
|
2400
|
+
)
|
|
2291
2401
|
try:
|
|
2292
2402
|
provenance = _a2a_turn_provenance(config, request)
|
|
2293
2403
|
except CallerIdentityError as error:
|
|
@@ -2362,7 +2472,7 @@ async def message_send(
|
|
|
2362
2472
|
client,
|
|
2363
2473
|
task,
|
|
2364
2474
|
poll_interval_seconds=config.a2a_task_event_poll_interval_seconds,
|
|
2365
|
-
wait_timeout_seconds=config
|
|
2475
|
+
wait_timeout_seconds=_task_wait_seconds(config, deadline),
|
|
2366
2476
|
)
|
|
2367
2477
|
task = await _task_with_history(client, task, history_length=history_length)
|
|
2368
2478
|
return {"task": _task_payload(task, history_length=history_length)}
|
|
@@ -2397,7 +2507,7 @@ async def message_send(
|
|
|
2397
2507
|
client,
|
|
2398
2508
|
task,
|
|
2399
2509
|
poll_interval_seconds=config.a2a_task_event_poll_interval_seconds,
|
|
2400
|
-
wait_timeout_seconds=config
|
|
2510
|
+
wait_timeout_seconds=_task_wait_seconds(config, deadline),
|
|
2401
2511
|
)
|
|
2402
2512
|
task = await _task_with_history(client, task, history_length=history_length)
|
|
2403
2513
|
return {"task": _task_payload(task, history_length=history_length)}
|
|
@@ -2422,31 +2532,52 @@ async def message_send(
|
|
|
2422
2532
|
client,
|
|
2423
2533
|
task,
|
|
2424
2534
|
poll_interval_seconds=config.a2a_task_event_poll_interval_seconds,
|
|
2425
|
-
wait_timeout_seconds=config
|
|
2535
|
+
wait_timeout_seconds=_task_wait_seconds(config, deadline),
|
|
2426
2536
|
)
|
|
2427
2537
|
task = await _task_with_history(client, task, history_length=history_length)
|
|
2428
2538
|
return {"task": _task_payload(task, history_length=history_length)}
|
|
2429
2539
|
request_message_id = str(message["messageId"])
|
|
2540
|
+
context_id = str(message["contextId"])
|
|
2430
2541
|
if config.local_mode:
|
|
2431
2542
|
replay = await client.begin_local_conversation_message(message_to_protocol(message))
|
|
2432
2543
|
if replay is not None:
|
|
2433
2544
|
return {"message": replay}
|
|
2434
|
-
|
|
2435
|
-
|
|
2436
|
-
|
|
2437
|
-
|
|
2438
|
-
|
|
2439
|
-
|
|
2440
|
-
|
|
2441
|
-
|
|
2442
|
-
|
|
2545
|
+
if (
|
|
2546
|
+
platform_limit is not None
|
|
2547
|
+
and deadline is not None
|
|
2548
|
+
and asyncio.get_running_loop().time() >= deadline
|
|
2549
|
+
):
|
|
2550
|
+
# The request used up the platform's limit before its turn could start, so none starts.
|
|
2551
|
+
raise _message_limit_exceeded(context_id, platform_limit)
|
|
2552
|
+
request_limit = asyncio.timeout_at(deadline)
|
|
2553
|
+
try:
|
|
2554
|
+
async with request_limit:
|
|
2555
|
+
result = await _execute_message(
|
|
2556
|
+
manager,
|
|
2557
|
+
context_id=context_id,
|
|
2558
|
+
prompt=prompt,
|
|
2559
|
+
output_contract=output_contract,
|
|
2560
|
+
max_output_bytes=config.max_turn_output_bytes,
|
|
2561
|
+
provenance=provenance,
|
|
2562
|
+
caller_assertion=request_caller_assertion(request),
|
|
2563
|
+
deadline=deadline,
|
|
2564
|
+
)
|
|
2565
|
+
except TimeoutError as error:
|
|
2566
|
+
# The platform's limit for the request or the turn's own limit expired, and the turn was
|
|
2567
|
+
# stopped. The caller learns which limit.
|
|
2568
|
+
raise _message_limit_exceeded(
|
|
2569
|
+
context_id,
|
|
2570
|
+
platform_limit
|
|
2571
|
+
if platform_limit is not None and request_limit.expired()
|
|
2572
|
+
else config.turn_timeout_seconds,
|
|
2573
|
+
) from error
|
|
2443
2574
|
if config.local_mode:
|
|
2444
2575
|
result = await client.complete_local_conversation_message(
|
|
2445
|
-
|
|
2576
|
+
context_id,
|
|
2446
2577
|
request_message_id,
|
|
2447
2578
|
result,
|
|
2448
2579
|
)
|
|
2449
|
-
manager.mark_response_delivered(
|
|
2580
|
+
manager.mark_response_delivered(context_id)
|
|
2450
2581
|
return {"message": result}
|
|
2451
2582
|
|
|
2452
2583
|
|
|
@@ -2703,14 +2834,17 @@ async def json_rpc(
|
|
|
2703
2834
|
|
|
2704
2835
|
try:
|
|
2705
2836
|
if method in {"SendMessage", "message/send"}:
|
|
2706
|
-
|
|
2707
|
-
|
|
2708
|
-
|
|
2709
|
-
|
|
2710
|
-
|
|
2711
|
-
|
|
2712
|
-
|
|
2713
|
-
|
|
2837
|
+
try:
|
|
2838
|
+
result = await _message_send(
|
|
2839
|
+
params,
|
|
2840
|
+
client,
|
|
2841
|
+
manager,
|
|
2842
|
+
config,
|
|
2843
|
+
request,
|
|
2844
|
+
a2a_extensions,
|
|
2845
|
+
)
|
|
2846
|
+
except _MessageLimitExceeded as error:
|
|
2847
|
+
return _message_limit_json_rpc_error(request_id, error.limit_seconds)
|
|
2714
2848
|
elif method in {"SendStreamingMessage", "message/stream"}:
|
|
2715
2849
|
_response_kind(
|
|
2716
2850
|
params,
|