ms-tau-sdk 2.0.26.dev77__tar.gz → 2.0.26.dev78__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 (92) hide show
  1. {ms_tau_sdk-2.0.26.dev77 → ms_tau_sdk-2.0.26.dev78}/CHANGELOG.md +21 -0
  2. {ms_tau_sdk-2.0.26.dev77 → ms_tau_sdk-2.0.26.dev78}/PKG-INFO +12 -2
  3. {ms_tau_sdk-2.0.26.dev77 → ms_tau_sdk-2.0.26.dev78}/README.md +11 -1
  4. {ms_tau_sdk-2.0.26.dev77 → ms_tau_sdk-2.0.26.dev78}/pyproject.toml +1 -1
  5. {ms_tau_sdk-2.0.26.dev77 → ms_tau_sdk-2.0.26.dev78}/src/ms_tau_sdk/agent_skills/tau_project_customization/SKILL.md +31 -14
  6. {ms_tau_sdk-2.0.26.dev77 → ms_tau_sdk-2.0.26.dev78}/src/ms_tau_sdk/agent_skills/tau_repository_integration/SKILL.md +2 -0
  7. ms_tau_sdk-2.0.26.dev78/src/ms_tau_sdk/resources/context_compat.py +107 -0
  8. ms_tau_sdk-2.0.26.dev78/src/ms_tau_sdk/resources/loader.py +120 -0
  9. {ms_tau_sdk-2.0.26.dev77 → ms_tau_sdk-2.0.26.dev78}/src/ms_tau_sdk/runtime/extensions.py +80 -0
  10. {ms_tau_sdk-2.0.26.dev77 → ms_tau_sdk-2.0.26.dev78}/src/ms_tau_sdk/runtime/manager.py +17 -0
  11. {ms_tau_sdk-2.0.26.dev77 → ms_tau_sdk-2.0.26.dev78}/src/ms_tau_sdk/runtime/session.py +9 -1
  12. {ms_tau_sdk-2.0.26.dev77 → ms_tau_sdk-2.0.26.dev78}/src/ms_tau_sdk/settings.py +6 -0
  13. ms_tau_sdk-2.0.26.dev77/src/ms_tau_sdk/resources/loader.py +0 -76
  14. {ms_tau_sdk-2.0.26.dev77 → ms_tau_sdk-2.0.26.dev78}/.gitignore +0 -0
  15. {ms_tau_sdk-2.0.26.dev77 → ms_tau_sdk-2.0.26.dev78}/packages/tau-board/src/ms_tau_board/__init__.py +0 -0
  16. {ms_tau_sdk-2.0.26.dev77 → ms_tau_sdk-2.0.26.dev78}/packages/tau-board/src/ms_tau_board/app.py +0 -0
  17. {ms_tau_sdk-2.0.26.dev77 → ms_tau_sdk-2.0.26.dev78}/packages/tau-board/src/ms_tau_board/cli.py +0 -0
  18. {ms_tau_sdk-2.0.26.dev77 → ms_tau_sdk-2.0.26.dev78}/packages/tau-board/src/ms_tau_board/config.py +0 -0
  19. {ms_tau_sdk-2.0.26.dev77 → ms_tau_sdk-2.0.26.dev78}/packages/tau-board/src/ms_tau_board/env_file.py +0 -0
  20. {ms_tau_sdk-2.0.26.dev77 → ms_tau_sdk-2.0.26.dev78}/packages/tau-board/src/ms_tau_board/logs.py +0 -0
  21. {ms_tau_sdk-2.0.26.dev77 → ms_tau_sdk-2.0.26.dev78}/packages/tau-board/src/ms_tau_board/proxy.py +0 -0
  22. {ms_tau_sdk-2.0.26.dev77 → ms_tau_sdk-2.0.26.dev78}/packages/tau-board/src/ms_tau_board/state.py +0 -0
  23. {ms_tau_sdk-2.0.26.dev77 → ms_tau_sdk-2.0.26.dev78}/packages/tau-board/src/ms_tau_board/static/BULMA-LICENSE.txt +0 -0
  24. {ms_tau_sdk-2.0.26.dev77 → ms_tau_sdk-2.0.26.dev78}/packages/tau-board/src/ms_tau_board/static/app.js +0 -0
  25. {ms_tau_sdk-2.0.26.dev77 → ms_tau_sdk-2.0.26.dev78}/packages/tau-board/src/ms_tau_board/static/board.css +0 -0
  26. {ms_tau_sdk-2.0.26.dev77 → ms_tau_sdk-2.0.26.dev78}/packages/tau-board/src/ms_tau_board/static/bulma.min.css +0 -0
  27. {ms_tau_sdk-2.0.26.dev77 → ms_tau_sdk-2.0.26.dev78}/packages/tau-board/src/ms_tau_board/static/index.html +0 -0
  28. {ms_tau_sdk-2.0.26.dev77 → ms_tau_sdk-2.0.26.dev78}/src/ms_tau_sdk/__init__.py +0 -0
  29. {ms_tau_sdk-2.0.26.dev77 → ms_tau_sdk-2.0.26.dev78}/src/ms_tau_sdk/agent_skills/design-agent-icons-and-motion/SKILL.md +0 -0
  30. {ms_tau_sdk-2.0.26.dev77 → ms_tau_sdk-2.0.26.dev78}/src/ms_tau_sdk/agent_skills/design-agent-icons-and-motion/agents/openai.yaml +0 -0
  31. {ms_tau_sdk-2.0.26.dev77 → ms_tau_sdk-2.0.26.dev78}/src/ms_tau_sdk/agent_skills/design-agent-icons-and-motion/references/metatables-example.html +0 -0
  32. {ms_tau_sdk-2.0.26.dev77 → ms_tau_sdk-2.0.26.dev78}/src/ms_tau_sdk/agent_skills/tau_a2a_runtime_adapter/SKILL.md +0 -0
  33. {ms_tau_sdk-2.0.26.dev77 → ms_tau_sdk-2.0.26.dev78}/src/ms_tau_sdk/agent_skills/tau_local_development/SKILL.md +0 -0
  34. {ms_tau_sdk-2.0.26.dev77 → ms_tau_sdk-2.0.26.dev78}/src/ms_tau_sdk/agent_skills/tau_security_and_access/SKILL.md +0 -0
  35. {ms_tau_sdk-2.0.26.dev77 → ms_tau_sdk-2.0.26.dev78}/src/ms_tau_sdk/api/__init__.py +0 -0
  36. {ms_tau_sdk-2.0.26.dev77 → ms_tau_sdk-2.0.26.dev78}/src/ms_tau_sdk/api/a2a.py +0 -0
  37. {ms_tau_sdk-2.0.26.dev77 → ms_tau_sdk-2.0.26.dev78}/src/ms_tau_sdk/api/chat.py +0 -0
  38. {ms_tau_sdk-2.0.26.dev77 → ms_tau_sdk-2.0.26.dev78}/src/ms_tau_sdk/api/conversations.py +0 -0
  39. {ms_tau_sdk-2.0.26.dev77 → ms_tau_sdk-2.0.26.dev78}/src/ms_tau_sdk/api/dependencies.py +0 -0
  40. {ms_tau_sdk-2.0.26.dev77 → ms_tau_sdk-2.0.26.dev78}/src/ms_tau_sdk/api/health.py +0 -0
  41. {ms_tau_sdk-2.0.26.dev77 → ms_tau_sdk-2.0.26.dev78}/src/ms_tau_sdk/api/inspection.py +0 -0
  42. {ms_tau_sdk-2.0.26.dev77 → ms_tau_sdk-2.0.26.dev78}/src/ms_tau_sdk/api/local_chat.py +0 -0
  43. {ms_tau_sdk-2.0.26.dev77 → ms_tau_sdk-2.0.26.dev78}/src/ms_tau_sdk/api/models.py +0 -0
  44. {ms_tau_sdk-2.0.26.dev77 → ms_tau_sdk-2.0.26.dev78}/src/ms_tau_sdk/api/request_identity.py +0 -0
  45. {ms_tau_sdk-2.0.26.dev77 → ms_tau_sdk-2.0.26.dev78}/src/ms_tau_sdk/api/sessions.py +0 -0
  46. {ms_tau_sdk-2.0.26.dev77 → ms_tau_sdk-2.0.26.dev78}/src/ms_tau_sdk/app.py +0 -0
  47. {ms_tau_sdk-2.0.26.dev77 → ms_tau_sdk-2.0.26.dev78}/src/ms_tau_sdk/application.py +0 -0
  48. {ms_tau_sdk-2.0.26.dev77 → ms_tau_sdk-2.0.26.dev78}/src/ms_tau_sdk/backend/__init__.py +0 -0
  49. {ms_tau_sdk-2.0.26.dev77 → ms_tau_sdk-2.0.26.dev78}/src/ms_tau_sdk/backend/assertions.py +0 -0
  50. {ms_tau_sdk-2.0.26.dev77 → ms_tau_sdk-2.0.26.dev78}/src/ms_tau_sdk/backend/auth.py +0 -0
  51. {ms_tau_sdk-2.0.26.dev77 → ms_tau_sdk-2.0.26.dev78}/src/ms_tau_sdk/backend/client.py +0 -0
  52. {ms_tau_sdk-2.0.26.dev77 → ms_tau_sdk-2.0.26.dev78}/src/ms_tau_sdk/backend/local.py +0 -0
  53. {ms_tau_sdk-2.0.26.dev77 → ms_tau_sdk-2.0.26.dev78}/src/ms_tau_sdk/backend/mcp.py +0 -0
  54. {ms_tau_sdk-2.0.26.dev77 → ms_tau_sdk-2.0.26.dev78}/src/ms_tau_sdk/backend/models.py +0 -0
  55. {ms_tau_sdk-2.0.26.dev77 → ms_tau_sdk-2.0.26.dev78}/src/ms_tau_sdk/backend/routes.py +0 -0
  56. {ms_tau_sdk-2.0.26.dev77 → ms_tau_sdk-2.0.26.dev78}/src/ms_tau_sdk/backend/team_memberships.py +0 -0
  57. {ms_tau_sdk-2.0.26.dev77 → ms_tau_sdk-2.0.26.dev78}/src/ms_tau_sdk/cli.py +0 -0
  58. {ms_tau_sdk-2.0.26.dev77 → ms_tau_sdk-2.0.26.dev78}/src/ms_tau_sdk/errors.py +0 -0
  59. {ms_tau_sdk-2.0.26.dev77 → ms_tau_sdk-2.0.26.dev78}/src/ms_tau_sdk/logging.py +0 -0
  60. {ms_tau_sdk-2.0.26.dev77 → ms_tau_sdk-2.0.26.dev78}/src/ms_tau_sdk/protocols/__init__.py +0 -0
  61. {ms_tau_sdk-2.0.26.dev77 → ms_tau_sdk-2.0.26.dev78}/src/ms_tau_sdk/protocols/a2a_failure.py +0 -0
  62. {ms_tau_sdk-2.0.26.dev77 → ms_tau_sdk-2.0.26.dev78}/src/ms_tau_sdk/protocols/a2a_message.py +0 -0
  63. {ms_tau_sdk-2.0.26.dev77 → ms_tau_sdk-2.0.26.dev78}/src/ms_tau_sdk/protocols/a2a_roles.py +0 -0
  64. {ms_tau_sdk-2.0.26.dev77 → ms_tau_sdk-2.0.26.dev78}/src/ms_tau_sdk/protocols/assistant_ui.py +0 -0
  65. {ms_tau_sdk-2.0.26.dev77 → ms_tau_sdk-2.0.26.dev78}/src/ms_tau_sdk/protocols/chat_history.py +0 -0
  66. {ms_tau_sdk-2.0.26.dev77 → ms_tau_sdk-2.0.26.dev78}/src/ms_tau_sdk/protocols/strict_json.py +0 -0
  67. {ms_tau_sdk-2.0.26.dev77 → ms_tau_sdk-2.0.26.dev78}/src/ms_tau_sdk/providers/__init__.py +0 -0
  68. {ms_tau_sdk-2.0.26.dev77 → ms_tau_sdk-2.0.26.dev78}/src/ms_tau_sdk/providers/definitions.py +0 -0
  69. {ms_tau_sdk-2.0.26.dev77 → ms_tau_sdk-2.0.26.dev78}/src/ms_tau_sdk/providers/factory.py +0 -0
  70. {ms_tau_sdk-2.0.26.dev77 → ms_tau_sdk-2.0.26.dev78}/src/ms_tau_sdk/providers/tau_compat.py +0 -0
  71. {ms_tau_sdk-2.0.26.dev77 → ms_tau_sdk-2.0.26.dev78}/src/ms_tau_sdk/resources/SYSTEM.md +0 -0
  72. {ms_tau_sdk-2.0.26.dev77 → ms_tau_sdk-2.0.26.dev78}/src/ms_tau_sdk/resources/__init__.py +0 -0
  73. {ms_tau_sdk-2.0.26.dev77 → ms_tau_sdk-2.0.26.dev78}/src/ms_tau_sdk/resources/prompts/review-code-repository.md +0 -0
  74. {ms_tau_sdk-2.0.26.dev77 → ms_tau_sdk-2.0.26.dev78}/src/ms_tau_sdk/runtime/__init__.py +0 -0
  75. {ms_tau_sdk-2.0.26.dev77 → ms_tau_sdk-2.0.26.dev78}/src/ms_tau_sdk/runtime/deployment_health.py +0 -0
  76. {ms_tau_sdk-2.0.26.dev77 → ms_tau_sdk-2.0.26.dev78}/src/ms_tau_sdk/runtime/events.py +0 -0
  77. {ms_tau_sdk-2.0.26.dev77 → ms_tau_sdk-2.0.26.dev78}/src/ms_tau_sdk/runtime/failures.py +0 -0
  78. {ms_tau_sdk-2.0.26.dev77 → ms_tau_sdk-2.0.26.dev78}/src/ms_tau_sdk/runtime/live_turns.py +0 -0
  79. {ms_tau_sdk-2.0.26.dev77 → ms_tau_sdk-2.0.26.dev78}/src/ms_tau_sdk/runtime/observability.py +0 -0
  80. {ms_tau_sdk-2.0.26.dev77 → ms_tau_sdk-2.0.26.dev78}/src/ms_tau_sdk/runtime/provenance.py +0 -0
  81. {ms_tau_sdk-2.0.26.dev77 → ms_tau_sdk-2.0.26.dev78}/src/ms_tau_sdk/runtime/requester.py +0 -0
  82. {ms_tau_sdk-2.0.26.dev77 → ms_tau_sdk-2.0.26.dev78}/src/ms_tau_sdk/runtime/snapshots.py +0 -0
  83. {ms_tau_sdk-2.0.26.dev77 → ms_tau_sdk-2.0.26.dev78}/src/ms_tau_sdk/runtime/task_context.py +0 -0
  84. {ms_tau_sdk-2.0.26.dev77 → ms_tau_sdk-2.0.26.dev78}/src/ms_tau_sdk/sessions/__init__.py +0 -0
  85. {ms_tau_sdk-2.0.26.dev77 → ms_tau_sdk-2.0.26.dev78}/src/ms_tau_sdk/sessions/storage.py +0 -0
  86. {ms_tau_sdk-2.0.26.dev77 → ms_tau_sdk-2.0.26.dev78}/src/ms_tau_sdk/skills.py +0 -0
  87. {ms_tau_sdk-2.0.26.dev77 → ms_tau_sdk-2.0.26.dev78}/src/ms_tau_sdk/tools/__init__.py +0 -0
  88. {ms_tau_sdk-2.0.26.dev77 → ms_tau_sdk-2.0.26.dev78}/src/ms_tau_sdk/tools/mainsequence_mcp.py +0 -0
  89. {ms_tau_sdk-2.0.26.dev77 → ms_tau_sdk-2.0.26.dev78}/src/ms_tau_sdk/tools/mcp_applications.py +0 -0
  90. {ms_tau_sdk-2.0.26.dev77 → ms_tau_sdk-2.0.26.dev78}/src/ms_tau_sdk/tools/mcp_connection.py +0 -0
  91. {ms_tau_sdk-2.0.26.dev77 → ms_tau_sdk-2.0.26.dev78}/src/ms_tau_sdk/tools/skill_read.py +0 -0
  92. {ms_tau_sdk-2.0.26.dev77 → ms_tau_sdk-2.0.26.dev78}/src/ms_tau_sdk/tools/task_control.py +0 -0
