ms-tau-sdk 2.0.21.dev72__tar.gz → 2.0.22.dev73__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 (87) hide show
  1. {ms_tau_sdk-2.0.21.dev72 → ms_tau_sdk-2.0.22.dev73}/CHANGELOG.md +20 -0
  2. {ms_tau_sdk-2.0.21.dev72 → ms_tau_sdk-2.0.22.dev73}/PKG-INFO +1 -1
  3. {ms_tau_sdk-2.0.21.dev72 → ms_tau_sdk-2.0.22.dev73}/pyproject.toml +1 -1
  4. {ms_tau_sdk-2.0.21.dev72 → ms_tau_sdk-2.0.22.dev73}/src/ms_tau_sdk/agent_skills/tau_a2a_runtime_adapter/SKILL.md +3 -1
  5. {ms_tau_sdk-2.0.21.dev72 → ms_tau_sdk-2.0.22.dev73}/src/ms_tau_sdk/agent_skills/tau_security_and_access/SKILL.md +2 -1
  6. {ms_tau_sdk-2.0.21.dev72 → ms_tau_sdk-2.0.22.dev73}/src/ms_tau_sdk/api/a2a.py +32 -4
  7. {ms_tau_sdk-2.0.21.dev72 → ms_tau_sdk-2.0.22.dev73}/src/ms_tau_sdk/backend/client.py +15 -6
  8. {ms_tau_sdk-2.0.21.dev72 → ms_tau_sdk-2.0.22.dev73}/src/ms_tau_sdk/runtime/manager.py +12 -6
  9. {ms_tau_sdk-2.0.21.dev72 → ms_tau_sdk-2.0.22.dev73}/src/ms_tau_sdk/runtime/task_context.py +5 -1
  10. {ms_tau_sdk-2.0.21.dev72 → ms_tau_sdk-2.0.22.dev73}/.gitignore +0 -0
  11. {ms_tau_sdk-2.0.21.dev72 → ms_tau_sdk-2.0.22.dev73}/README.md +0 -0
  12. {ms_tau_sdk-2.0.21.dev72 → ms_tau_sdk-2.0.22.dev73}/packages/tau-board/src/ms_tau_board/__init__.py +0 -0
  13. {ms_tau_sdk-2.0.21.dev72 → ms_tau_sdk-2.0.22.dev73}/packages/tau-board/src/ms_tau_board/app.py +0 -0
  14. {ms_tau_sdk-2.0.21.dev72 → ms_tau_sdk-2.0.22.dev73}/packages/tau-board/src/ms_tau_board/cli.py +0 -0
  15. {ms_tau_sdk-2.0.21.dev72 → ms_tau_sdk-2.0.22.dev73}/packages/tau-board/src/ms_tau_board/config.py +0 -0
  16. {ms_tau_sdk-2.0.21.dev72 → ms_tau_sdk-2.0.22.dev73}/packages/tau-board/src/ms_tau_board/env_file.py +0 -0
  17. {ms_tau_sdk-2.0.21.dev72 → ms_tau_sdk-2.0.22.dev73}/packages/tau-board/src/ms_tau_board/logs.py +0 -0
  18. {ms_tau_sdk-2.0.21.dev72 → ms_tau_sdk-2.0.22.dev73}/packages/tau-board/src/ms_tau_board/proxy.py +0 -0
  19. {ms_tau_sdk-2.0.21.dev72 → ms_tau_sdk-2.0.22.dev73}/packages/tau-board/src/ms_tau_board/state.py +0 -0
  20. {ms_tau_sdk-2.0.21.dev72 → ms_tau_sdk-2.0.22.dev73}/packages/tau-board/src/ms_tau_board/static/BULMA-LICENSE.txt +0 -0
  21. {ms_tau_sdk-2.0.21.dev72 → ms_tau_sdk-2.0.22.dev73}/packages/tau-board/src/ms_tau_board/static/app.js +0 -0
  22. {ms_tau_sdk-2.0.21.dev72 → ms_tau_sdk-2.0.22.dev73}/packages/tau-board/src/ms_tau_board/static/board.css +0 -0
  23. {ms_tau_sdk-2.0.21.dev72 → ms_tau_sdk-2.0.22.dev73}/packages/tau-board/src/ms_tau_board/static/bulma.min.css +0 -0
  24. {ms_tau_sdk-2.0.21.dev72 → ms_tau_sdk-2.0.22.dev73}/packages/tau-board/src/ms_tau_board/static/index.html +0 -0
  25. {ms_tau_sdk-2.0.21.dev72 → ms_tau_sdk-2.0.22.dev73}/src/ms_tau_sdk/__init__.py +0 -0
  26. {ms_tau_sdk-2.0.21.dev72 → ms_tau_sdk-2.0.22.dev73}/src/ms_tau_sdk/agent_skills/tau_local_development/SKILL.md +0 -0
  27. {ms_tau_sdk-2.0.21.dev72 → ms_tau_sdk-2.0.22.dev73}/src/ms_tau_sdk/agent_skills/tau_project_customization/SKILL.md +0 -0
  28. {ms_tau_sdk-2.0.21.dev72 → ms_tau_sdk-2.0.22.dev73}/src/ms_tau_sdk/agent_skills/tau_repository_integration/SKILL.md +0 -0
  29. {ms_tau_sdk-2.0.21.dev72 → ms_tau_sdk-2.0.22.dev73}/src/ms_tau_sdk/api/__init__.py +0 -0
  30. {ms_tau_sdk-2.0.21.dev72 → ms_tau_sdk-2.0.22.dev73}/src/ms_tau_sdk/api/chat.py +0 -0
  31. {ms_tau_sdk-2.0.21.dev72 → ms_tau_sdk-2.0.22.dev73}/src/ms_tau_sdk/api/conversations.py +0 -0
  32. {ms_tau_sdk-2.0.21.dev72 → ms_tau_sdk-2.0.22.dev73}/src/ms_tau_sdk/api/dependencies.py +0 -0
  33. {ms_tau_sdk-2.0.21.dev72 → ms_tau_sdk-2.0.22.dev73}/src/ms_tau_sdk/api/health.py +0 -0
  34. {ms_tau_sdk-2.0.21.dev72 → ms_tau_sdk-2.0.22.dev73}/src/ms_tau_sdk/api/inspection.py +0 -0
  35. {ms_tau_sdk-2.0.21.dev72 → ms_tau_sdk-2.0.22.dev73}/src/ms_tau_sdk/api/local_chat.py +0 -0
  36. {ms_tau_sdk-2.0.21.dev72 → ms_tau_sdk-2.0.22.dev73}/src/ms_tau_sdk/api/models.py +0 -0
  37. {ms_tau_sdk-2.0.21.dev72 → ms_tau_sdk-2.0.22.dev73}/src/ms_tau_sdk/api/request_identity.py +0 -0
  38. {ms_tau_sdk-2.0.21.dev72 → ms_tau_sdk-2.0.22.dev73}/src/ms_tau_sdk/api/sessions.py +0 -0
  39. {ms_tau_sdk-2.0.21.dev72 → ms_tau_sdk-2.0.22.dev73}/src/ms_tau_sdk/app.py +0 -0
  40. {ms_tau_sdk-2.0.21.dev72 → ms_tau_sdk-2.0.22.dev73}/src/ms_tau_sdk/application.py +0 -0
  41. {ms_tau_sdk-2.0.21.dev72 → ms_tau_sdk-2.0.22.dev73}/src/ms_tau_sdk/backend/__init__.py +0 -0
  42. {ms_tau_sdk-2.0.21.dev72 → ms_tau_sdk-2.0.22.dev73}/src/ms_tau_sdk/backend/assertions.py +0 -0
  43. {ms_tau_sdk-2.0.21.dev72 → ms_tau_sdk-2.0.22.dev73}/src/ms_tau_sdk/backend/auth.py +0 -0
  44. {ms_tau_sdk-2.0.21.dev72 → ms_tau_sdk-2.0.22.dev73}/src/ms_tau_sdk/backend/local.py +0 -0
  45. {ms_tau_sdk-2.0.21.dev72 → ms_tau_sdk-2.0.22.dev73}/src/ms_tau_sdk/backend/mcp.py +0 -0
  46. {ms_tau_sdk-2.0.21.dev72 → ms_tau_sdk-2.0.22.dev73}/src/ms_tau_sdk/backend/models.py +0 -0
  47. {ms_tau_sdk-2.0.21.dev72 → ms_tau_sdk-2.0.22.dev73}/src/ms_tau_sdk/backend/routes.py +0 -0
  48. {ms_tau_sdk-2.0.21.dev72 → ms_tau_sdk-2.0.22.dev73}/src/ms_tau_sdk/backend/team_memberships.py +0 -0
  49. {ms_tau_sdk-2.0.21.dev72 → ms_tau_sdk-2.0.22.dev73}/src/ms_tau_sdk/cli.py +0 -0
  50. {ms_tau_sdk-2.0.21.dev72 → ms_tau_sdk-2.0.22.dev73}/src/ms_tau_sdk/errors.py +0 -0
  51. {ms_tau_sdk-2.0.21.dev72 → ms_tau_sdk-2.0.22.dev73}/src/ms_tau_sdk/logging.py +0 -0
  52. {ms_tau_sdk-2.0.21.dev72 → ms_tau_sdk-2.0.22.dev73}/src/ms_tau_sdk/protocols/__init__.py +0 -0
  53. {ms_tau_sdk-2.0.21.dev72 → ms_tau_sdk-2.0.22.dev73}/src/ms_tau_sdk/protocols/a2a_failure.py +0 -0
  54. {ms_tau_sdk-2.0.21.dev72 → ms_tau_sdk-2.0.22.dev73}/src/ms_tau_sdk/protocols/a2a_message.py +0 -0
  55. {ms_tau_sdk-2.0.21.dev72 → ms_tau_sdk-2.0.22.dev73}/src/ms_tau_sdk/protocols/a2a_roles.py +0 -0
  56. {ms_tau_sdk-2.0.21.dev72 → ms_tau_sdk-2.0.22.dev73}/src/ms_tau_sdk/protocols/assistant_ui.py +0 -0
  57. {ms_tau_sdk-2.0.21.dev72 → ms_tau_sdk-2.0.22.dev73}/src/ms_tau_sdk/protocols/chat_history.py +0 -0
  58. {ms_tau_sdk-2.0.21.dev72 → ms_tau_sdk-2.0.22.dev73}/src/ms_tau_sdk/protocols/strict_json.py +0 -0
  59. {ms_tau_sdk-2.0.21.dev72 → ms_tau_sdk-2.0.22.dev73}/src/ms_tau_sdk/providers/__init__.py +0 -0
  60. {ms_tau_sdk-2.0.21.dev72 → ms_tau_sdk-2.0.22.dev73}/src/ms_tau_sdk/providers/definitions.py +0 -0
  61. {ms_tau_sdk-2.0.21.dev72 → ms_tau_sdk-2.0.22.dev73}/src/ms_tau_sdk/providers/factory.py +0 -0
  62. {ms_tau_sdk-2.0.21.dev72 → ms_tau_sdk-2.0.22.dev73}/src/ms_tau_sdk/providers/tau_compat.py +0 -0
  63. {ms_tau_sdk-2.0.21.dev72 → ms_tau_sdk-2.0.22.dev73}/src/ms_tau_sdk/resources/SYSTEM.md +0 -0
  64. {ms_tau_sdk-2.0.21.dev72 → ms_tau_sdk-2.0.22.dev73}/src/ms_tau_sdk/resources/__init__.py +0 -0
  65. {ms_tau_sdk-2.0.21.dev72 → ms_tau_sdk-2.0.22.dev73}/src/ms_tau_sdk/resources/loader.py +0 -0
  66. {ms_tau_sdk-2.0.21.dev72 → ms_tau_sdk-2.0.22.dev73}/src/ms_tau_sdk/resources/prompts/review-code-repository.md +0 -0
  67. {ms_tau_sdk-2.0.21.dev72 → ms_tau_sdk-2.0.22.dev73}/src/ms_tau_sdk/runtime/__init__.py +0 -0
  68. {ms_tau_sdk-2.0.21.dev72 → ms_tau_sdk-2.0.22.dev73}/src/ms_tau_sdk/runtime/deployment_health.py +0 -0
  69. {ms_tau_sdk-2.0.21.dev72 → ms_tau_sdk-2.0.22.dev73}/src/ms_tau_sdk/runtime/events.py +0 -0
  70. {ms_tau_sdk-2.0.21.dev72 → ms_tau_sdk-2.0.22.dev73}/src/ms_tau_sdk/runtime/extensions.py +0 -0
  71. {ms_tau_sdk-2.0.21.dev72 → ms_tau_sdk-2.0.22.dev73}/src/ms_tau_sdk/runtime/failures.py +0 -0
  72. {ms_tau_sdk-2.0.21.dev72 → ms_tau_sdk-2.0.22.dev73}/src/ms_tau_sdk/runtime/live_turns.py +0 -0
  73. {ms_tau_sdk-2.0.21.dev72 → ms_tau_sdk-2.0.22.dev73}/src/ms_tau_sdk/runtime/observability.py +0 -0
  74. {ms_tau_sdk-2.0.21.dev72 → ms_tau_sdk-2.0.22.dev73}/src/ms_tau_sdk/runtime/provenance.py +0 -0
  75. {ms_tau_sdk-2.0.21.dev72 → ms_tau_sdk-2.0.22.dev73}/src/ms_tau_sdk/runtime/requester.py +0 -0
  76. {ms_tau_sdk-2.0.21.dev72 → ms_tau_sdk-2.0.22.dev73}/src/ms_tau_sdk/runtime/session.py +0 -0
  77. {ms_tau_sdk-2.0.21.dev72 → ms_tau_sdk-2.0.22.dev73}/src/ms_tau_sdk/runtime/snapshots.py +0 -0
  78. {ms_tau_sdk-2.0.21.dev72 → ms_tau_sdk-2.0.22.dev73}/src/ms_tau_sdk/sessions/__init__.py +0 -0
  79. {ms_tau_sdk-2.0.21.dev72 → ms_tau_sdk-2.0.22.dev73}/src/ms_tau_sdk/sessions/storage.py +0 -0
  80. {ms_tau_sdk-2.0.21.dev72 → ms_tau_sdk-2.0.22.dev73}/src/ms_tau_sdk/settings.py +0 -0
  81. {ms_tau_sdk-2.0.21.dev72 → ms_tau_sdk-2.0.22.dev73}/src/ms_tau_sdk/skills.py +0 -0
  82. {ms_tau_sdk-2.0.21.dev72 → ms_tau_sdk-2.0.22.dev73}/src/ms_tau_sdk/tools/__init__.py +0 -0
  83. {ms_tau_sdk-2.0.21.dev72 → ms_tau_sdk-2.0.22.dev73}/src/ms_tau_sdk/tools/mainsequence_mcp.py +0 -0
  84. {ms_tau_sdk-2.0.21.dev72 → ms_tau_sdk-2.0.22.dev73}/src/ms_tau_sdk/tools/mcp_applications.py +0 -0
  85. {ms_tau_sdk-2.0.21.dev72 → ms_tau_sdk-2.0.22.dev73}/src/ms_tau_sdk/tools/mcp_connection.py +0 -0
  86. {ms_tau_sdk-2.0.21.dev72 → ms_tau_sdk-2.0.22.dev73}/src/ms_tau_sdk/tools/skill_read.py +0 -0
  87. {ms_tau_sdk-2.0.21.dev72 → ms_tau_sdk-2.0.22.dev73}/src/ms_tau_sdk/tools/task_control.py +0 -0
