|
| 1 | +# Harness TOS Skills |
| 2 | + |
| 3 | +[中文版](2026-10-12-harness-tos-skills.zh.md) |
| 4 | + |
| 5 | +## Metadata |
| 6 | + |
| 7 | +| Field | Value | |
| 8 | +| --- | --- | |
| 9 | +| Change ID | `harness-tos-skills` | |
| 10 | +| Created | 2026-10-12 | |
| 11 | +| Status | implemented | |
| 12 | +| Related specs | No component spec update; this extends the existing Harness selected-skill request contract without changing ownership boundaries. | |
| 13 | + |
| 14 | +## Background And Evidence |
| 15 | + |
| 16 | +Harness can already load SkillHub skills and Skill Space skills. Skill Space loading eventually downloads TOS-backed Skill archives through the existing materializer, but callers cannot directly provide an object-storage Skill directory. |
| 17 | + |
| 18 | +The requested behavior should align with AWS AgentCore Harness S3 skills: one object-storage URI points to one Skill directory containing `SKILL.md`; multiple Skills are represented as multiple selected-skill entries. |
| 19 | + |
| 20 | +## Goals |
| 21 | + |
| 22 | +- Add `source="tos"` to `HarnessSelectedSkill`. |
| 23 | +- Support current-account TOS directories via Runtime cloud identity. |
| 24 | +- Support public readable TOS zip URLs without requiring AK/SK. |
| 25 | +- Preserve existing SkillHub and Skill Space behavior. |
| 26 | + |
| 27 | +## Non-Goals |
| 28 | + |
| 29 | +- No parent-directory auto-discovery of multiple Skills. |
| 30 | +- No plaintext AK/SK in Harness requests. |
| 31 | +- No cross-account private TOS credential reference in this MVP. |
| 32 | + |
| 33 | +## Requirements |
| 34 | + |
| 35 | +| ID | Requirement | |
| 36 | +| --- | --- | |
| 37 | +| FR-1 | A TOS selected Skill with `tos_url="tos://bucket/path/to/skill/"` loads exactly one Skill directory and requires `SKILL.md` or `skill.md` at that URL root. | |
| 38 | +| FR-2 | Multiple TOS Skills are loaded by multiple `selected_skills` entries. | |
| 39 | +| FR-3 | A public TOS zip URL can load one Skill archive. | |
| 40 | +| FR-4 | Empty/bucket-root TOS URLs, parent directories, unsupported public URLs, non-TOS hosts, credential-bearing public URLs, and redirects to non-TOS hosts fail closed. | |
| 41 | +| FR-5 | TOS download failures and invalid Skill directories fail the whole SkillToolset build. | |
| 42 | +| FR-6 | Public TOS zip downloads, public zip extraction, and private TOS directory downloads enforce bounded file/object count and byte limits before Skill loading. | |
| 43 | + |
| 44 | +## Design |
| 45 | + |
| 46 | +`HarnessSelectedSkill.source` accepts `tos` with either: |
| 47 | + |
| 48 | +```python |
| 49 | +HarnessSelectedSkill(source="tos", tos_url="tos://bucket/skills/reporting/", region="cn-beijing") |
| 50 | +HarnessSelectedSkill(source="tos", tos_public_url="https://bucket.tos-cn-beijing.volces.com/skills/reporting.zip") |
| 51 | +``` |
| 52 | + |
| 53 | +Multiple Skills are expressed as multiple entries: |
| 54 | + |
| 55 | +```python |
| 56 | +selected_skills=[ |
| 57 | + HarnessSelectedSkill(source="tos", tos_url="tos://bucket/skills/reporting/", region="cn-beijing"), |
| 58 | + HarnessSelectedSkill(source="tos", tos_url="tos://bucket/skills/search/", region="cn-beijing"), |
| 59 | +] |
| 60 | +``` |
| 61 | + |
| 62 | +Harness serializes these as internal refs and dispatches them in `build_skill_toolset`. TOS selected Skills use `tos_url` and `tos_public_url` as the only locator fields. The materializer parses `tos://` URLs, downloads one directory through `VeTOS`, validates path containment, requires root-level `SKILL.md` or `skill.md`, and loads with ADK. A URL pointing at a parent folder containing child Skills is rejected instead of selecting one child. Public URLs must not contain userinfo or TOS signature query credentials. They are streamed as zip archives from validated TOS hosts, every redirect hop must also target a TOS host before it is requested, and the existing safe zip extraction path enforces expanded file count and uncompressed byte limits for public TOS archives. |
| 63 | + |
| 64 | +Rejected TOS refs are redacted when wrapped into Harness skill-load errors, so invalid userinfo or signature query values are not returned to callers or written by the Harness error path. |
| 65 | + |
| 66 | +## Tasks |
| 67 | + |
| 68 | +| ID | Task | |
| 69 | +| --- | --- | |
| 70 | +| T-1 | Add schema fields and validation for TOS selected Skills. | |
| 71 | +| T-2 | Add materializer helpers for TOS directory and public zip sources, including strict root-manifest validation and resource limits. | |
| 72 | +| T-3 | Wire Harness SkillToolset building to TOS refs. | |
| 73 | +| T-4 | Add focused contract and materializer tests. | |
| 74 | + |
| 75 | +## Verification |
| 76 | + |
| 77 | +| Requirement | Command | Result | |
| 78 | +| --- | --- | --- | |
| 79 | +| FR-1..FR-6 | `uv run --extra dev pytest tests/skills/test_adk_skill_materializer.py tests/test_ve_tos.py::test_download_can_propagate_tos_error tests/test_ve_tos.py::test_download_rejects_object_over_byte_limit tests/test_ve_tos.py::test_download_directory_rejects_too_many_objects tests/test_ve_tos.py::test_download_directory_rejects_total_bytes_from_listing -q` | pass, 30 passed on 2026-10-12 | |
| 80 | +| FR-4..FR-5 | `uv run --extra dev pytest tests/cloud/test_harness_app_contract.py::TestHarnessOverrides::test_tos_selected_skill_rejects_public_url_userinfo tests/cloud/test_harness_app_contract.py::TestHarnessOverrides::test_tos_selected_skill_rejects_public_url_signature_query tests/cloud/test_harness_skill_download.py::test_build_skill_toolset_redacts_tos_ref_on_error -q` | pass, 3 passed on 2026-10-13 | |
| 81 | +| FR-1..FR-6 | `uv run --extra dev pytest tests/cloud/test_harness_skill_download.py::test_build_skill_toolset_loads_tos_skill_uri tests/cloud/test_harness_skill_download.py::test_build_skill_toolset_loads_multiple_tos_entries -q` | pass, 2 passed on 2026-10-12 | |
| 82 | +| FR-1..FR-6 | `uv run --extra dev ruff check veadk/skills/materializer.py veadk/integrations/ve_tos/ve_tos.py tests/skills/test_adk_skill_materializer.py tests/test_ve_tos.py` | pass on 2026-10-12 | |
| 83 | +| FR-1..FR-6 | `uv run --extra dev pre-commit run --all-files` | pass on 2026-10-12 | |
| 84 | +| FR-1..FR-6 | `git diff --check` | pass on 2026-10-12 | |
| 85 | +| FR-1..FR-6 | `uv run --extra dev pytest tests/skills/test_adk_skill_materializer.py tests/test_ve_tos.py -q` | fail, existing environment limitation: four async tests in `tests/test_ve_tos.py` need an async pytest plugin; TOS-focused tests passed. | |
| 86 | + |
| 87 | +## Risks |
| 88 | + |
| 89 | +- Real TOS IAM behavior requires cloud smoke validation outside the offline unit tests. |
| 90 | +- Public directory URLs remain excluded because object storage does not provide a stable generic HTTP directory listing contract. |
0 commit comments