@@ -2,6 +2,27 @@
2
2
 
3
3
  ## Unreleased
4
4
 
5
+ - **The deployed Agent reads its resources only from `.tau/` by default (ADR 0025).** Tau, a
6
+ coding-agent harness, also read the repository's development instructions as the Agent's: the
7
+ root `AGENTS.md` and every `AGENTS.md` above the workspace were injected as project
8
+ instructions, and since 2.0.15 every skill under `.agents/skills`, among them the `ms_tau_sdk`,
9
+ `mainsequence` and repository development bundles, was listed as one of the Agent's skills,
10
+ expandable from chat with `/skill:<name>` and served by the skill-scoped `read`. The runtime now
11
+ reads skills from `.tau/skills`, prompt templates from `.tau/prompts` and project instructions
12
+ from `.tau/AGENTS.md`; `.agents/skills`, `.agents/prompts` and every `AGENTS.md` outside `.tau`
13
+ are not read. The new `TAU_INCLUDE_DEVELOPMENT_RESOURCES` setting (default `false`) restores
14
+ Tau's whole-repository reading for an Agent that is a coding agent over its own repository; like
15
+ the two exclusion settings it is process-wide and applies to managed and local sessions. The
16
+ interception of Tau's `AGENTS.md` discovery is locked to `tau-ai==0.4.2`, like the provider
17
+ patch of ADR 0016, and another version fails session construction. The selected system prompt
18
+ files, context files and skills, and Tau's resource diagnostics, such as a skill overriding
19
+ another or an `AGENTS.md` left out, are logged as `runtime.project_resources.*` and shown in
20
+ agent inspection under `resources`. **Migration:** a repository whose Agent used skills under
21
+ `.agents/skills` at runtime moves them directly into `.tau/skills` and, when it publishes them
22
+ with the mainsequence SDK's `skills_extension`, points the publisher at `.tau/skills`; the
23
+ publisher's folder rules then apply to the whole folder, and a skill folder cannot share its
24
+ name with a group folder. Instructions for the Agent that lived in the root `AGENTS.md` move to
25
+ `.tau/AGENTS.md`. `ms-tau skills sync` and the development workflow are unchanged.
5
26
  - Move the advisory `design-agent-icons-and-motion` skill and its worked MetaTables visual
