# NousResearch/hermes-agent · google-workspace required_credential_files and OAuth file access in remote terminals

## Open now

- Nothing open on GitHub.

Three members relate to the google-workspace skill: two pull request titles about restoring required_credential_files in SKILL.md, and one bug report about being unable to read an OAuth file in remote terminals. The titles suggest a shared theme around credential file handling, but the link between the bug and the fix is inferred from titles alone.

## TL;DR

- 3 member(s): 0 open issue(s), 1 closed issue(s), 0 open PR(s), 1 merged PR(s), 1 PR(s) closed without merging.
- A fix was merged to main in PR #19886. Verify it addresses your case.
- **Recommended:** read PR #19886 (and any competing PRs below) before filing. Confirm the merged change covers your symptom.

## Maintainer resolution verdict

- Internal-tracker verdict over this cluster's issue members: `unresolved`. This is distinct from the fix status above. Weigh it yourself.

## How this cluster was grouped

- Link evidence (`STRUCTURAL_SPANNING`): every member is connected by structural evidence: shared changed files, or a closing/superseded reference.
- Pairwise link density `0.67`: the share of member pairs that carry a link, counting a structural link in full and a relevance-only link at 0.36. `1.00` means every pair is linked.

## Closed / duplicate issues

- #16452: \[Bug\]: google-workspace can't read oauth file in remote terminals

## Prior & competing PRs

- **#19886: fix(google-workspace): restore required_credential_files in SKILL.md**. merged to main · fix tier `verified`
  - maintainer direction: category `merged`
  - files: `skills/productivity/google-workspace/SKILL.md`, `tests/skills/test_google_workspace_credential_files.py`
  - fix hunks:
  - `skills/productivity/google-workspace/SKILL.md`

    ```diff
    @@ -1,9 +1,14 @@
     ---
     name: google-workspace
     description: "Gmail, Calendar, Drive, Docs, Sheets via gws CLI or Python."
    -version: 1.0.0
    +version: 1.0.1
     author: Nous Research
     license: MIT
    +required_credential_files:
    +  - path: google_token.json
    +    description: Google OAuth2 token (created by setup script)
    +  - path: google_client_secret.json
    +    description: Google OAuth2 client credentials (downloaded from Google Cloud Console)
     metadata:
       hermes:
         tags: [Google, Gmail, Calendar, Drive, Sheets, Docs, Contacts, Email, OAuth]
    ```
  - `tests/skills/test_google_workspace_credential_files.py`

    ```diff
    @@ -0,0 +1,102 @@
    +"""Regression test: google-workspace SKILL.md must declare required_credential_files.
    +
    +PR #9931 accidentally removed the required_credential_files header, which broke
    +credential file mounting in Docker/Modal remote backends (#16452). This test
    +prevents the regression from silently reappearing.
    +"""
    +
    +from __future__ import annotations
    +
    +import os
    +from pathlib import Path
    +from unittest.mock import patch
    +
    +import pytest
    +
    +SKILL_MD = (
    +    Path(__file__).resolve().parents[2]
    +    / "skills/productivity/google-workspace/SKILL.md"
    +)
    +
    +_EXPECTED_PATHS = {"google_token.json", "google_client_secret.json"}
    +
    +
    +def _parse_frontmatter(content: str) -> dict:
    +    from agent.skill_utils import parse_frontmatter
    +
    +    fm, _ = parse_frontmatter(content)
    +    return fm
    +
    +
    +class TestGoogleWorkspaceCredentialFiles:
    +    def test_required_credential_files_present_in_skill_md(self):
    +        content = SKILL_MD.read_text(encoding="utf-8")
    +        fm = _parse_frontmatter(content)
    +        entries = fm.get("required_credential_files")
    +        assert entries, "required_credential_files missing from google-workspace SKILL.md"
    +        assert isinstance(entries, list), "required_credential_files must be a list"
    +        paths = {
    +            (e["path"] if isinstance(e, dict) else e)
    +            for e in entries
    +        }
    +        assert _EXPECTED_PATHS <= paths, (
    +            f"Missing entries in required_credential_files: [redacted:secret-assignment] - paths}"
    +        )
    +
    +    def test_entries_are_registered_when_files_exist(self, tmp_path):
    +        hermes_home = tmp_path / ".hermes"
    +        hermes_home.mkdir()
    +        (hermes_home / "google_token.json").write_text("{}")
    +        (hermes_home / "google_client_secret.json").write_text("{}")
    +
    +        from tools.credential_files import (
    +            clear_credential_files,
    +            get_credential_file_mounts,
    +            register_credential_files,
    +        )
    +
    +        clear_credential_files()
    +        try:
    +            content = SKILL_MD.read_text(encoding="utf-8")
    +            fm = _parse_frontmatter(content)
    +            entries = fm.get("required_credential_files", [])
    +
    +            with patch.dict(os.environ, {"HERMES_HOME": str(hermes_home)}):
    +                missing = register_credential_files(entries)
    +
    +            assert missing == [], f"Unexpected missing files: {missing}"
    +            mounts = get_credential_file_mounts()
    +            container_paths = {m["container_path"] for m in mounts}
    +            assert "/root/.hermes/google_token.json" in container_paths
    +            assert "/root/.hermes/google_client_secret.json" in container_paths
    +        finally:
    +            clear_credential_files()
    +
    +    def test_missing_token_is_reported(self, tmp_path):
    +        """google_token.json absent (first-time setup) — reported as missing, client secret still mounts."""
    +        hermes_home = tmp_path / ".hermes"
    +        hermes_home.mkdir()
    +        (hermes_home / "google_client_secret.json").write_text("{}")
    +
    +        from tools.credential_files import (
    +            clear_credential_files,
    +            get_credential_file_mounts,
    +            register_credential_files,
    +        )
    +
    +        clear_credential_files()
    +        try:
    +            content = SKILL_MD.read_text(encoding="utf-8")
    +            fm = _parse_frontmatter(content)
    +            entries = fm.get("required_credential_files", [])
    +
    +            with patch.dict(os.environ, {"HERMES_HOME": str(hermes_home)}):
    +                missing = register_credential_files(entries)
    +
    +            assert "google_token.json" in missing
    +            mounts = get_credential_file_mounts()
    +            container_paths = {m["container_path"] for m in mounts}
    +            assert "/root/.hermes/google_client_secret.json" in container_paths
    +            assert "/root/.hermes/google_token.json" not in container_paths
    +        finally:
    +            clear_credential_files()
    ```
- **#16470: fix(google-workspace): restore required_credential_files in SKILL.md (#16452)**. closed without merge
  - maintainer direction: category `merged_via`; implication `superseded`; refs `#19886`

## Fix status

- Merged to main in PR #19886 (cited above). No release data is tracked. Verify downstream.
- Fix last verified (merged) `2026-05-04`.
- Subsystem: `skills`