@@ -1,5 +1,25 @@
1
1
  # Changelog
2
2
 
3
+ ## 2.0.22 — 2026-10-21
4
+
5
+ - **A hosted Task's answer reaches the caller of GetTask
6
+ ([#113](https://github.com/mainsequence-sdk/ms-tau-sdk/issues/113)).** GetTask answered a
7
+ completed hosted Task with no Artifacts, although the platform had stored its answer. The SDK
8
+ found the Task in the platform's Task list and answered with that list's entry, which carries
9
+ only the counts of a Task's outputs. It now reads the Task in full after the list finds it, so
10
+ GetTask, JSON-RPC `GetTask` and the stream of an existing Task answer with its Artifacts, an
11
+ error answer included. A2A ListTasks still answers each Task without its Artifacts.
12
+
13
+ - **A Task acts for the person who asked for it
14
+ ([#114](https://github.com/mainsequence-sdk/ms-tau-sdk/issues/114), ADR 0019, ADR 0021).** A
15
+ Task attempt took the person it serves from the platform's answer to its turn start, which names
16
+ nobody for a Task, so a Task's application calls went out as the Agent alone and the application
17
+ refused them. A Task attempt now serves the person the platform names for running the Task: in
18
+ its answer when the request creates or continues the Task, or in the dispatch when the platform
19
+ asks the runtime to run it. The platform names only an active person whom the executing Agent may
20
+ act for, and checks them again on every call made for them. Chat, A2A Message and
21
+ caller-delivery turns still take the person from the turn start.
22
+
3
23
  ## 2.0.21 — 2026-10-21
4
24
 
5
25
  - **Hosted Tasks can be sent, read and listed with their Messages
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: ms-tau-sdk
3
- Version: 2.0.21.dev72
3
+ Version: 2.0.22.dev73
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
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "ms-tau-sdk"
7
- version = "2.0.21.dev72"
7
+ version = "2.0.22.dev73"
8
8
  description = "Workspace-bound Tau application primitives for Main Sequence projects"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.13"
@@ -32,7 +32,9 @@ The SDK owns:
32
32
  which the platform's own records establish, never the `X-Caller-*` headers;
33
33
  - presenting the verified caller assertion of the request that starts a chat or A2A Message turn
34
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
35
+ taking the person a chat, A2A Message or caller-delivery turn serves from the platform's answer
36
+ to the turn start, and the person a Task attempt serves from the platform's answer to the Task's
37
+ creation or continuation or from its dispatch; and giving
36
38
  extension tools that person and calls for the work through `current_requester()` and
37
39
  `platform_client()` without exposing the assertion, the lease proof, or any token;
38
40
  - translating inbound A2A requests into the shared TAU runtime; and
@@ -21,7 +21,8 @@ Every call a tool makes through `platform_client()` follows one rule:
21
21
  | A valid delegation | The person the turn serves, with their ordinary permissions, including administrative ones. |
22
22
  | A delegation that ended or is invalid | None. The call is refused with a `PermissionError` and is never retried as the Agent. |
23
23
 
24
- - The platform names the person when the turn starts, and `current_requester()` returns them. It
24
+ - The platform names the person when a chat or Message turn starts, and for a Task when it is
25
+ created, continued or dispatched; `current_requester()` returns them. It
25
26
  returns `None` when the Agent is not enabled to act for people (only an Organization admin can
26
27
  enable it, with `acts_for_requester`), for work no person asked for, in local mode and outside a
27
28
  turn.
@@ -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
66
+ from ms_tau_sdk.runtime.requester import CallerDelivery, canonical_requester_uid
67
67
  from ms_tau_sdk.runtime.task_context import (
68
68
  TaskExecutionContext,
69
69
  active_task_execution,
@@ -1072,13 +1072,26 @@ def _presented_assertion(caller_assertion: CallerAssertion | None) -> dict[str,
1072
1072
  def _request_caller(caller_assertion: CallerAssertion | None) -> VerifiedCaller | None:
1073
1073
  """Return the verified caller of a request that runs a Task attempt itself.
1074
1074
 
1075
- The platform's answer to the attempt's turn start names the person the attempt serves; this
1076
- caller only supplies that person's teams, when it is that person.
1075
+ The platform names the person the attempt serves; this caller only supplies that person's
1076
+ teams, when it is that person.
1077
1077
  """
1078
1078
 
1079
1079
  return caller_assertion.caller if caller_assertion is not None else None
1080
1080
 
1081
1081
 
1082
+ def _task_requester_uid(requester_user_uid: object, requester_identity_type: object) -> str | None:
1083
+ """Return the person a Task attempt serves, as the platform named them for running the Task.
1084
+
1085
+ The platform names a requester for running a Task in its answer to the runtime's own creation
1086
+ or continuation of the Task and in the dispatch: an active person, only while the executing
1087
+ Agent may act for them, and nobody otherwise.
1088
+ """
1089
+
1090
+ if requester_identity_type != "human":
1091
+ return None
1092
+ return canonical_requester_uid(requester_user_uid)
1093
+
1094
+
1082
1095
  async def _wait_for_task_return_state(
1083
1096
  client: MainSequenceClient,
1084
1097
  task: AgentTask,
@@ -1441,6 +1454,7 @@ async def _execute_task(
1441
1454
  dispatch_uid: str | None = None,
1442
1455
  claim: _ClaimedTask | None = None,
1443
1456
  caller: VerifiedCaller | None = None,
1457
+ requester_uid: str | None = None,
1444
1458
  ) -> dict[str, Any]:
1445
1459
  bind_contextvars(
1446
1460
  a2a_task_id=task.task_id,
@@ -1474,6 +1488,7 @@ async def _execute_task(
1474
1488
  holder_id=claim.holder_id,
1475
1489
  lease_token=claim.lease_token,
1476
1490
  caller=caller,
1491
+ requester_uid=requester_uid,
1477
1492
  )
1478
1493
  try:
1479
1494
  await writer.ensure_not_canceled(force=True)
@@ -1561,7 +1576,11 @@ async def _schedule_task_accelerator(
1561
1576
  config: TauSDKSettings,
1562
1577
  caller: VerifiedCaller | None = None,
1563
1578
  ) -> bool:
1564
- """Best-effort local start after durable creation; dispatch remains recovery owner."""
1579
+ """Best-effort local start after durable creation; dispatch remains recovery owner.
1580
+
1581
+ ``task`` is the platform's answer to the runtime's own creation or continuation of the Task,
1582
+ which names the person the attempt serves.
1583
+ """
1565
1584
 
1566
1585
  try:
1567
1586
  claim = await _claim_backend_task(client, manager, task)
@@ -1592,6 +1611,7 @@ async def _schedule_task_accelerator(
1592
1611
  config=config,
1593
1612
  claim=claim,
1594
1613
  caller=caller,
1614
+ requester_uid=_task_requester_uid(task.requester_user_uid, task.requester_identity_type),
1595
1615
  )
1596
1616
  try:
1597
1617
  _background, scheduled = manager.create_a2a_task_execution(
@@ -1854,6 +1874,8 @@ async def _stream_task_events(
1854
1874
  holder_id=claim.holder_id,
1855
1875
  lease_token=claim.lease_token,
1856
1876
  caller=caller,
1877
+ # ``task`` is the platform's answer to the runtime's own creation of the Task.
1878
+ requester_uid=_task_requester_uid(task.requester_user_uid, task.requester_identity_type),
1857
1879
  )
1858
1880
  working = await client.get_task(task.uid)
1859
1881
  working = await _task_with_history(client, working, history_length=history_length)
@@ -2337,6 +2359,12 @@ async def task_dispatch_available(
2337
2359
  provenance=provenance,
2338
2360
  config=config,
2339
2361
  claim=claim,
2362
+ # The dispatch names the person the attempt serves. The Task read above records who
2363
+ # asked, but does not say whether the Agent may act for them.
2364
+ requester_uid=_task_requester_uid(
2365
+ body.get("requester_user_uid"),
2366
+ body.get("requester_identity_type"),
2367
+ ),
2340
2368
  ),
2341
2369
  name=f"a2a-task-{task.task_id}",
2342
2370
  )
@@ -1009,16 +1009,26 @@ class MainSequenceClient:
1009
1009
  for task in TypeAdapter(list[AgentTask]).validate_python(values or [])
1010
1010
  ]
1011
1011
 
1012
+ async def _task_uid_by_protocol_id(self, task_id: str) -> str:
1013
+ tasks = await self.list_tasks(task_id=task_id)
1014
+ if not tasks:
1015
+ raise SessionNotFoundError(f"A2A task not found: {task_id}")
1016
+ return tasks[0].uid
1017
+
1012
1018
  async def get_task_by_protocol_id(
1013
1019
  self,
1014
1020
  task_id: str,
1015
1021
  *,
1016
1022
  history_length: int | None = None,
1017
1023
  ) -> AgentTask:
1018
- tasks = await self.list_tasks(task_id=task_id, history_length=history_length)
1019
- if not tasks:
1020
- raise SessionNotFoundError(f"A2A task not found: {task_id}")
1021
- return tasks[0]
1024
+ """Return the complete Task that the protocol ID names.
1025
+
1026
+ The platform's Task list finds the Task but answers only the counts of its outputs, so
1027
+ the Task is then read in full.
1028
+ """
1029
+
1030
+ task_uid = await self._task_uid_by_protocol_id(task_id)
1031
+ return await self.get_task(task_uid, history_length=history_length)
1022
1032
 
1023
1033
  async def get_task(
1024
1034
  self,
@@ -1067,8 +1077,7 @@ class MainSequenceClient:
1067
1077
  return snapshot.model_copy(update={"task": _with_binding_messages(snapshot.task)})
1068
1078
 
1069
1079
  async def get_task_snapshot_by_protocol_id(self, task_id: str) -> AgentTaskSnapshot:
1070
- task = await self.get_task_by_protocol_id(task_id)
1071
- return await self.get_task_snapshot(task.uid)
1080
+ return await self.get_task_snapshot(await self._task_uid_by_protocol_id(task_id))
1072
1081
 
1073
1082
  async def list_task_events(
1074
1083
  self,
@@ -83,7 +83,7 @@ from ms_tau_sdk.runtime.snapshots import (
83
83
  restore_snapshot,
84
84
  sha256_json,
85
85
  )
86
- from ms_tau_sdk.runtime.task_context import active_task_execution
86
+ from ms_tau_sdk.runtime.task_context import TaskExecutionContext, active_task_execution
87
87
  from ms_tau_sdk.sessions.storage import SESSION_ENTRY_ADAPTER, BackendSessionStorage
88
88
  from ms_tau_sdk.settings import TauSDKSettings
89
89
  from ms_tau_sdk.tools.mainsequence_mcp import (
@@ -1573,7 +1573,7 @@ class SessionRuntimeManager:
1573
1573
  )
1574
1574
  requester_binding = TurnRequesterBinding(
1575
1575
  session_uid=session_uid,
1576
- requester=self._turn_requester(runtime),
1576
+ requester=self._turn_requester(runtime, task_context),
1577
1577
  lease_proof=lambda: LeaseProof(
1578
1578
  session_uid=session_uid,
1579
1579
  holder_id=self.holder_id,
@@ -1756,16 +1756,22 @@ class SessionRuntimeManager:
1756
1756
 
1757
1757
  return not self.settings.local_mode and self.settings.request_identity_mode == "assertion"
1758
1758
 
1759
- def _turn_requester(self, runtime: ActiveSessionRuntime) -> Requester | None:
1759
+ def _turn_requester(
1760
+ self,
1761
+ runtime: ActiveSessionRuntime,
1762
+ task_context: TaskExecutionContext | None,
1763
+ ) -> Requester | None:
1760
1764
  """Name the person a turn serves, or None.
1761
1765
 
1762
- The platform's answer to the turn start names that person, whoever called: a chat or A2A
1763
- Message turn, a Task attempt, and a turn the platform starts for a caller delivery alike.
1766
+ A Task attempt serves the person the platform named for running the Task, which its
1767
+ execution context holds; the platform's answer to a Task attempt's turn start names
1768
+ nobody. Any other turn, a chat or A2A Message turn and a turn the platform starts for a
1769
+ caller delivery, serves the person the platform's answer to the turn start names.
1764
1770
  """
1765
1771
 
1766
1772
  if not self._requester_identity_verified():
1767
1773
  return None
1768
- uid = runtime.requester_user_uid
1774
+ uid = task_context.requester_uid if task_context is not None else runtime.requester_user_uid
1769
1775
  if uid is None:
1770
1776
  return None
1771
1777
  return Requester(uid=uid)
@@ -27,8 +27,12 @@ class TaskExecutionContext:
27
27
  interruption_message: dict[str, Any] | None = None
28
28
  # The verified caller of the request that runs this attempt itself; None for an attempt the
29
29
  # platform dispatches. It supplies the teams of the person the attempt serves when that
30
- # person is the caller; the platform's answer to the turn start names the person.
30
+ # person is the caller.
31
31
  caller: VerifiedCaller | None = None
32
+ # The person the attempt serves, as the platform named them for running the Task: in its
33
+ # answer to the runtime's own creation or continuation of the Task, or in the dispatch. None
34
+ # when it named nobody. The platform checks the person again on every call made for them.
35
+ requester_uid: str | None = None
32
36
 
33
37
  def request_interruption(
34
38
  self,