6
27
  example into the SDK-owned skill bundle. `ms-tau skills sync` installs both under
7
28
  `.agents/skills/ms_tau_sdk/`; project artwork remains in its owning repository. No runtime
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: ms-tau-sdk
3
- Version: 2.0.26.dev77
3
+ Version: 2.0.26.dev78
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
@@ -119,7 +119,9 @@ uv run ms-tau skills sync --path .
119
119
  ```
120
120
 
121
121
  This explicit command owns only `.agents/skills/ms_tau_sdk/`; package installation and runtime
122
- startup never modify the repository automatically. The bundle includes
122
+ startup never modify the repository automatically. These skills guide the coding agent that works
123
+ on the repository; the deployed Agent reads its own resources from `.tau/` only and never sees
124
+ them. The bundle includes
123
125
  [`design-agent-icons-and-motion`](src/ms_tau_sdk/agent_skills/design-agent-icons-and-motion/SKILL.md):
124
126
  advisory composition and animation guidance with a self-contained MetaTables visual example.
125
127
  Each agent's artwork and build files remain in its owning project.
@@ -268,12 +270,20 @@ defaults and install project-specific tools through the normal `.tau/` structure
268
270
  your-project/
269
271
  ├── .tau/
270
272
  │ ├── SYSTEM.md
273
+ │ ├── AGENTS.md
271
274
  │ ├── settings.json
275
+ │ ├── skills/
272
276
  │ └── extensions/
273
277
  ├── pyproject.toml
274
278
  └── uv.lock
275
279
  ```
