Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions plugins/memory/honcho/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -372,11 +372,16 @@ def from_global_config(
# intentionally configured Honcho for this host.
_explicitly_configured = bool(host_block) or raw.get("enabled") is True

# Explicit host block fields win, then flat/global, then defaults
# Explicit host block fields win, then flat/global, then defaults.
# Issue #26459: when falling back to ``resolved_host`` (e.g.
# ``hermes.<profile>``), replace ``.`` with ``_`` because Honcho's
# workspace ID regex is ``^[a-zA-Z0-9_-]+$`` β€” a dotted host key
# makes the API reject the request once per minute. Explicit
# workspace fields are passed through unchanged (user controls them).
workspace = (
host_block.get("workspace")
or raw.get("workspace")
or resolved_host
or resolved_host.replace(".", "_")
)
ai_peer = (
host_block.get("aiPeer")
Expand Down
48 changes: 48 additions & 0 deletions tests/honcho_plugin/test_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -523,6 +523,54 @@ def test_from_global_config_auto_resolves_host(self, tmp_path):
assert config.host == "hermes.dreamer"
assert config.peer_name == "dreamer-user"

def test_workspace_fallback_sanitizes_dotted_host(self, tmp_path):
"""Issue #26459: workspace fallback must not contain ``.`` characters.

When a profile host block exists but does not set an explicit
``workspace``, the fallback uses ``resolved_host`` (``hermes.<profile>``).
Honcho's API regex is ``^[a-zA-Z0-9_-]+$``, so the dot makes every
init call fail with ``string_pattern_mismatch`` β€” once per minute the
plugin retries and the warning floods the journal. Dots must be
rewritten to underscores.
"""
config_file = tmp_path / "config.json"
# No ``workspace`` field β€” exercises the fallback chain.
config_file.write_text(json.dumps({
"apiKey": "shared-key",
"hosts": {
"hermes.fundraising": {"peerName": "alice"},
},
}))
config = HonchoClientConfig.from_global_config(
host="hermes.fundraising", config_path=config_file,
)
assert "." not in config.workspace_id, (
f"workspace_id {config.workspace_id!r} contains '.', which violates "
"Honcho's ^[a-zA-Z0-9_-]+$ regex (issue #26459)"
)
assert config.workspace_id == "hermes_fundraising"

def test_explicit_workspace_field_is_passed_through_unchanged(self, tmp_path):
"""Explicit ``workspace`` values must NOT be rewritten β€” user controls them.

Sanitization only kicks in on the fallback. If the user has set
``hosts.<host>.workspace`` (or the root-level ``workspace``) to any
value, including one with characters Honcho would reject, it is
passed through verbatim so the user sees the real API error
rather than a silently-rewritten value.
"""
config_file = tmp_path / "config.json"
config_file.write_text(json.dumps({
"apiKey": "k",
"hosts": {
"hermes.fundraising": {"workspace": "my.explicit.ws"},
},
}))
config = HonchoClientConfig.from_global_config(
host="hermes.fundraising", config_path=config_file,
)
assert config.workspace_id == "my.explicit.ws"


class TestObservationModeMigration:
"""Existing configs without explicit observationMode keep 'unified' default."""
Expand Down
Loading