ms-tau-sdk 2.0.6.dev40__tar.gz → 2.0.7.dev41__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (85) hide show
  1. {ms_tau_sdk-2.0.6.dev40 → ms_tau_sdk-2.0.7.dev41}/CHANGELOG.md +34 -0
  2. {ms_tau_sdk-2.0.6.dev40 → ms_tau_sdk-2.0.7.dev41}/PKG-INFO +12 -10
  3. {ms_tau_sdk-2.0.6.dev40 → ms_tau_sdk-2.0.7.dev41}/README.md +11 -9
  4. {ms_tau_sdk-2.0.6.dev40 → ms_tau_sdk-2.0.7.dev41}/pyproject.toml +1 -1
  5. {ms_tau_sdk-2.0.6.dev40 → ms_tau_sdk-2.0.7.dev41}/src/ms_tau_sdk/__init__.py +2 -2
  6. {ms_tau_sdk-2.0.6.dev40 → ms_tau_sdk-2.0.7.dev41}/src/ms_tau_sdk/agent_skills/tau_a2a_runtime_adapter/SKILL.md +7 -8
  7. {ms_tau_sdk-2.0.6.dev40 → ms_tau_sdk-2.0.7.dev41}/src/ms_tau_sdk/agent_skills/tau_project_customization/SKILL.md +42 -34
  8. {ms_tau_sdk-2.0.6.dev40 → ms_tau_sdk-2.0.7.dev41}/src/ms_tau_sdk/api/a2a.py +20 -48
  9. {ms_tau_sdk-2.0.6.dev40 → ms_tau_sdk-2.0.7.dev41}/src/ms_tau_sdk/backend/client.py +25 -19
  10. {ms_tau_sdk-2.0.6.dev40 → ms_tau_sdk-2.0.7.dev41}/src/ms_tau_sdk/backend/local.py +30 -10
  11. {ms_tau_sdk-2.0.6.dev40 → ms_tau_sdk-2.0.7.dev41}/src/ms_tau_sdk/runtime/manager.py +37 -53
  12. {ms_tau_sdk-2.0.6.dev40 → ms_tau_sdk-2.0.7.dev41}/src/ms_tau_sdk/runtime/requester.py +220 -120
  13. {ms_tau_sdk-2.0.6.dev40 → ms_tau_sdk-2.0.7.dev41}/src/ms_tau_sdk/runtime/task_context.py +5 -4
  14. {ms_tau_sdk-2.0.6.dev40 → ms_tau_sdk-2.0.7.dev41}/src/ms_tau_sdk/tools/mainsequence_mcp.py +34 -63
  15. {ms_tau_sdk-2.0.6.dev40 → ms_tau_sdk-2.0.7.dev41}/src/ms_tau_sdk/tools/mcp_applications.py +15 -23
  16. {ms_tau_sdk-2.0.6.dev40 → ms_tau_sdk-2.0.7.dev41}/src/ms_tau_sdk/tools/mcp_connection.py +3 -10
  17. {ms_tau_sdk-2.0.6.dev40 → ms_tau_sdk-2.0.7.dev41}/.gitignore +0 -0
  18. {ms_tau_sdk-2.0.6.dev40 → ms_tau_sdk-2.0.7.dev41}/packages/tau-board/src/ms_tau_board/__init__.py +0 -0
  19. {ms_tau_sdk-2.0.6.dev40 → ms_tau_sdk-2.0.7.dev41}/packages/tau-board/src/ms_tau_board/app.py +0 -0
  20. {ms_tau_sdk-2.0.6.dev40 → ms_tau_sdk-2.0.7.dev41}/packages/tau-board/src/ms_tau_board/cli.py +0 -0
  21. {ms_tau_sdk-2.0.6.dev40 → ms_tau_sdk-2.0.7.dev41}/packages/tau-board/src/ms_tau_board/config.py +0 -0
  22. {ms_tau_sdk-2.0.6.dev40 → ms_tau_sdk-2.0.7.dev41}/packages/tau-board/src/ms_tau_board/env_file.py +0 -0
  23. {ms_tau_sdk-2.0.6.dev40 → ms_tau_sdk-2.0.7.dev41}/packages/tau-board/src/ms_tau_board/logs.py +0 -0
  24. {ms_tau_sdk-2.0.6.dev40 → ms_tau_sdk-2.0.7.dev41}/packages/tau-board/src/ms_tau_board/proxy.py +0 -0
  25. {ms_tau_sdk-2.0.6.dev40 → ms_tau_sdk-2.0.7.dev41}/packages/tau-board/src/ms_tau_board/state.py +0 -0
  26. {ms_tau_sdk-2.0.6.dev40 → ms_tau_sdk-2.0.7.dev41}/packages/tau-board/src/ms_tau_board/static/BULMA-LICENSE.txt +0 -0
  27. {ms_tau_sdk-2.0.6.dev40 → ms_tau_sdk-2.0.7.dev41}/packages/tau-board/src/ms_tau_board/static/app.js +0 -0
  28. {ms_tau_sdk-2.0.6.dev40 → ms_tau_sdk-2.0.7.dev41}/packages/tau-board/src/ms_tau_board/static/board.css +0 -0
  29. {ms_tau_sdk-2.0.6.dev40 → ms_tau_sdk-2.0.7.dev41}/packages/tau-board/src/ms_tau_board/static/bulma.min.css +0 -0
  30. {ms_tau_sdk-2.0.6.dev40 → ms_tau_sdk-2.0.7.dev41}/packages/tau-board/src/ms_tau_board/static/index.html +0 -0
  31. {ms_tau_sdk-2.0.6.dev40 → ms_tau_sdk-2.0.7.dev41}/src/ms_tau_sdk/agent_skills/tau_local_development/SKILL.md +0 -0
  32. {ms_tau_sdk-2.0.6.dev40 → ms_tau_sdk-2.0.7.dev41}/src/ms_tau_sdk/agent_skills/tau_repository_integration/SKILL.md +0 -0
  33. {ms_tau_sdk-2.0.6.dev40 → ms_tau_sdk-2.0.7.dev41}/src/ms_tau_sdk/api/__init__.py +0 -0
  34. {ms_tau_sdk-2.0.6.dev40 → ms_tau_sdk-2.0.7.dev41}/src/ms_tau_sdk/api/chat.py +0 -0
  35. {ms_tau_sdk-2.0.6.dev40 → ms_tau_sdk-2.0.7.dev41}/src/ms_tau_sdk/api/conversations.py +0 -0
  36. {ms_tau_sdk-2.0.6.dev40 → ms_tau_sdk-2.0.7.dev41}/src/ms_tau_sdk/api/dependencies.py +0 -0
  37. {ms_tau_sdk-2.0.6.dev40 → ms_tau_sdk-2.0.7.dev41}/src/ms_tau_sdk/api/health.py +0 -0
  38. {ms_tau_sdk-2.0.6.dev40 → ms_tau_sdk-2.0.7.dev41}/src/ms_tau_sdk/api/inspection.py +0 -0
  39. {ms_tau_sdk-2.0.6.dev40 → ms_tau_sdk-2.0.7.dev41}/src/ms_tau_sdk/api/local_chat.py +0 -0
  40. {ms_tau_sdk-2.0.6.dev40 → ms_tau_sdk-2.0.7.dev41}/src/ms_tau_sdk/api/models.py +0 -0
  41. {ms_tau_sdk-2.0.6.dev40 → ms_tau_sdk-2.0.7.dev41}/src/ms_tau_sdk/api/request_identity.py +0 -0
  42. {ms_tau_sdk-2.0.6.dev40 → ms_tau_sdk-2.0.7.dev41}/src/ms_tau_sdk/api/sessions.py +0 -0
  43. {ms_tau_sdk-2.0.6.dev40 → ms_tau_sdk-2.0.7.dev41}/src/ms_tau_sdk/app.py +0 -0
  44. {ms_tau_sdk-2.0.6.dev40 → ms_tau_sdk-2.0.7.dev41}/src/ms_tau_sdk/application.py +0 -0
  45. {ms_tau_sdk-2.0.6.dev40 → ms_tau_sdk-2.0.7.dev41}/src/ms_tau_sdk/backend/__init__.py +0 -0
  46. {ms_tau_sdk-2.0.6.dev40 → ms_tau_sdk-2.0.7.dev41}/src/ms_tau_sdk/backend/assertions.py +0 -0
  47. {ms_tau_sdk-2.0.6.dev40 → ms_tau_sdk-2.0.7.dev41}/src/ms_tau_sdk/backend/auth.py +0 -0
  48. {ms_tau_sdk-2.0.6.dev40 → ms_tau_sdk-2.0.7.dev41}/src/ms_tau_sdk/backend/mcp.py +0 -0
  49. {ms_tau_sdk-2.0.6.dev40 → ms_tau_sdk-2.0.7.dev41}/src/ms_tau_sdk/backend/models.py +0 -0
  50. {ms_tau_sdk-2.0.6.dev40 → ms_tau_sdk-2.0.7.dev41}/src/ms_tau_sdk/backend/routes.py +0 -0
  51. {ms_tau_sdk-2.0.6.dev40 → ms_tau_sdk-2.0.7.dev41}/src/ms_tau_sdk/cli.py +0 -0
  52. {ms_tau_sdk-2.0.6.dev40 → ms_tau_sdk-2.0.7.dev41}/src/ms_tau_sdk/errors.py +0 -0
  53. {ms_tau_sdk-2.0.6.dev40 → ms_tau_sdk-2.0.7.dev41}/src/ms_tau_sdk/logging.py +0 -0
  54. {ms_tau_sdk-2.0.6.dev40 → ms_tau_sdk-2.0.7.dev41}/src/ms_tau_sdk/protocols/__init__.py +0 -0
  55. {ms_tau_sdk-2.0.6.dev40 → ms_tau_sdk-2.0.7.dev41}/src/ms_tau_sdk/protocols/a2a_failure.py +0 -0
  56. {ms_tau_sdk-2.0.6.dev40 → ms_tau_sdk-2.0.7.dev41}/src/ms_tau_sdk/protocols/a2a_message.py +0 -0
  57. {ms_tau_sdk-2.0.6.dev40 → ms_tau_sdk-2.0.7.dev41}/src/ms_tau_sdk/protocols/a2a_roles.py +0 -0
  58. {ms_tau_sdk-2.0.6.dev40 → ms_tau_sdk-2.0.7.dev41}/src/ms_tau_sdk/protocols/assistant_ui.py +0 -0
  59. {ms_tau_sdk-2.0.6.dev40 → ms_tau_sdk-2.0.7.dev41}/src/ms_tau_sdk/protocols/chat_history.py +0 -0
  60. {ms_tau_sdk-2.0.6.dev40 → ms_tau_sdk-2.0.7.dev41}/src/ms_tau_sdk/protocols/strict_json.py +0 -0
  61. {ms_tau_sdk-2.0.6.dev40 → ms_tau_sdk-2.0.7.dev41}/src/ms_tau_sdk/providers/__init__.py +0 -0
  62. {ms_tau_sdk-2.0.6.dev40 → ms_tau_sdk-2.0.7.dev41}/src/ms_tau_sdk/providers/definitions.py +0 -0
  63. {ms_tau_sdk-2.0.6.dev40 → ms_tau_sdk-2.0.7.dev41}/src/ms_tau_sdk/providers/factory.py +0 -0
  64. {ms_tau_sdk-2.0.6.dev40 → ms_tau_sdk-2.0.7.dev41}/src/ms_tau_sdk/providers/tau_compat.py +0 -0
  65. {ms_tau_sdk-2.0.6.dev40 → ms_tau_sdk-2.0.7.dev41}/src/ms_tau_sdk/resources/SYSTEM.md +0 -0
  66. {ms_tau_sdk-2.0.6.dev40 → ms_tau_sdk-2.0.7.dev41}/src/ms_tau_sdk/resources/__init__.py +0 -0
  67. {ms_tau_sdk-2.0.6.dev40 → ms_tau_sdk-2.0.7.dev41}/src/ms_tau_sdk/resources/loader.py +0 -0
  68. {ms_tau_sdk-2.0.6.dev40 → ms_tau_sdk-2.0.7.dev41}/src/ms_tau_sdk/resources/prompts/review-code-repository.md +0 -0
  69. {ms_tau_sdk-2.0.6.dev40 → ms_tau_sdk-2.0.7.dev41}/src/ms_tau_sdk/runtime/__init__.py +0 -0
  70. {ms_tau_sdk-2.0.6.dev40 → ms_tau_sdk-2.0.7.dev41}/src/ms_tau_sdk/runtime/deployment_health.py +0 -0
  71. {ms_tau_sdk-2.0.6.dev40 → ms_tau_sdk-2.0.7.dev41}/src/ms_tau_sdk/runtime/events.py +0 -0
  72. {ms_tau_sdk-2.0.6.dev40 → ms_tau_sdk-2.0.7.dev41}/src/ms_tau_sdk/runtime/extensions.py +0 -0
  73. {ms_tau_sdk-2.0.6.dev40 → ms_tau_sdk-2.0.7.dev41}/src/ms_tau_sdk/runtime/failures.py +0 -0
  74. {ms_tau_sdk-2.0.6.dev40 → ms_tau_sdk-2.0.7.dev41}/src/ms_tau_sdk/runtime/live_turns.py +0 -0
  75. {ms_tau_sdk-2.0.6.dev40 → ms_tau_sdk-2.0.7.dev41}/src/ms_tau_sdk/runtime/observability.py +0 -0
  76. {ms_tau_sdk-2.0.6.dev40 → ms_tau_sdk-2.0.7.dev41}/src/ms_tau_sdk/runtime/provenance.py +0 -0
  77. {ms_tau_sdk-2.0.6.dev40 → ms_tau_sdk-2.0.7.dev41}/src/ms_tau_sdk/runtime/session.py +0 -0
  78. {ms_tau_sdk-2.0.6.dev40 → ms_tau_sdk-2.0.7.dev41}/src/ms_tau_sdk/runtime/snapshots.py +0 -0
  79. {ms_tau_sdk-2.0.6.dev40 → ms_tau_sdk-2.0.7.dev41}/src/ms_tau_sdk/sessions/__init__.py +0 -0
  80. {ms_tau_sdk-2.0.6.dev40 → ms_tau_sdk-2.0.7.dev41}/src/ms_tau_sdk/sessions/storage.py +0 -0
  81. {ms_tau_sdk-2.0.6.dev40 → ms_tau_sdk-2.0.7.dev41}/src/ms_tau_sdk/settings.py +0 -0
  82. {ms_tau_sdk-2.0.6.dev40 → ms_tau_sdk-2.0.7.dev41}/src/ms_tau_sdk/skills.py +0 -0
  83. {ms_tau_sdk-2.0.6.dev40 → ms_tau_sdk-2.0.7.dev41}/src/ms_tau_sdk/tools/__init__.py +0 -0
  84. {ms_tau_sdk-2.0.6.dev40 → ms_tau_sdk-2.0.7.dev41}/src/ms_tau_sdk/tools/skill_read.py +0 -0
  85. {ms_tau_sdk-2.0.6.dev40 → ms_tau_sdk-2.0.7.dev41}/src/ms_tau_sdk/tools/task_control.py +0 -0