276
280
 
281
+ `.tau/` is the only directory the deployed Agent reads by default. The repository root's
282
+ `AGENTS.md` and the `.agents/` skills are for the coding agents that develop the repository and do
283
+ not reach the deployed Agent, unless the Agent is itself a coding agent over its repository and
284
+ the host sets `TAU_INCLUDE_DEVELOPMENT_RESOURCES=true`. See
285
+ [ADR 0025](docs/adrs/0025-runtime-resources-only-from-tau.md).
286
+
277
287
  Extensions run as project code in the same process and trust boundary as the rest of the
278
288
  repository. Optional capabilities such as general web access belong in a project extension; they
279
289
  are not bundled into the SDK. Main Sequence transport and protocol behavior remains SDK-owned.
@@ -85,7 +85,9 @@ uv run ms-tau skills sync --path .
85
85
  ```
86
86
 
87
87
  This explicit command owns only `.agents/skills/ms_tau_sdk/`; package installation and runtime
88
- startup never modify the repository automatically. The bundle includes
88
+ startup never modify the repository automatically. These skills guide the coding agent that works
89
+ on the repository; the deployed Agent reads its own resources from `.tau/` only and never sees
90
+ them. The bundle includes
89
91
  [`design-agent-icons-and-motion`](src/ms_tau_sdk/agent_skills/design-agent-icons-and-motion/SKILL.md):
90
92
  advisory composition and animation guidance with a self-contained MetaTables visual example.
91
93
  Each agent's artwork and build files remain in its owning project.
@@ -234,12 +236,20 @@ defaults and install project-specific tools through the normal `.tau/` structure
234
236
  your-project/
235
237
  ├── .tau/
236
238
  │ ├── SYSTEM.md
239
+ │ ├── AGENTS.md
237
240
  │ ├── settings.json
241
+ │ ├── skills/
238
242
  │ └── extensions/
239
243
  ├── pyproject.toml
240
244
  └── uv.lock
241
245
  ```
242
246
 
247
+ `.tau/` is the only directory the deployed Agent reads by default. The repository root's
248
+ `AGENTS.md` and the `.agents/` skills are for the coding agents that develop the repository and do
249
+ not reach the deployed Agent, unless the Agent is itself a coding agent over its repository and
250
+ the host sets `TAU_INCLUDE_DEVELOPMENT_RESOURCES=true`. See
251
+ [ADR 0025](docs/adrs/0025-runtime-resources-only-from-tau.md).
252
+
243
253
  Extensions run as project code in the same process and trust boundary as the rest of the
244
254
  repository. Optional capabilities such as general web access belong in a project extension; they
245
255
  are not bundled into the SDK. Main Sequence transport and protocol behavior remains SDK-owned.
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "ms-tau-sdk"
7
- version = "2.0.26.dev77"
7
+ version = "2.0.26.dev78"
8
8
  description = "Workspace-bound Tau application primitives for Main Sequence projects"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.13"
@@ -15,24 +15,39 @@ The workspace `.tau/` directory is the project behavior boundary:
15
15
 
16
16
  ```text
17
17
  .tau/
18
- ├── SYSTEM.md
18
+ ├── SYSTEM.md replaces the packaged system prompt
19
+ ├── APPEND_SYSTEM.md appended to the system prompt
20
+ ├── AGENTS.md project instructions for the deployed Agent
19
21
  ├── prompts/
20
22
  ├── skills/
21
23
  └── extensions/
22
24
  ```
23
25
 
24
26
  Creating `.tau/SYSTEM.md` replaces the packaged default general system instructions. There is no
25
- second SDK-specific prompt override or companion append file. Other resources follow the TAU
26
- version pinned by the installed SDK.
27
-
28
- `.agents/skills/ms_tau_sdk/` is different: it contains managed development instructions for coding
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.
27
+ SDK-specific prompt setting; Tau's own `APPEND_SYSTEM.md` and `AGENTS.md` under `.tau/` are the
28
+ other prompt resources. Other resources follow the TAU version pinned by the installed SDK.
29
+
30
+ By default the deployed Agent reads `.tau/` and nothing else in the repository. The root
31
+ `AGENTS.md` and `.agents/` hold instructions for coding agents working on the repository, you
32
+ among them: `.agents/skills/ms_tau_sdk/` is this SDK's managed development bundle,
33
+ `.agents/skills/mainsequence/` the platform SDK's, and the repository adds its own. None of it is
34
+ runtime behavior, none of it reaches the Agent, and none of it may be used as application
35
+ configuration. Put instructions meant for the Agent in `.tau/AGENTS.md`, `.tau/APPEND_SYSTEM.md`
36
+ or `.tau/SYSTEM.md`. Only an Agent that is itself a coding agent over its repository sets
37
+ `TAU_INCLUDE_DEVELOPMENT_RESOURCES=true`, which reads the root `AGENTS.md` and `.agents/` as well.
38
+
39
+ Tau reads the Agent's skills in `.tau/skills/` at any depth: a folder with a `SKILL.md` is a skill,
40
+ named after its folder; a folder without one groups skills, such as
41
+ `.tau/skills/<group>/<skill>/SKILL.md`. When the application also publishes the Agent's usage skills
42
+ to other agents with the mainsequence SDK's `skills_extension`, point the publisher at `.tau/skills`
43
+ itself: the Agent's skills and the published skills are one set. Do not publish a group folder
44
+ inside it to keep some skills private; instructions other agents may not see belong in
45
+ `.tau/SYSTEM.md` or `.tau/AGENTS.md`. The publisher's rules then hold for the whole folder:
46
+ lowercase letters, digits and single hyphens in every skill and group folder name; every folder a
47
+ skill or a non-empty group; frontmatter `name` equal to the folder name; leaf folder names unique,
48
+ because Tau names a skill by its folder. A group folder prefixes the published `skill://` URIs, so
49
+ a pack that was published from its own folder moves its skills directly into `.tau/skills` to keep
50
+ its URIs. Development skills stay in `.agents/skills` and are never published.
36
51
 
37
52
  ## Agent artwork
38
53
 
@@ -62,12 +77,14 @@ tools are project choices and are not part of the base SDK.
62
77
 
63
78
  ## Runtime tool composition
64
79
 
65
- Two independent process settings select the host-provided tools. Both default to `false`:
80
+ Three independent process settings select the host-provided tools and the reading scope. All
81
+ default to `false`:
66
82
 
67
83
  | Setting | Effect when `true` |
68
84
  | --- | --- |
69
85
  | `TAU_EXCLUDE_BASE_TOOLS` | Replace Tau's `read`, `write`, `edit`, and `bash` with a skill-scoped `read`. |
70
86
  | `TAU_EXCLUDE_MAINSEQUENCE_MCP` | Skip Main Sequence MCP connection, tools, resources, and resource prompt. |
87
+ | `TAU_INCLUDE_DEVELOPMENT_RESOURCES` | Read the repository as a coding harness: the root `AGENTS.md`, every `AGENTS.md` above the workspace, `.agents/AGENTS.md`, `.agents/skills` and `.agents/prompts`, beside `.tau`. |
71
88
 
72
89
  Set them in `harness_agent.spec.env_vars` in a managed repository workflow, in the process
73
90
  environment for local `ms-tau`, or through explicit `TauSDKSettings` fields in a Python host. They
@@ -116,7 +133,7 @@ A declared application can publish Agent Skills through the MCP Skills extension
116
133
  mainsequence SDK's skills publisher does. The Agent lists them with `<name>__list_resources` and
117
134
  reads one file with `<name>__read_resource`; a skill file is returned only when its bytes match the
118
135
  size and SHA-256 digest the skill lists. Published skills stay the application's: they are not
119
- project skills under `.tau/skills/`, and fields such as `allowed-tools` grant nothing. With the
136
+ added to this Agent's skills under `.tau/skills/`, and fields such as `allowed-tools` grant nothing. With the
120
137
  coding tools, a model that follows a published skill can run commands; set
121
138
  `TAU_EXCLUDE_BASE_TOOLS=true` when that must not happen.
122
139
 
@@ -22,6 +22,8 @@ uv run ms-tau skills sync --path .
22
22
 
23
23
  This command atomically owns only `.agents/skills/ms_tau_sdk/`. Do not copy these files manually,
24
24
  edit the managed copies, or make installation and runtime startup synchronize them implicitly.
25
+ These are development instructions for coding agents; the Agent the repository deploys reads its
26
+ resources from `.tau/` only and never sees `.agents/` or the root `AGENTS.md`.
25
27
 
26
28
  ## Service entry points
27
29
 