@@ -2,6 +2,40 @@
2
2
 
3
3
  ## Unreleased
4
4
 
5
+ - **Breaking: one delegation envelope on every call made for the work
6
+ ([#78](https://github.com/mainsequence-sdk/ms-tau-sdk/issues/78), ADR 0021 amendment).** This
7
+ release works only with the matching platform change, deployed in the same cutover; there is no
8
+ deprecation period, and earlier releases, including 2.0.6, stop acting for people once that
9
+ platform is deployed.
10
+ - Every hosted Main Sequence MCP call names the turn's session, and carries the person's
11
+ delegation (`mainsequence.ai/delegation/v1`) while the turn serves a person. The platform's
12
+ per-tool labels (`mainsequence.ai/requires-requester/v1`,
13
+ `mainsequence.ai/requires-caller-session-proof/v1`) are no longer read, and a turn that serves
14
+ nobody keeps its MCP tools: its calls are the Agent's own.
15
+ - Declared application tools run in every turn, with a token for the person the turn serves or
16
+ the Agent's own token when it serves nobody. A refused delegated call is reported and never
17
+ retried as the Agent.
18
+ - The person a turn serves comes only from the platform's answer to the turn start, whoever
19
+ called; dispatch, delivery and Task answers are no longer read for it. Delegated work serves
20
+ the person the platform records for it.
21
+ - `platform_client()` replaces `requester_client()`, with no alias. By default a call carries the
22
+ delegation while the turn serves a person; `delegation="none"` never carries it and
23
+ `delegation="required"` refuses the call before sending when the turn serves nobody. Outside a
24
+ turn no call carries a delegation, and in local mode calls use the signed-in person's own
25
+ credential.
26
+ - Delegated calls use the person's ordinary permissions, including administrative ones; the
27
+ statement people are shown changes accordingly.
28
+ - The SDK no longer hides the withdrawn private Secret entry operations, and the local health
29
+ field `mcp_session_proof_limited_tool_count` is removed.
30
+
31
+ - Documentation now distinguishes requester-bound reads and supported writes from workload grants,
32
+ states the write risk and platform rollout requirements, and includes conversation/Task
33
+ permissions and denial recovery. The Security and access guide keeps its `security-model.md`
34
+ path. Provider selection settings and troubleshooting, the public readiness-hook reference, and
35
+ the exported API compatibility list are complete. This changes documentation only.
36
+
37
+ ## 2.0.6 — 2026-10-07
38
+
5
39
  - The documentation no longer describes the platform's internal implementation. It no longer names
6
40
  the backend framework, its repositories, ADRs, classes or commits, and calls the backend "the
7
41
  platform". The archived Astro documents (`docs/history/astro`) and the ADR 56 migration workspace
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: ms-tau-sdk
3
- Version: 2.0.6.dev40
3
+ Version: 2.0.7.dev41
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
@@ -133,10 +133,11 @@ only an application that declares it, so serve the application `create_app()` re
133
133
  hosting, requests are handled as before. See
134
134
  [request identity](docs/reference/runtime-contract.md#request-identity).
135
135
 
136
- An Agent that an Organization admin enabled for it can read with the access of the person whose
137
- request a turn is serving. Project tools read that person with `current_requester()` and call the
138
- platform or another platform application for them with `requester_client()`; neither exposes a
139
- proof or a token. See the [public API](docs/reference/public-api.md#current_requester-and-requester_client).
136
+ An Agent that an Organization admin enabled for it can act with the ordinary permissions of the
137
+ person whose request a turn is serving. Project tools read that person with `current_requester()`
138
+ and call the platform or another platform application with `platform_client()`, which carries the
139
+ person's delegation by default while the turn serves one; neither exposes a proof or a token. See
140
+ the [public API](docs/reference/public-api.md#current_requester-and-platform_client).
140
141
 
141
142
  Job-hosted batch execution is an [accepted design](docs/adrs/0015-job-hosted-batch-execution.md)
142
143
  with implementation pending. It will run this SDK's configured Tau composition for one assignment
@@ -287,13 +288,14 @@ are not bundled into the SDK. Main Sequence transport and protocol behavior rema
287
288
  - explicit, version-matched development skills for repository integration, local debugging,
288
289
  project customization, and TAU's A2A host adapter
289
290
 
290
- ## Security model
291
+ ## Security and access guide
291
292
 
292
- A hosted Agent runs as its own workload, with no access until it is granted, and never reads or
293
- writes Secret values. An Organization admin can let it read with the access of the person it is
294
- serving, read-only and for at most 24 hours. Everything its tools read reaches the model, and its
293
+ A hosted Agent runs as its own workload, with no access until it is granted. An Organization admin
294
+ can let it act with the ordinary permissions of the person it serves, including administrative
295
+ ones, only during that work and for at most 24 hours. Prompt injection can cause unintended actions within those rights;
296
+ completed changes can outlast that access. Everything its tools read reaches the model, and its
295
297
  tools and extensions run with its credentials. The
296
- [agent security model](docs/reference/security-model.md) explains what people who build and use
298
+ [Security and access guide](docs/reference/security-model.md) explains what people who build and use
297
299
  Agents need to know.
298
300
 
299
301
  ## Deployment boundary
@@ -100,10 +100,11 @@ only an application that declares it, so serve the application `create_app()` re
100
100
  hosting, requests are handled as before. See
101
101
  [request identity](docs/reference/runtime-contract.md#request-identity).
102
102
 
103
- An Agent that an Organization admin enabled for it can read with the access of the person whose
104
- request a turn is serving. Project tools read that person with `current_requester()` and call the
105
- platform or another platform application for them with `requester_client()`; neither exposes a
106
- proof or a token. See the [public API](docs/reference/public-api.md#current_requester-and-requester_client).
103
+ An Agent that an Organization admin enabled for it can act with the ordinary permissions of the
104
+ person whose request a turn is serving. Project tools read that person with `current_requester()`
105
+ and call the platform or another platform application with `platform_client()`, which carries the
106
+ person's delegation by default while the turn serves one; neither exposes a proof or a token. See
107
+ the [public API](docs/reference/public-api.md#current_requester-and-platform_client).
107
108
 
108
109
  Job-hosted batch execution is an [accepted design](docs/adrs/0015-job-hosted-batch-execution.md)
109
110
  with implementation pending. It will run this SDK's configured Tau composition for one assignment
@@ -254,13 +255,14 @@ are not bundled into the SDK. Main Sequence transport and protocol behavior rema
254
255
  - explicit, version-matched development skills for repository integration, local debugging,
255
256
  project customization, and TAU's A2A host adapter
256
257
 
257
- ## Security model
258
+ ## Security and access guide
258
259
 
259
- A hosted Agent runs as its own workload, with no access until it is granted, and never reads or
260
- writes Secret values. An Organization admin can let it read with the access of the person it is
261
- serving, read-only and for at most 24 hours. Everything its tools read reaches the model, and its
260
+ A hosted Agent runs as its own workload, with no access until it is granted. An Organization admin
261
+ can let it act with the ordinary permissions of the person it serves, including administrative
262
+ ones, only during that work and for at most 24 hours. Prompt injection can cause unintended actions within those rights;
263
+ completed changes can outlast that access. Everything its tools read reaches the model, and its
262
264
  tools and extensions run with its credentials. The
263
- [agent security model](docs/reference/security-model.md) explains what people who build and use
265
+ [Security and access guide](docs/reference/security-model.md) explains what people who build and use
264
266
  Agents need to know.
265
267
 
266
268
  ## Deployment boundary
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "ms-tau-sdk"
7
- version = "2.0.6.dev40"
7
+ version = "2.0.7.dev41"
8
8
  description = "Workspace-bound Tau application primitives for Main Sequence projects"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.13"
@@ -9,7 +9,7 @@ from .runtime.deployment_health import (
9
9
  RUNTIME_HEALTH_ABI_VERSION,
10
10
  register_deployment_readiness_hook,
11
11
  )
12
- from .runtime.requester import current_requester, requester_client
12
+ from .runtime.requester import current_requester, platform_client
13
13
  from .settings import TauSDKSettings
14
14
 
15
15
  __all__ = [
@@ -18,6 +18,6 @@ __all__ = [
18
18
  "__version__",
19
19
  "create_app",
20
20
  "current_requester",
21
+ "platform_client",
21
22
  "register_deployment_readiness_hook",
22
- "requester_client",
23
23
  ]
@@ -23,19 +23,18 @@ The SDK owns:
23
23
 
24
24
  - projecting the authenticated Main Sequence MCP catalog into a TAU session;
25
25
  - preserving each tool's canonical name and metadata through host normalization;
26
- - privately attaching active caller-session proof when a tool advertises
27
- `mainsequence.ai/requires-caller-session-proof/v1: true`;
28
- - attaching the same proof when a tool advertises `mainsequence.ai/requires-requester/v1: true`,
29
- which the platform runs for the turn's requester, and refusing that tool before sending it in a
30
- hosted turn that serves nobody;
26
+ - privately attaching, to every hosted Main Sequence MCP call, the turn's caller-session proof under
27
+ `mainsequence.ai/caller-session-proof/v1`, and, while the turn serves a person, the same proof as
28
+ that person's delegation under `mainsequence.ai/delegation/v1`. No tool metadata decides it;
31
29
  - verifying the platform's signed caller or platform assertion on every inbound request of a
32
30
  hosted runtime, and admitting a request to a session or its Tasks only for the session's owner,
33
31
  an Organization admin, or the workload User of the Agent that delegated to that child session,
34
32
  which the platform's own records establish, never the `X-Caller-*` headers;
35
33
  - presenting the verified caller assertion of the request that starts a chat or A2A Message turn
36
- only when it marks that turn active, so that the platform can record the turn's requester, and
37
- giving extension tools that requester and requester-bound calls through `current_requester()`
38
- and `requester_client()` without exposing the assertion, the lease proof, or any token;
34
+ only when it marks that turn active, so that the platform can record the turn's requester;
35
+ taking the person the turn serves only from the platform's answer to the turn start; and giving
36
+ extension tools that person and calls for the work through `current_requester()` and
37
+ `platform_client()` without exposing the assertion, the lease proof, or any token;
39
38
  - translating inbound A2A requests into the shared TAU runtime; and
40
39
  - translating runtime events and results into validated A2A responses.
41
40
 
@@ -97,10 +97,11 @@ tools do not grant new platform permissions merely because they run inside TAU.
97
97
 
98
98
  ## Acting for the person a turn serves
99
99
 
100
- An Agent that an Organization admin enabled for it can read platform data, and ask other platform
101
- applications, with the access of the person whose request a turn is serving: the requester. The
102
- platform keeps that authority and finds the person in its own records; the runtime only proves
103
- which of its sessions it is working on. Extension tools use two SDK functions and nothing else:
100
+ An Agent that an Organization admin enabled for it can call the platform and other platform
101
+ applications with the ordinary permissions of the person whose request a turn is serving: the
102
+ requester. The platform keeps that authority and finds the person in its own records; the runtime
103
+ only proves which of its sessions it is working on. Extension tools use two SDK functions and
104
+ nothing else:
104
105
 
105
106
  ```python
106
107
  import json
@@ -108,64 +109,71 @@ import json
108
109
  from tau_agent.messages import TextContent
109
110
  from tau_agent.tools import AgentToolResult
110
111
 
111
- from ms_tau_sdk import current_requester, requester_client
112
+ from ms_tau_sdk import platform_client
112
113
 
113
114
  ANALYST_DATA_RELEASE_UID = "..." # the release of the application to ask
114
115
 
115
116
 
116
117
  async def revenue_by_region(tool_call_id, arguments, signal=None, on_update=None):
117
- if current_requester() is None:
118
- return AgentToolResult(content=[TextContent(text="Ask me from your own conversation.")])
118
+ # This tool answers each person with their own data, so it needs a person.
119
119
  try:
120
- answer = await requester_client().call_release(
120
+ answer = await platform_client(delegation="required").call_release(
121
121
  ANALYST_DATA_RELEASE_UID,
122
122
  "POST",
123
123
  "/query",
124
124
  json={"question": "revenue by region"},
125
125
  )
126
126
  except PermissionError:
127
- return AgentToolResult(content=[TextContent(text="Your access for this request ended.")])
127
+ return AgentToolResult(content=[TextContent(text="Ask me from your own conversation.")])
128
128
  return AgentToolResult(content=[TextContent(text=json.dumps(answer.json()["rows"]))])
129
129
  ```
130
130
 
131
- - `current_requester()` returns the turn's verified requester, with `uid` and `team_uids`, or
132
- `None`. It is `None` for Agent callers, the platform's own calls other than a caller delivery
133
- (which resumes delegated work for the person who asked), local mode, and code outside a turn.
134
- Only it names the requester:
135
- never take a person's UID from tool arguments, the prompt, history, or a header.
136
- - `requester_client()` returns a client bound to the turn. `await client.request("GET",
137
- "/api/v1/...")` reads a platform API path for the requester. `await client.call_release(
138
- release_uid, method, path, ...)` asks another platform application, which answers as the
139
- requester. Both return an `httpx.Response`.
140
- - Without a requester, `requester_client()` raises a `PermissionError`. So does a call that the
141
- platform refuses because the requester's access ended (`code` `requester_binding_invalid` or
142
- `runtime_lease_*`): the turn is over, the access was removed, more than 24 hours passed, or the
143
- Agent is not enabled. Catch it, say in plain words that the request cannot be served, and never
144
- fall back to the Agent's own access.
131
+ - `current_requester()` returns the person the turn serves, with `uid` and `team_uids`, or
132
+ `None`. The platform names that person when the turn starts, whoever called; it is `None` when
133
+ the platform names nobody, in local mode, and in code outside a turn. Only it names the
134
+ requester: never take a person's UID from tool arguments, the prompt, history, or a header.
135
+ - `platform_client()` returns a client. `await client.request("GET", "/api/v1/...")` calls a
136
+ platform API path, and `await client.call_release(release_uid, method, path, ...)` calls another
137
+ platform application. Both return an `httpx.Response`.
138
+ - By default (`delegation="auto"`) a call carries the person's delegation while the turn serves a
139
+ person, and is the Agent's own otherwise; the operation or application decides what it may do.
140
+ `delegation="none"` never carries it. `delegation="required"` raises a `PermissionError` before
141
+ sending when the turn serves nobody; use it in a tool that only makes sense for a person.
142
+ - A call the platform refuses because the person's access ended (`code`
143
+ `requester_binding_invalid` or `runtime_lease_*`) raises a `PermissionError`: the turn is over,
144
+ the access was removed, more than 24 hours passed, or the Agent is not enabled. Catch it, say in
145
+ plain words that the request cannot be served, and never retry it as the Agent.
145
146
 
146
147
  Rules for tools that act for the requester:
147
148
 
148
149
  - Never handle proofs or tokens. The SDK attaches the session, the lease proof, and the
149
150
  credentials itself. A tool never reads, logs, stores, or forwards them, passes a path rather than
150
151
  a URL, and never sets `Authorization` or an `X-MainSequence-*` header.
151
- - Requester-bound calls are read-only. The platform refuses writes, sharing, and Secret values;
152
- do not build tools that try them.
152
+ - A delegated call uses the person's ordinary permissions, including administrative ones; any other
153
+ call uses the Agent's own. Each operation decides what it needs. Never infer write permission
154
+ from view access.
155
+ - This needs the platform's matching change, deployed in the same cutover; check the deployed
156
+ platform and the installed SDK before depending on it.
153
157
  - Return to the model only business results, such as rows, numbers, and names. Never return the
154
158
  response object, its headers, a token, a proof, or a raw error body.
155
- - Keep nothing for another turn or another person. The binding ends with the turn, and a task the
156
- tool leaves running has no access afterwards.
159
+ - Keep nothing for another turn or another person. The delegation ends with the turn, and a task
160
+ the tool leaves running acts only as the Agent afterwards.
157
161
  - What a tool reads for a person belongs to that person's conversation. Do not write it to shared
158
162
  stores, other Agents, or external systems.
159
163
 
160
164
  People who use such an Agent are told:
161
165
 
162
- > **This Agent works with your identity, securely.** It reads only what you can already read, only
163
- > to answer your own requests, and for at most 24 hours after you ask. It cannot act as anyone else,
164
- > cannot change, share or delete anything, never sees your secret values, and stops the moment your
165
- > access ends. Your Organization's administrator approved it to work this way.
166
-
167
- The limit is plain: while it works on your request, the Agent's code can read what you can read,
168
- which is why only administrators decide which Agents may work this way.
166
+ > **This Agent works with your identity.** It can read, create, change, run,
167
+ > share or delete only what your permissions allow through supported operations,
168
+ > only while serving your request, and for at most 24 hours after you ask. It
169
+ > uses your ordinary permissions, including administrative permissions, and
170
+ > access is checked on every call. Your Organization's administrator approved it
171
+ > to work this way.
172
+
173
+ The statement describes delegated calls. The Agent's own grants and trusted extension code remain
174
+ separate authority. Prompt injection can cause unintended changes, sharing, or deletion within the
175
+ person's permissions, and completed writes can outlast the delegation. Only Organization admins
176
+ enable this authority.
169
177
 
170
178
  ## Validation
171
179
 
@@ -20,7 +20,7 @@ from fastapi.responses import JSONResponse, StreamingResponse
20
20
  from structlog.contextvars import bind_contextvars
21
21
  from tau_agent.types import JSONValue
22
22
 
23
- from ms_tau_sdk.backend.assertions import CallerAssertion
23
+ from ms_tau_sdk.backend.assertions import CallerAssertion, VerifiedCaller
24
24
  from ms_tau_sdk.backend.client import MainSequenceClient
25
25
  from ms_tau_sdk.backend.models import (
26
26
  AgentTask,
@@ -63,7 +63,7 @@ from ms_tau_sdk.runtime.provenance import (
63
63
  TurnProvenance,
64
64
  turn_provenance_from_request,
65
65
  )
66
- from ms_tau_sdk.runtime.requester import CallerDelivery, Requester, canonical_requester_uid
66
+ from ms_tau_sdk.runtime.requester import CallerDelivery
67
67
  from ms_tau_sdk.runtime.task_context import (
68
68
  TaskExecutionContext,
69
69
  active_task_execution,
@@ -920,22 +920,14 @@ def _presented_assertion(caller_assertion: CallerAssertion | None) -> dict[str,
920
920
  return {"caller_assertion": token} if token else {}
921
921
 
922
922
 
923
- def _requester_named_by_task(
924
- task: AgentTask,
925
- caller_assertion: CallerAssertion | None,
926
- ) -> Requester | None:
927
- """Return the request's verified caller when the platform recorded that person as requester.
923
+ def _request_caller(caller_assertion: CallerAssertion | None) -> VerifiedCaller | None:
924
+ """Return the verified caller of a request that runs a Task attempt itself.
928
925
 
929
- This is the requester of a Task attempt that the request itself runs: the platform's Task
930
- answer must name the same User, as a person, as ``requester_user_uid``.
926
+ The platform's answer to the attempt's turn start names the person the attempt serves; this
927
+ caller only supplies that person's teams, when it is that person.
931
928
  """
932
929
 
933
- if caller_assertion is None or caller_assertion.caller_is_workload:
934
- return None
935
- caller = caller_assertion.caller
936
- if task.requester_identity_type != "human" or task.requester_user_uid != caller.uid:
937
- return None
938
- return Requester(uid=caller.uid, team_uids=caller.team_uids)
930
+ return caller_assertion.caller if caller_assertion is not None else None
939
931
 
940
932
 
941
933
  async def _wait_for_task_return_state(
@@ -1289,7 +1281,7 @@ async def _execute_task(
1289
1281
  config: TauSDKSettings | None = None,
1290
1282
  dispatch_uid: str | None = None,
1291
1283
  claim: _ClaimedTask | None = None,
1292
- requester: Requester | None = None,
1284
+ caller: VerifiedCaller | None = None,
1293
1285
  ) -> dict[str, Any]:
1294
1286
  bind_contextvars(
1295
1287
  a2a_task_id=task.task_id,
@@ -1322,7 +1314,7 @@ async def _execute_task(
1322
1314
  attempt_uid=claim.attempt_uid,
1323
1315
  holder_id=claim.holder_id,
1324
1316
  lease_token=claim.lease_token,
1325
- requester=requester,
1317
+ caller=caller,
1326
1318
  )
1327
1319
  try:
1328
1320
  await writer.ensure_not_canceled(force=True)
@@ -1408,7 +1400,7 @@ async def _schedule_task_accelerator(
1408
1400
  max_output_bytes: int,
1409
1401
  provenance: TurnProvenance,
1410
1402
  config: TauSDKSettings,
1411
- requester: Requester | None = None,
1403
+ caller: VerifiedCaller | None = None,
1412
1404
  ) -> bool:
1413
1405
  """Best-effort local start after durable creation; dispatch remains recovery owner."""
1414
1406
 
@@ -1440,7 +1432,7 @@ async def _schedule_task_accelerator(
1440
1432
  provenance=provenance,
1441
1433
  config=config,
1442
1434
  claim=claim,
1443
- requester=requester,
1435
+ caller=caller,
1444
1436
  )
1445
1437
  try:
1446
1438
  _background, scheduled = manager.create_a2a_task_execution(
@@ -1680,7 +1672,7 @@ async def _stream_task_events(
1680
1672
  provenance: TurnProvenance,
1681
1673
  config: TauSDKSettings | None = None,
1682
1674
  history_length: int = DEFAULT_TASK_HISTORY_LENGTH,
1683
- requester: Requester | None = None,
1675
+ caller: VerifiedCaller | None = None,
1684
1676
  ) -> AsyncIterator[dict[str, Any]]:
1685
1677
  claim = await _claim_backend_task(client, manager, task)
1686
1678
  resolved = config or manager.settings
@@ -1700,7 +1692,7 @@ async def _stream_task_events(
1700
1692
  attempt_uid=claim.attempt_uid,
1701
1693
  holder_id=claim.holder_id,
1702
1694
  lease_token=claim.lease_token,
1703
- requester=requester,
1695
+ caller=caller,
1704
1696
  )
1705
1697
  working = await client.get_task(task.uid)
1706
1698
  working = await _task_with_history(client, working, history_length=history_length)
@@ -1878,7 +1870,7 @@ def _message_stream_response(
1878
1870
  history_length: int = DEFAULT_TASK_HISTORY_LENGTH,
1879
1871
  json_rpc: bool = False,
1880
1872
  request_id: object = None,
1881
- requester: Requester | None = None,
1873
+ caller: VerifiedCaller | None = None,
1882
1874
  ) -> StreamingResponse:
1883
1875
  async def stream() -> AsyncIterator[bytes]:
1884
1876
  try:
@@ -1892,7 +1884,7 @@ def _message_stream_response(
1892
1884
  provenance=provenance,
1893
1885
  config=config,
1894
1886
  history_length=history_length,
1895
- requester=requester,
1887
+ caller=caller,
1896
1888
  ):
1897
1889
  yield _sse(
1898
1890
  _stream_payload(
@@ -2149,21 +2141,6 @@ def _durable_task_execution_input(
2149
2141
  return prompt, contract, provenance
2150
2142
 
2151
2143
 
2152
- def _dispatched_requester(body: dict[str, Any], config: TauSDKSettings) -> Requester | None:
2153
- """Return the requester that the platform's verified dispatch or caller delivery names.
2154
-
2155
- Only a person is a requester: an Agent's workload User never is. A runtime that does not
2156
- verify the platform's assertion on its internal routes takes no requester from them.
2157
- """
2158
-
2159
- if config.local_mode or config.request_identity_mode != "assertion":
2160
- return None
2161
- if body.get("requester_identity_type") != "human":
2162
- return None
2163
- uid = canonical_requester_uid(body.get("requester_user_uid"))
2164
- return Requester(uid=uid) if uid is not None else None
2165
-
2166
-
2167
2144
  @router.post("/internal/a2a/task-dispatch", status_code=200)
2168
2145
  async def task_dispatch_available(
2169
2146
  body: dict[str, Any],
@@ -2199,7 +2176,6 @@ async def task_dispatch_available(
2199
2176
  provenance=provenance,
2200
2177
  config=config,
2201
2178
  claim=claim,
2202
- requester=_dispatched_requester(body, config),
2203
2179
  ),
2204
2180
  name=f"a2a-task-{task.task_id}",
2205
2181
  )
@@ -2242,7 +2218,6 @@ async def _resume_caller_delivery(
2242
2218
  async def task_caller_delivery_available(
2243
2219
  body: dict[str, Any],
2244
2220
  manager: RuntimeManagerDep,
2245
- config: SettingsDep,
2246
2221
  ) -> dict[str, Any]:
2247
2222
  delivery_uid = str(body.get("delivery_uid") or "").strip()
2248
2223
  task_uid = str(body.get("task_uid") or "").strip()
@@ -2289,10 +2264,7 @@ async def task_caller_delivery_available(
2289
2264
  task_id=task_id,
2290
2265
  task_status=task_status,
2291
2266
  caller_agent_session_uid=caller_agent_session_uid,
2292
- delivery=CallerDelivery(
2293
- uid=delivery_uid,
2294
- requester=_dispatched_requester(body, config),
2295
- ),
2267
+ delivery=CallerDelivery(uid=delivery_uid),
2296
2268
  ),
2297
2269
  name=f"a2a-caller-delivery-{delivery_uid}",
2298
2270
  )
@@ -2381,7 +2353,7 @@ async def message_send(
2381
2353
  max_output_bytes=config.max_turn_output_bytes,
2382
2354
  provenance=provenance,
2383
2355
  config=config,
2384
- requester=_requester_named_by_task(task, caller_assertion),
2356
+ caller=_request_caller(caller_assertion),
2385
2357
  )
2386
2358
  if return_immediately:
2387
2359
  task = await _task_with_history(client, task, history_length=history_length)
@@ -2416,7 +2388,7 @@ async def message_send(
2416
2388
  max_output_bytes=config.max_turn_output_bytes,
2417
2389
  provenance=provenance,
2418
2390
  config=config,
2419
- requester=_requester_named_by_task(task, caller_assertion),
2391
+ caller=_request_caller(caller_assertion),
2420
2392
  )
2421
2393
  if return_immediately:
2422
2394
  task = await _task_with_history(client, task, history_length=history_length)
@@ -2556,7 +2528,7 @@ async def message_stream(
2556
2528
  provenance=provenance,
2557
2529
  config=config,
2558
2530
  history_length=history_length,
2559
- requester=_requester_named_by_task(task, caller_assertion),
2531
+ caller=_request_caller(caller_assertion),
2560
2532
  )
2561
2533
 
2562
2534
 
@@ -2808,7 +2780,7 @@ async def json_rpc(
2808
2780
  history_length=history_length,
2809
2781
  json_rpc=True,
2810
2782
  request_id=request_id,
2811
- requester=_requester_named_by_task(task, caller_assertion),
2783
+ caller=_request_caller(caller_assertion),
2812
2784
  )
2813
2785
  elif method in {"GetTask", "tasks/get"}:
2814
2786
  result = await get_task(
@@ -88,8 +88,8 @@ PATH_IDENTIFIER_PATTERN = re.compile(
88
88
  r"(?<=/)(?:[0-9a-f]{8}-[0-9a-f-]{27,}|[0-9]+)(?=/|$)",
89
89
  re.IGNORECASE,
90
90
  )
91
- # A requester-bound call names the session the runtime is serving and proves the runtime's lease on
92
- # it, beside the runtime's own credential. The platform finds the person in its own record of that
91
+ # A delegated call names the session the runtime is serving and proves the runtime's lease on it,
92
+ # beside the runtime's own credential. The platform finds the person in its own record of that
93
93
  # session's active turn or Task attempt: the call never names a person.
94
94
  ACTING_FOR_SESSION_HEADER = "X-MainSequence-Acting-For-Session"
95
95
  LEASE_HOLDER_HEADER = "X-MainSequence-Lease-Holder"
@@ -119,7 +119,7 @@ def _field_error_paths(value: object, *, prefix: str = "") -> list[str]:
119
119
 
120
120
  @dataclass(frozen=True, slots=True)
121
121
  class LeaseProof:
122
- """The session a requester-bound call serves, and the runtime's lease on that session."""
122
+ """The session a delegated call serves, and the runtime's lease on that session."""
123
123
 
124
124
  session_uid: str
125
125
  holder_id: str
@@ -601,14 +601,18 @@ class MainSequenceClient:
601
601
  self,
602
602
  release_uid: str,
603
603
  *,
604
- proof: LeaseProof,
604
+ proof: LeaseProof | None,
605
605
  ) -> ReleaseRuntimeAccess:
606
- """Obtain access to an application release as a requester-bound call."""
606
+ """Obtain access to an application release.
607
+
608
+ With ``proof`` the call carries the delegation of the person the turn serves, and the
609
+ token acts for that person. Without it the token is the runtime's own.
610
+ """
607
611
 
608
612
  data = await self._request(
609
613
  "POST",
610
614
  resource_release_runtime_access(release_uid),
611
- headers=proof.headers(),
615
+ headers=proof.headers() if proof is not None else None,
612
616
  )
613
617
  try:
614
618
  return ReleaseRuntimeAccess.model_validate(data)
@@ -617,12 +621,12 @@ class MainSequenceClient:
617
621
  # Raised outside the handler: a validation error repeats its input, which holds the token.
618
622
  raise BackendError("Backend release access response is invalid")
619
623
 
620
- async def requester_bound_request(
624
+ async def platform_request(
621
625
  self,
622
626
  method: str,
623
627
  path: str,
624
628
  *,
625
- proof: LeaseProof,
629
+ proof: LeaseProof | None,
626
630
  params: Any = None,
627
631
  json: Any = None,
628
632
  content: bytes | str | None = None,
@@ -630,11 +634,12 @@ class MainSequenceClient:
630
634
  files: Any = None,
631
635
  headers: Mapping[str, str] | None = None,
632
636
  ) -> httpx.Response:
633
- """Send one requester-bound call to a platform path, with the runtime's own credential.
637
+ """Send one call to a platform path, with the runtime's own credential.
634
638
 
635
- ``path`` is relative to the configured platform base URL, and the call never leaves that
636
- origin or follows a redirect. The answer is returned on a copy of the request that carries
637
- no header, so neither the credential nor the lease proof travels with it.
639
+ With ``proof`` the call also carries the delegation of the person the turn serves. ``path``
640
+ is relative to the configured platform base URL, and the call never leaves that origin or
641
+ follows a redirect. The answer is returned on a copy of the request that carries no header,
642
+ so neither the credential nor the lease proof travels with it.
638
643
  """
639
644
 
640
645
  base = httpx.URL(self.settings.backend_url)
@@ -644,7 +649,8 @@ class MainSequenceClient:
644
649
  base.host,
645
650
  base.port,
646
651
  ):
647
- raise ValueError("A requester-bound call takes a path on the platform's base URL")
652
+ raise ValueError("A platform call takes a path on the platform's base URL")
653
+ delegation = proof.headers() if proof is not None else {}
648
654
  force_auth = False
649
655
  operation = _dependency_operation(method, path)
650
656
  while True:
@@ -661,7 +667,7 @@ class MainSequenceClient:
661
667
  files=files,
662
668
  headers={
663
669
  **dict(headers or {}),
664
- **proof.headers(),
670
+ **delegation,
665
671
  **await self.auth.headers(force=force_auth),
666
672
  },
667
673
  follow_redirects=False,
@@ -671,22 +677,22 @@ class MainSequenceClient:
671
677
  if failure:
672
678
  logger.warning(
673
679
  "dependency.call.failed",
674
- message="Requester-bound backend call failed",
680
+ message="Platform call failed",
675
681
  dependency_operation=operation,
676
682
  target_system="mainsequence_backend",
677
- requester_bound=True,
683
+ requester_bound=proof is not None,
678
684
  duration_ms=round((time.monotonic() - started_at) * 1000, 3),
679
685
  error_type=failure,
680
686
  outcome="failed",
681
687
  )
682
688
  # Raised outside the handler: the error holds the request and its credentials.
683
- raise BackendError(f"Requester-bound backend call failed: {failure}")
689
+ raise BackendError(f"Platform call failed: {failure}")
684
690
  logger.info(
685
691
  "dependency.call.completed",
686
- message="Requester-bound backend call completed",
692
+ message="Platform call completed",
687
693
  dependency_operation=operation,
688
694
  target_system="mainsequence_backend",
689
- requester_bound=True,
695
+ requester_bound=proof is not None,
690
696
  status_code=response.status_code,
691
697
  duration_ms=round((time.monotonic() - started_at) * 1000, 3),
692
698
  outcome=(