@@ -0,0 +1,107 @@
1
+ """Version-locked interception of Tau's project instruction discovery (ADR 0025).
2
+
3
+ Tau is a coding-agent harness. It reads the repository root's ``AGENTS.md``, every ``AGENTS.md``
4
+ between the project root and the workspace, and ``.agents/AGENTS.md`` as project instructions,
5
+ the way Codex and Claude Code do. In a repository that deploys an Agent those files instruct the
6
+ coding agents that develop the repository, not the Agent. ``tau-ai==0.4.2`` computes the ancestor
7
+ files from the workspace directly, so the SDK's ``TauPaths`` cannot redirect them, and no session
8
+ option or extension hook removes a discovered file.
9
+
10
+ This module wraps the discovery function where ``tau_coding.session`` calls it, on load and on
11
+ reload. For resource paths whose ``TauPaths`` declare that they do not read the development
12
+ resources, it keeps only the files under the workspace ``.tau`` directory; for every other caller
13
+ it returns Tau's result unchanged. Tau's own reading, diagnostics and de-duplication are reused.
14
+ Like the provider patch of ADR 0016, the wrapper is locked to the exact Tau version it was
15
+ reviewed against: an unverified wrapper could let development instructions reach a deployed Agent
16
+ silently.
17
+ """
18
+
19
+ from __future__ import annotations
20
+
21
+ from importlib.metadata import version
22
+ from pathlib import Path
23
+ from typing import Any
24
+
25
+ from tau_coding import context as tau_context
26
+ from tau_coding import session as tau_session
27
+ from tau_coding.resources import ResourceDiagnostic, TauResourcePaths
28
+ from tau_coding.system_prompt import ProjectContextFile
29
+
30
+ from ms_tau_sdk.errors import ConfigurationError
31
+
32
+ AFFECTED_TAU_AI_VERSION = "0.4.2"
33
+ _PATCH_MARKER = "__ms_tau_sdk_project_context_filter__"
34
+ # The attribute a `TauPaths` sets to `False` to keep project instructions to `.tau`.
35
+ READS_DEVELOPMENT_RESOURCES = "reads_development_resources"
36
+ EXCLUDED_CONTEXT_MESSAGE = (
37
+ "not read: an AGENTS.md outside .tau instructs the coding agents that develop the "
38
+ "repository, not the Agent; TAU_INCLUDE_DEVELOPMENT_RESOURCES reads it (ADR 0025)"
39
+ )
40
+
41
+
42
+ def install_project_context_filter() -> None:
43
+ """Install the reviewed Tau 0.4.2 project instruction filter exactly once.
44
+
45
+ A Tau upgrade must review and either remove or explicitly retarget this private-boundary
46
+ wrapper. Failing session construction is safer than applying it to another version unseen.
47
+ """
48
+
49
+ installed_version = version("tau-ai")
50
+ if installed_version != AFFECTED_TAU_AI_VERSION:
51
+ raise ConfigurationError(
52
+ "The ms-tau-sdk project instruction filter only supports "
53
+ f"tau-ai=={AFFECTED_TAU_AI_VERSION}; found {installed_version}. Review "
54
+ "tau_coding.context.discover_project_context_with_diagnostics and its use in "
55
+ "tau_coding.session before changing the pin (ADR 0025)."
56
+ )
57
+
58
+ session_module: Any = tau_session
59
+ current = session_module.discover_project_context_with_diagnostics
60
+ if getattr(current, _PATCH_MARKER, False):
61
+ return
62
+ if current is not tau_context.discover_project_context_with_diagnostics:
63
+ raise ConfigurationError(
64
+ "Tau project context discovery changed; compatibility review required (ADR 0025)"
65
+ )
66
+
67
+ original = current
68
+
69
+ def filtered(
70
+ paths: TauResourcePaths | None = None,
71
+ ) -> tuple[tuple[ProjectContextFile, ...], tuple[ResourceDiagnostic, ...]]:
72
+ context_files, diagnostics = original(paths)
73
+ if (
74
+ paths is None
75
+ or paths.cwd is None
76
+ or getattr(paths.paths, READS_DEVELOPMENT_RESOURCES, True)
77
+ ):
78
+ return context_files, diagnostics
79
+ # Tau resolves every discovered path; compare against the resolved `.tau` directory.
80
+ tau_dir = (paths.cwd / ".tau").expanduser().resolve()
81
+ kept: list[ProjectContextFile] = []
82
+ excluded: list[ResourceDiagnostic] = []
83
+ for context_file in context_files:
84
+ path = Path(context_file.path)
85
+ if path.is_relative_to(tau_dir):
86
+ kept.append(context_file)
87
+ else:
88
+ excluded.append(
89
+ ResourceDiagnostic(
90
+ kind="context",
91
+ path=path,
92
+ severity="info",
93
+ message=EXCLUDED_CONTEXT_MESSAGE,
94
+ )
95
+ )
96
+ return tuple(kept), (*diagnostics, *excluded)
97
+
98
+ setattr(filtered, _PATCH_MARKER, True)
99
+ session_module.discover_project_context_with_diagnostics = filtered
100
+
101
+
102
+ __all__ = [
103
+ "AFFECTED_TAU_AI_VERSION",
104
+ "EXCLUDED_CONTEXT_MESSAGE",
105
+ "READS_DEVELOPMENT_RESOURCES",
106
+ "install_project_context_filter",
107
+ ]
@@ -0,0 +1,120 @@
1
+ """Packaged Tau resource paths with project-native override semantics."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from collections.abc import Generator, Iterator
6
+ from importlib.resources import files
7
+ from pathlib import Path
8
+ from typing import ClassVar, Self
9
+
10
+ from tau_coding.paths import TauPaths
11
+ from tau_coding.resources import TauResourcePaths
12
+
13
+ from ms_tau_sdk.resources.context_compat import install_project_context_filter
14
+
15
+
16
+ def resource_root() -> Path:
17
+ return Path(str(files("ms_tau_sdk.resources")))
18
+
19
+
20
+ class _SkillsDir(Path):
21
+ """A skills directory whose listing also holds the skills inside its group folders.
22
+
23
+ Tau reads the folders directly in a skills directory, so the skills in a folder without a
24
+ `SKILL.md`, such as `.tau/skills/<group>/<skill>/SKILL.md`, were skipped. Listing them
25
+ beside the direct entries lets Tau read, index, reload and serve them like any other skill.
26
+ """
27
+
28
+ def iterdir(self) -> Generator[Self]:
29
+ for entry in sorted(Path(self).iterdir()):
30
+ yield self.with_segments(entry)
31
+ if _is_group(entry):
32
+ yield from map(self.with_segments, _skill_folders(entry))
33
+
34
+
35
+ def _is_group(folder: Path) -> bool:
36
+ # A symbolic link is not followed into, so a link cannot loop the listing.
37
+ return folder.is_dir() and not folder.is_symlink() and not (folder / "SKILL.md").exists()
38
+
39
+
40
+ def _skill_folders(group: Path) -> Iterator[Path]:
41
+ for entry in sorted(group.iterdir()):
42
+ if entry.is_dir() and (entry / "SKILL.md").exists():
43
+ yield entry
44
+ elif _is_group(entry):
45
+ yield from _skill_folders(entry)
46
+
47
+
48
+ class _RepositoryPaths(TauPaths):
49
+ """Tau paths that read the repository as a coding harness: `.tau` and `.agents`.
50
+
51
+ This is Tau's own scope, with nested skills. It is selected with
52
+ `TAU_INCLUDE_DEVELOPMENT_RESOURCES` for an Agent that is a coding agent over its own
53
+ repository, whose root `AGENTS.md` and `.agents/` are its instructions (ADR 0025, section 2).
54
+ Tau rebuilds its resource paths on load and reload but keeps this object, so the scope holds
55
+ on both.
56
+ """
57
+
58
+ __slots__ = ()
59
+
60
+ # Read by the project instruction filter of `context_compat`.
61
+ reads_development_resources: ClassVar[bool] = True
62
+
63
+ def project_skills_dir(self, cwd: Path) -> Path:
64
+ return _SkillsDir(super().project_skills_dir(cwd))
65
+
66
+ def project_agents_skills_dir(self, cwd: Path) -> Path:
67
+ return _SkillsDir(super().project_agents_skills_dir(cwd))
68
+
69
+
70
+ class _TauOnlyPaths(_RepositoryPaths):
71
+ """Tau paths that read the Agent's project resources from `.tau` alone (ADR 0025).
72
+
73
+ The project's `.agents/skills`, `.agents/prompts` and `.agents/AGENTS.md` hold instructions
74
+ for the coding agents that develop the repository, not for the Agent it deploys. Every
75
+ `.agents` project location resolves to its `.tau` counterpart here; Tau de-duplicates the
76
+ repeated directories, so each is read once. The `AGENTS.md` files Tau finds without consulting
77
+ these paths are removed by the filter of `context_compat`, which reads the class attribute.
78
+ """
79
+
80
+ __slots__ = ()
81
+
82
+ reads_development_resources: ClassVar[bool] = False
83
+
84
+ def project_agents_dir(self, cwd: Path) -> Path:
85
+ return self.project_tau_dir(cwd)
86
+
87
+ def project_agents_skills_dir(self, cwd: Path) -> Path:
88
+ return self.project_skills_dir(cwd)
89
+
90
+ def project_agents_prompts_dir(self, cwd: Path) -> Path:
91
+ return self.project_prompts_dir(cwd)
92
+
93
+
94
+ def tau_resource_paths(
95
+ cwd: Path,
96
+ *,
97
+ state_home: Path,
98
+ include_development_resources: bool = False,
99
+ ) -> TauResourcePaths:
100
+ """Return SDK defaults plus Tau-native project resource discovery.
101
+
102
+ `root` stays on the packaged resources, which are read-only inputs: an
103
+ installed wheel lives in site-packages, so the SDK must never write there.
104
+ `paths` therefore sends every durable Tau write — built-in extension state,
105
+ provider credentials, project trust, diagnostics, sessions — to the
106
+ writable `state_home` instead.
107
+
108
+ Project resources come from `.tau` alone unless `include_development_resources`
109
+ also admits the repository's `AGENTS.md` files and `.agents/`. The version-locked
110
+ filter that keeps Tau's `AGENTS.md` discovery to `.tau` is installed here, before
111
+ the session that uses these paths loads; it fails on an unreviewed Tau version.
112
+ """
113
+ install_project_context_filter()
114
+ paths_class = _RepositoryPaths if include_development_resources else _TauOnlyPaths
115
+ return TauResourcePaths(
116
+ root=resource_root(),
117
+ cwd=cwd,
118
+ agents_root=None,
119
+ paths=paths_class(home=state_home),
120
+ )
@@ -2,8 +2,11 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
+ import os
5
6
  from dataclasses import dataclass
7
+ from pathlib import Path
6
8
 
9
+ import structlog
7
10
  from tau_agent.types import JSONValue
8
11
  from tau_coding import CodingSession
9
12
 
@@ -94,3 +97,80 @@ class ProjectExtensionState:
94
97
  "extension_error_count": self.extension_error_count,
95
98
  "tool_catalog_digest": self.tool_catalog_digest or None,
96
99
  }
100
+
101
+
102
+ _resources_logger = structlog.get_logger("ms_tau_sdk.runtime.resources")
103
+ # Extension and trust diagnostics are logged with the extension composition.
104
+ _RESOURCE_KINDS_LOGGED_ELSEWHERE = frozenset({"extension", "project-trust"})
105
+
106
+
107
+ def _workspace_relative_path(path: Path | None, *, cwd: Path) -> str | None:
108
+ # Never an absolute path: a file above the workspace reads as `../AGENTS.md`.
109
+ if path is None:
110
+ return None
111
+ return Path(os.path.relpath(path.resolve(), cwd.resolve())).as_posix()
112
+
113
+
114
+ def project_resources_summary(
115
+ session: CodingSession, *, cwd: Path, development_resources: bool
116
+ ) -> dict[str, JSONValue]:
117
+ """The Agent's resources Tau selected from the workspace (ADR 0025)."""
118
+ system_prompt_files: list[JSONValue] = [
119
+ _workspace_relative_path(path, cwd=cwd) for path in session.system_prompt_files
120
+ ]
121
+ context_files: list[JSONValue] = [
122
+ _workspace_relative_path(Path(context_file.path), cwd=cwd)
123
+ for context_file in session.context_files
124
+ ]
125
+ skills: list[JSONValue] = [*sorted(skill.name for skill in session.skills)]
126
+ prompt_templates: list[JSONValue] = [
127
+ *sorted(template.name for template in session.prompt_templates)
128
+ ]
129
+ return {
130
+ "developmentResources": development_resources,
131
+ "systemPromptFiles": system_prompt_files,
132
+ "contextFiles": context_files,
133
+ "skills": skills,
134
+ "promptTemplates": prompt_templates,
135
+ }
136
+
137
+
138
+ def log_project_resources(
139
+ session: CodingSession,
140
+ *,
141
+ session_uid: str,
142
+ cwd: Path,
143
+ development_resources: bool,
144
+ phase: str,
145
+ ) -> None:
146
+ """Log what Tau selected and reported for the Agent's resources on load or reload."""
147
+ summary = project_resources_summary(
148
+ session, cwd=cwd, development_resources=development_resources
149
+ )
150
+ _resources_logger.info(
151
+ "runtime.project_resources.load.completed",
152
+ session_uid=session_uid,
153
+ phase=phase,
154
+ development_resources=development_resources,
155
+ system_prompt_files=summary["systemPromptFiles"],
156
+ context_files=summary["contextFiles"],
157
+ skill_count=len(session.skills),
158
+ prompt_template_count=len(session.prompt_templates),
159
+ )
160
+ workspace = str(cwd.resolve())
161
+ for diagnostic in session.resource_diagnostics:
162
+ if diagnostic.kind in _RESOURCE_KINDS_LOGGED_ELSEWHERE:
163
+ continue
164
+ log = {"error": _resources_logger.error, "warning": _resources_logger.warning}.get(
165
+ diagnostic.severity, _resources_logger.info
166
+ )
167
+ log(
168
+ "runtime.project_resources.diagnostic",
169
+ session_uid=session_uid,
170
+ phase=phase,
171
+ resource_kind=diagnostic.kind,
172
+ resource_name=diagnostic.name,
173
+ resource_path=_workspace_relative_path(diagnostic.path, cwd=cwd),
174
+ diagnostic_severity=diagnostic.severity,
175
+ message=diagnostic.message.replace(workspace, "."),
176
+ )
@@ -59,6 +59,8 @@ from ms_tau_sdk.runtime.events import TauRuntimeEvent
59
59
  from ms_tau_sdk.runtime.extensions import (
60
60
  TASK_CONTROL_TOOL_NAMES,
61
61
  ProjectExtensionState,
62
+ log_project_resources,
63
+ project_resources_summary,
62
64
  validate_tool_catalog,
63
65
  )
64
66
  from ms_tau_sdk.runtime.live_turns import LiveTurn, LocalChatTurn
@@ -327,6 +329,7 @@ class SessionRuntimeManager:
327
329
  "mode": "local" if self.settings.local_mode else "managed",
328
330
  "exclude_base_tools": self.settings.exclude_base_tools,
329
331
  "exclude_mainsequence_mcp": self.settings.exclude_mainsequence_mcp,
332
+ "include_development_resources": self.settings.include_development_resources,
330
333
  "base_tool_count": 0 if self.settings.exclude_base_tools else 4,
331
334
  "task_control_tool_count": 2,
332
335
  "workspace_digest": self.settings.workspace_digest,
@@ -612,6 +615,11 @@ class SessionRuntimeManager:
612
615
  "tools": tools,
613
616
  "extensions": extensions,
614
617
  "diagnostics": diagnostics,
618
+ "resources": project_resources_summary(
619
+ coding_session,
620
+ cwd=self.settings.workspace,
621
+ development_resources=self.settings.include_development_resources,
622
+ ),
615
623
  }
616
624
 
617
625
  def local_extension_source(self, session_uid: str, source_uid: str) -> dict[str, object]:
@@ -1193,6 +1201,7 @@ class SessionRuntimeManager:
1193
1201
  resource_paths=tau_resource_paths(
1194
1202
  cwd,
1195
1203
  state_home=self.settings.tau_state_home,
1204
+ include_development_resources=(self.settings.include_development_resources),
1196
1205
  ),
1197
1206
  project_extensions_enabled=True,
1198
1207
  trust_override="approve",
@@ -1211,6 +1220,13 @@ class SessionRuntimeManager:
1211
1220
  coding_session=coding_session,
1212
1221
  state=project_extension_state,
1213
1222
  )
1223
+ log_project_resources(
1224
+ coding_session,
1225
+ session_uid=session_uid,
1226
+ cwd=cwd,
1227
+ development_resources=self.settings.include_development_resources,
1228
+ phase="load",
1229
+ )
1214
1230
  runtime = ActiveSessionRuntime(
1215
1231
  session_uid=session_uid,
1216
1232
  holder_id=self.holder_id,
@@ -1226,6 +1242,7 @@ class SessionRuntimeManager:
1226
1242
  catalog_digest=bootstrap.provider_control.catalog_digest,
1227
1243
  project_extension_state=project_extension_state,
1228
1244
  validate_tool_catalog=check_catalog,
1245
+ development_resources=self.settings.include_development_resources,
1229
1246
  )
1230
1247
  state = bootstrap.runtime_state
1231
1248
  runtime.runtime_activity = state.runtime_activity or "loading"
@@ -14,7 +14,7 @@ from tau_coding.reload import CodingReloadSummary
14
14
  from ms_tau_sdk.backend.mcp import MainSequenceMCPClient
15
15
  from ms_tau_sdk.backend.models import TauTurnCommit
16
16
  from ms_tau_sdk.runtime.events import TauRuntimeEvent, translate_tau_event
17
- from ms_tau_sdk.runtime.extensions import ProjectExtensionState
17
+ from ms_tau_sdk.runtime.extensions import ProjectExtensionState, log_project_resources
18
18
  from ms_tau_sdk.runtime.provenance import PROVENANCE_NAMESPACE, TurnProvenance
19
19
  from ms_tau_sdk.sessions.storage import BackendSessionStorage
20
20
 
@@ -52,6 +52,7 @@ class ActiveSessionRuntime:
52
52
  catalog_digest: str = ""
53
53
  project_extension_state: ProjectExtensionState | None = None
54
54
  validate_tool_catalog: Callable[[CodingSession], None] | None = None
55
+ development_resources: bool = False
55
56
 
56
57
  async def reload(self) -> CodingReloadSummary:
57
58
  """Reload Tau resources and reject an invalid replacement catalog."""
@@ -61,6 +62,13 @@ class ActiveSessionRuntime:
61
62
  self.validate_tool_catalog(self.coding_session)
62
63
  if self.project_extension_state is not None:
63
64
  self.project_extension_state.update_from_session(self.coding_session)
65
+ log_project_resources(
66
+ self.coding_session,
67
+ session_uid=self.session_uid,
68
+ cwd=self.coding_session.cwd,
69
+ development_resources=self.development_resources,
70
+ phase="reload",
71
+ )
64
72
  return summary
65
73
 
66
74
  async def prompt(
@@ -161,6 +161,12 @@ class TauSDKSettings(BaseSettings):
161
161
  default=False,
162
162
  validation_alias="TAU_EXCLUDE_MAINSEQUENCE_MCP",
163
163
  )
164
+ # Read the repository as a coding harness: its `AGENTS.md` files and `.agents/` join the
165
+ # Agent's `.tau` resources (ADR 0025). Off, the Agent reads `.tau` alone.
166
+ include_development_resources: bool = Field(
167
+ default=False,
168
+ validation_alias="TAU_INCLUDE_DEVELOPMENT_RESOURCES",
169
+ )
164
170
  local_custom_id: str | None = Field(default=None, validation_alias="TAU_LOCAL_CUSTOM_ID")
165
171
  local_organization_environment_uid: str | None = Field(
166
172
  default=None, validation_alias="TAU_LOCAL_ORGANIZATION_ENVIRONMENT_UID"
@@ -1,76 +0,0 @@
1
- """Packaged Tau resource paths with project-native override semantics."""
2
-
3
- from __future__ import annotations
4
-
5
- from collections.abc import Generator, Iterator
6
- from importlib.resources import files
7
- from pathlib import Path
8
- from typing import Self
9
-
10
- from tau_coding.paths import TauPaths
11
- from tau_coding.resources import TauResourcePaths
12
-
13
-
14
- def resource_root() -> Path:
15
- return Path(str(files("ms_tau_sdk.resources")))
16
-
17
-
18
- class _SkillsDir(Path):
19
- """A skills directory whose listing also holds the skills inside its group folders.
20
-
21
- Tau reads the folders directly in a skills directory, so the skills in a folder without a
22
- `SKILL.md`, such as `.agents/skills/mcp_tools/<skill>/SKILL.md`, were skipped. Listing them
23
- beside the direct entries lets Tau read, index, reload and serve them like any other skill.
24
- """
25
-
26
- def iterdir(self) -> Generator[Self]:
27
- for entry in sorted(Path(self).iterdir()):
28
- yield self.with_segments(entry)
29
- if _is_group(entry):
30
- yield from map(self.with_segments, _skill_folders(entry))
31
-
32
-
33
- def _is_group(folder: Path) -> bool:
34
- # A symbolic link is not followed into, so a link cannot loop the listing.
35
- return folder.is_dir() and not folder.is_symlink() and not (folder / "SKILL.md").exists()
36
-
37
-
38
- def _skill_folders(group: Path) -> Iterator[Path]:
39
- for entry in sorted(group.iterdir()):
40
- if entry.is_dir() and (entry / "SKILL.md").exists():
41
- yield entry
42
- elif _is_group(entry):
43
- yield from _skill_folders(entry)
44
-
45
-
46
- class _NestedSkillsPaths(TauPaths):
47
- """Tau paths whose project skills directories read nested skills.
48
-
49
- Tau rebuilds its resource paths on load and reload but keeps this object, so nested skills
50
- are read on both.
51
- """
52
-
53
- __slots__ = ()
54
-
55
- def project_skills_dir(self, cwd: Path) -> Path:
56
- return _SkillsDir(super().project_skills_dir(cwd))
57
-
58
- def project_agents_skills_dir(self, cwd: Path) -> Path:
59
- return _SkillsDir(super().project_agents_skills_dir(cwd))
60
-
61
-
62
- def tau_resource_paths(cwd: Path, *, state_home: Path) -> TauResourcePaths:
63
- """Return SDK defaults plus Tau-native project resource discovery.
64
-
65
- `root` stays on the packaged resources, which are read-only inputs: an
66
- installed wheel lives in site-packages, so the SDK must never write there.
67
- `paths` therefore sends every durable Tau write — built-in extension state,
68
- provider credentials, project trust, diagnostics, sessions — to the
69
- writable `state_home` instead.
70
- """
71
- return TauResourcePaths(
72
- root=resource_root(),
73
- cwd=cwd,
74
- agents_root=None,
75
- paths=_NestedSkillsPaths(home=state_home),
76
- )