fix(deps): declare packaging as a core dependency so it ships everywhere - #40522
Conversation
packaging is imported directly on three production paths but was never declared in [project.dependencies], so it only reached users transitively (pip/uv pull it for other tools). The slim official Docker image ships without it, where each try/except-ImportError fallback silently degrades: - plugins/memory/hindsight/__init__.py (_meets_minimum_version) returns False when packaging is absent, disabling update_mode='append' so every session leaks separate Hindsight documents (the reported NousResearch#40503 symptom). - tools/lazy_deps.py (_is_satisfied) falls back to "installed counts as satisfied", defeating every version-constraint check on lazy extras. - hermes_cli/main.py drops to naive name==version requirement parsing. Promote it to a declared core dep pinned to packaging==26.0 — the exact version already resolved in uv.lock, so there is zero resolution churn (the lock change is two edge annotations marking it transitive->direct). It is a pure-Python py3-none-any wheel with no compiled extensions, safe to ship on every platform. Declaring it also wires it into the _verify_core_dependencies_installed() update-repair guard, which reinstalls missing [project.dependencies] on hermes update. Adds a hermetic tomllib-parse regression test that fails before the declaration and passes after. Fixes NousResearch#40503
There was a problem hiding this comment.
Pull request overview
Note
Copilot was unable to run its full agentic suite in this review.
This PR ensures the packaging library is explicitly declared as a core dependency (to avoid relying on transitive installs) and adds a regression test to prevent future accidental removal.
Changes:
- Add
packagingtoproject.dependenciesinpyproject.toml. - Add a regression test asserting
packagingis present in core dependencies.
Reviewed changes
Copilot reviewed 2 out of 3 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
| tests/test_packaging_metadata.py | Adds a regression test to ensure packaging remains a declared core dependency. |
| pyproject.toml | Declares packaging as a pinned core dependency with explanatory comments. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| names = { | ||
| # strip extras/markers/specifiers down to the distribution name | ||
| dep.split("==")[0].split(">=")[0].split("[")[0].split(";")[0].strip().lower() | ||
| for dep in core | ||
| } |
Address Copilot review on NousResearch#40522: the inline name-extraction only handled ==, >=, [ and ; and could mis-parse valid requirement strings using <=, ~=, !=, <, > or a direct reference (name @ url). Factor a _distribution_name helper that drops markers, direct-reference URLs and extras, then strips any version operator via regex, so a future dep declared with any PEP 508 specifier shape is matched correctly.
|
@copilot Addressed in commit 46dad59:
|
austinpickett
left a comment
There was a problem hiding this comment.
Reviewed by Hermes Agent — Approve ✅
Verified all three import sites use packaging directly:
plugins/memory/hindsight/__init__.py→from packaging.version import Versiontools/lazy_deps.py→from packaging.specifiers import SpecifierSet+version import Versionhermes_cli/main.py→from packaging.requirements import Requirement
Confirmed packaging is not declared anywhere in [project.dependencies] on main, so today it only arrives transitively — exactly the silent-degradation risk described (#40503). packaging==26.0 is a pure-Python py3-none-any wheel pinned to the already-resolved lock version, so no resolution churn or platform risk. The added regression test (test_packaging_declared_as_core_dependency) guards against reintroduction. LGTM.
…ere (NousResearch#40522) * fix(deps): declare packaging as a core dependency so it ships everywhere packaging is imported directly on three production paths but was never declared in [project.dependencies], so it only reached users transitively (pip/uv pull it for other tools). The slim official Docker image ships without it, where each try/except-ImportError fallback silently degrades: - plugins/memory/hindsight/__init__.py (_meets_minimum_version) returns False when packaging is absent, disabling update_mode='append' so every session leaks separate Hindsight documents (the reported NousResearch#40503 symptom). - tools/lazy_deps.py (_is_satisfied) falls back to "installed counts as satisfied", defeating every version-constraint check on lazy extras. - hermes_cli/main.py drops to naive name==version requirement parsing. Promote it to a declared core dep pinned to packaging==26.0 — the exact version already resolved in uv.lock, so there is zero resolution churn (the lock change is two edge annotations marking it transitive->direct). It is a pure-Python py3-none-any wheel with no compiled extensions, safe to ship on every platform. Declaring it also wires it into the _verify_core_dependencies_installed() update-repair guard, which reinstalls missing [project.dependencies] on hermes update. Adds a hermetic tomllib-parse regression test that fails before the declaration and passes after. Fixes NousResearch#40503 * test(deps): make packaging dep-name extraction PEP 508-robust Address Copilot review on NousResearch#40522: the inline name-extraction only handled ==, >=, [ and ; and could mis-parse valid requirement strings using <=, ~=, !=, <, > or a direct reference (name @ url). Factor a _distribution_name helper that drops markers, direct-reference URLs and extras, then strips any version operator via regex, so a future dep declared with any PEP 508 specifier shape is matched correctly. --------- Co-authored-by: briandevans <252620095+briandevans@users.noreply.github.com>
…ere (#40522) * fix(deps): declare packaging as a core dependency so it ships everywhere packaging is imported directly on three production paths but was never declared in [project.dependencies], so it only reached users transitively (pip/uv pull it for other tools). The slim official Docker image ships without it, where each try/except-ImportError fallback silently degrades: - plugins/memory/hindsight/__init__.py (_meets_minimum_version) returns False when packaging is absent, disabling update_mode='append' so every session leaks separate Hindsight documents (the reported #40503 symptom). - tools/lazy_deps.py (_is_satisfied) falls back to "installed counts as satisfied", defeating every version-constraint check on lazy extras. - hermes_cli/main.py drops to naive name==version requirement parsing. Promote it to a declared core dep pinned to packaging==26.0 — the exact version already resolved in uv.lock, so there is zero resolution churn (the lock change is two edge annotations marking it transitive->direct). It is a pure-Python py3-none-any wheel with no compiled extensions, safe to ship on every platform. Declaring it also wires it into the _verify_core_dependencies_installed() update-repair guard, which reinstalls missing [project.dependencies] on hermes update. Adds a hermetic tomllib-parse regression test that fails before the declaration and passes after. Fixes #40503 * test(deps): make packaging dep-name extraction PEP 508-robust Address Copilot review on #40522: the inline name-extraction only handled ==, >=, [ and ; and could mis-parse valid requirement strings using <=, ~=, !=, <, > or a direct reference (name @ url). Factor a _distribution_name helper that drops markers, direct-reference URLs and extras, then strips any version operator via regex, so a future dep declared with any PEP 508 specifier shape is matched correctly. --------- Co-authored-by: briandevans <252620095+briandevans@users.noreply.github.com>
…ere (NousResearch#40522) * fix(deps): declare packaging as a core dependency so it ships everywhere packaging is imported directly on three production paths but was never declared in [project.dependencies], so it only reached users transitively (pip/uv pull it for other tools). The slim official Docker image ships without it, where each try/except-ImportError fallback silently degrades: - plugins/memory/hindsight/__init__.py (_meets_minimum_version) returns False when packaging is absent, disabling update_mode='append' so every session leaks separate Hindsight documents (the reported NousResearch#40503 symptom). - tools/lazy_deps.py (_is_satisfied) falls back to "installed counts as satisfied", defeating every version-constraint check on lazy extras. - hermes_cli/main.py drops to naive name==version requirement parsing. Promote it to a declared core dep pinned to packaging==26.0 — the exact version already resolved in uv.lock, so there is zero resolution churn (the lock change is two edge annotations marking it transitive->direct). It is a pure-Python py3-none-any wheel with no compiled extensions, safe to ship on every platform. Declaring it also wires it into the _verify_core_dependencies_installed() update-repair guard, which reinstalls missing [project.dependencies] on hermes update. Adds a hermetic tomllib-parse regression test that fails before the declaration and passes after. Fixes NousResearch#40503 * test(deps): make packaging dep-name extraction PEP 508-robust Address Copilot review on NousResearch#40522: the inline name-extraction only handled ==, >=, [ and ; and could mis-parse valid requirement strings using <=, ~=, !=, <, > or a direct reference (name @ url). Factor a _distribution_name helper that drops markers, direct-reference URLs and extras, then strips any version operator via regex, so a future dep declared with any PEP 508 specifier shape is matched correctly. --------- Co-authored-by: briandevans <252620095+briandevans@users.noreply.github.com>
…ere (NousResearch#40522) * fix(deps): declare packaging as a core dependency so it ships everywhere packaging is imported directly on three production paths but was never declared in [project.dependencies], so it only reached users transitively (pip/uv pull it for other tools). The slim official Docker image ships without it, where each try/except-ImportError fallback silently degrades: - plugins/memory/hindsight/__init__.py (_meets_minimum_version) returns False when packaging is absent, disabling update_mode='append' so every session leaks separate Hindsight documents (the reported NousResearch#40503 symptom). - tools/lazy_deps.py (_is_satisfied) falls back to "installed counts as satisfied", defeating every version-constraint check on lazy extras. - hermes_cli/main.py drops to naive name==version requirement parsing. Promote it to a declared core dep pinned to packaging==26.0 — the exact version already resolved in uv.lock, so there is zero resolution churn (the lock change is two edge annotations marking it transitive->direct). It is a pure-Python py3-none-any wheel with no compiled extensions, safe to ship on every platform. Declaring it also wires it into the _verify_core_dependencies_installed() update-repair guard, which reinstalls missing [project.dependencies] on hermes update. Adds a hermetic tomllib-parse regression test that fails before the declaration and passes after. Fixes NousResearch#40503 * test(deps): make packaging dep-name extraction PEP 508-robust Address Copilot review on NousResearch#40522: the inline name-extraction only handled ==, >=, [ and ; and could mis-parse valid requirement strings using <=, ~=, !=, <, > or a direct reference (name @ url). Factor a _distribution_name helper that drops markers, direct-reference URLs and extras, then strips any version operator via regex, so a future dep declared with any PEP 508 specifier shape is matched correctly. --------- Co-authored-by: briandevans <252620095+briandevans@users.noreply.github.com>
…ere (NousResearch#40522) * fix(deps): declare packaging as a core dependency so it ships everywhere packaging is imported directly on three production paths but was never declared in [project.dependencies], so it only reached users transitively (pip/uv pull it for other tools). The slim official Docker image ships without it, where each try/except-ImportError fallback silently degrades: - plugins/memory/hindsight/__init__.py (_meets_minimum_version) returns False when packaging is absent, disabling update_mode='append' so every session leaks separate Hindsight documents (the reported NousResearch#40503 symptom). - tools/lazy_deps.py (_is_satisfied) falls back to "installed counts as satisfied", defeating every version-constraint check on lazy extras. - hermes_cli/main.py drops to naive name==version requirement parsing. Promote it to a declared core dep pinned to packaging==26.0 — the exact version already resolved in uv.lock, so there is zero resolution churn (the lock change is two edge annotations marking it transitive->direct). It is a pure-Python py3-none-any wheel with no compiled extensions, safe to ship on every platform. Declaring it also wires it into the _verify_core_dependencies_installed() update-repair guard, which reinstalls missing [project.dependencies] on hermes update. Adds a hermetic tomllib-parse regression test that fails before the declaration and passes after. Fixes NousResearch#40503 * test(deps): make packaging dep-name extraction PEP 508-robust Address Copilot review on NousResearch#40522: the inline name-extraction only handled ==, >=, [ and ; and could mis-parse valid requirement strings using <=, ~=, !=, <, > or a direct reference (name @ url). Factor a _distribution_name helper that drops markers, direct-reference URLs and extras, then strips any version operator via regex, so a future dep declared with any PEP 508 specifier shape is matched correctly. --------- Co-authored-by: briandevans <252620095+briandevans@users.noreply.github.com>
…ere (NousResearch#40522) * fix(deps): declare packaging as a core dependency so it ships everywhere packaging is imported directly on three production paths but was never declared in [project.dependencies], so it only reached users transitively (pip/uv pull it for other tools). The slim official Docker image ships without it, where each try/except-ImportError fallback silently degrades: - plugins/memory/hindsight/__init__.py (_meets_minimum_version) returns False when packaging is absent, disabling update_mode='append' so every session leaks separate Hindsight documents (the reported NousResearch#40503 symptom). - tools/lazy_deps.py (_is_satisfied) falls back to "installed counts as satisfied", defeating every version-constraint check on lazy extras. - hermes_cli/main.py drops to naive name==version requirement parsing. Promote it to a declared core dep pinned to packaging==26.0 — the exact version already resolved in uv.lock, so there is zero resolution churn (the lock change is two edge annotations marking it transitive->direct). It is a pure-Python py3-none-any wheel with no compiled extensions, safe to ship on every platform. Declaring it also wires it into the _verify_core_dependencies_installed() update-repair guard, which reinstalls missing [project.dependencies] on hermes update. Adds a hermetic tomllib-parse regression test that fails before the declaration and passes after. Fixes NousResearch#40503 * test(deps): make packaging dep-name extraction PEP 508-robust Address Copilot review on NousResearch#40522: the inline name-extraction only handled ==, >=, [ and ; and could mis-parse valid requirement strings using <=, ~=, !=, <, > or a direct reference (name @ url). Factor a _distribution_name helper that drops markers, direct-reference URLs and extras, then strips any version operator via regex, so a future dep declared with any PEP 508 specifier shape is matched correctly. --------- Co-authored-by: briandevans <252620095+briandevans@users.noreply.github.com>
…ere (NousResearch#40522) * fix(deps): declare packaging as a core dependency so it ships everywhere packaging is imported directly on three production paths but was never declared in [project.dependencies], so it only reached users transitively (pip/uv pull it for other tools). The slim official Docker image ships without it, where each try/except-ImportError fallback silently degrades: - plugins/memory/hindsight/__init__.py (_meets_minimum_version) returns False when packaging is absent, disabling update_mode='append' so every session leaks separate Hindsight documents (the reported NousResearch#40503 symptom). - tools/lazy_deps.py (_is_satisfied) falls back to "installed counts as satisfied", defeating every version-constraint check on lazy extras. - hermes_cli/main.py drops to naive name==version requirement parsing. Promote it to a declared core dep pinned to packaging==26.0 — the exact version already resolved in uv.lock, so there is zero resolution churn (the lock change is two edge annotations marking it transitive->direct). It is a pure-Python py3-none-any wheel with no compiled extensions, safe to ship on every platform. Declaring it also wires it into the _verify_core_dependencies_installed() update-repair guard, which reinstalls missing [project.dependencies] on hermes update. Adds a hermetic tomllib-parse regression test that fails before the declaration and passes after. Fixes NousResearch#40503 * test(deps): make packaging dep-name extraction PEP 508-robust Address Copilot review on NousResearch#40522: the inline name-extraction only handled ==, >=, [ and ; and could mis-parse valid requirement strings using <=, ~=, !=, <, > or a direct reference (name @ url). Factor a _distribution_name helper that drops markers, direct-reference URLs and extras, then strips any version operator via regex, so a future dep declared with any PEP 508 specifier shape is matched correctly. --------- Co-authored-by: briandevans <252620095+briandevans@users.noreply.github.com>
…ere (NousResearch#40522) * fix(deps): declare packaging as a core dependency so it ships everywhere packaging is imported directly on three production paths but was never declared in [project.dependencies], so it only reached users transitively (pip/uv pull it for other tools). The slim official Docker image ships without it, where each try/except-ImportError fallback silently degrades: - plugins/memory/hindsight/__init__.py (_meets_minimum_version) returns False when packaging is absent, disabling update_mode='append' so every session leaks separate Hindsight documents (the reported NousResearch#40503 symptom). - tools/lazy_deps.py (_is_satisfied) falls back to "installed counts as satisfied", defeating every version-constraint check on lazy extras. - hermes_cli/main.py drops to naive name==version requirement parsing. Promote it to a declared core dep pinned to packaging==26.0 — the exact version already resolved in uv.lock, so there is zero resolution churn (the lock change is two edge annotations marking it transitive->direct). It is a pure-Python py3-none-any wheel with no compiled extensions, safe to ship on every platform. Declaring it also wires it into the _verify_core_dependencies_installed() update-repair guard, which reinstalls missing [project.dependencies] on hermes update. Adds a hermetic tomllib-parse regression test that fails before the declaration and passes after. Fixes NousResearch#40503 * test(deps): make packaging dep-name extraction PEP 508-robust Address Copilot review on NousResearch#40522: the inline name-extraction only handled ==, >=, [ and ; and could mis-parse valid requirement strings using <=, ~=, !=, <, > or a direct reference (name @ url). Factor a _distribution_name helper that drops markers, direct-reference URLs and extras, then strips any version operator via regex, so a future dep declared with any PEP 508 specifier shape is matched correctly. --------- Co-authored-by: briandevans <252620095+briandevans@users.noreply.github.com>
…ere (NousResearch#40522) * fix(deps): declare packaging as a core dependency so it ships everywhere packaging is imported directly on three production paths but was never declared in [project.dependencies], so it only reached users transitively (pip/uv pull it for other tools). The slim official Docker image ships without it, where each try/except-ImportError fallback silently degrades: - plugins/memory/hindsight/__init__.py (_meets_minimum_version) returns False when packaging is absent, disabling update_mode='append' so every session leaks separate Hindsight documents (the reported NousResearch#40503 symptom). - tools/lazy_deps.py (_is_satisfied) falls back to "installed counts as satisfied", defeating every version-constraint check on lazy extras. - hermes_cli/main.py drops to naive name==version requirement parsing. Promote it to a declared core dep pinned to packaging==26.0 — the exact version already resolved in uv.lock, so there is zero resolution churn (the lock change is two edge annotations marking it transitive->direct). It is a pure-Python py3-none-any wheel with no compiled extensions, safe to ship on every platform. Declaring it also wires it into the _verify_core_dependencies_installed() update-repair guard, which reinstalls missing [project.dependencies] on hermes update. Adds a hermetic tomllib-parse regression test that fails before the declaration and passes after. Fixes NousResearch#40503 * test(deps): make packaging dep-name extraction PEP 508-robust Address Copilot review on NousResearch#40522: the inline name-extraction only handled ==, >=, [ and ; and could mis-parse valid requirement strings using <=, ~=, !=, <, > or a direct reference (name @ url). Factor a _distribution_name helper that drops markers, direct-reference URLs and extras, then strips any version operator via regex, so a future dep declared with any PEP 508 specifier shape is matched correctly. --------- Co-authored-by: briandevans <252620095+briandevans@users.noreply.github.com>
…ere (NousResearch#40522) * fix(deps): declare packaging as a core dependency so it ships everywhere packaging is imported directly on three production paths but was never declared in [project.dependencies], so it only reached users transitively (pip/uv pull it for other tools). The slim official Docker image ships without it, where each try/except-ImportError fallback silently degrades: - plugins/memory/hindsight/__init__.py (_meets_minimum_version) returns False when packaging is absent, disabling update_mode='append' so every session leaks separate Hindsight documents (the reported NousResearch#40503 symptom). - tools/lazy_deps.py (_is_satisfied) falls back to "installed counts as satisfied", defeating every version-constraint check on lazy extras. - hermes_cli/main.py drops to naive name==version requirement parsing. Promote it to a declared core dep pinned to packaging==26.0 — the exact version already resolved in uv.lock, so there is zero resolution churn (the lock change is two edge annotations marking it transitive->direct). It is a pure-Python py3-none-any wheel with no compiled extensions, safe to ship on every platform. Declaring it also wires it into the _verify_core_dependencies_installed() update-repair guard, which reinstalls missing [project.dependencies] on hermes update. Adds a hermetic tomllib-parse regression test that fails before the declaration and passes after. Fixes NousResearch#40503 * test(deps): make packaging dep-name extraction PEP 508-robust Address Copilot review on NousResearch#40522: the inline name-extraction only handled ==, >=, [ and ; and could mis-parse valid requirement strings using <=, ~=, !=, <, > or a direct reference (name @ url). Factor a _distribution_name helper that drops markers, direct-reference URLs and extras, then strips any version operator via regex, so a future dep declared with any PEP 508 specifier shape is matched correctly. --------- Co-authored-by: briandevans <252620095+briandevans@users.noreply.github.com>
What does this PR do?
packagingis imported directly on three production code paths but is not declared anywhere in[project.dependencies]— it only reached users transitively (pip/uv pull it for other tools). The slim official Docker image (nousresearch/hermes-agent) ships without it, and there eachtry/except ImportErrorfallback silently degrades rather than erroring:plugins/memory/hindsight/__init__.py(_meets_minimum_version) returnsFalsewhenpackagingis absent, which disablesupdate_mode='append', so every session leaks separate Hindsight documents — the reported Missingpackagingmodule in Docker image causes Hindsight version comparison to fail #40503 symptom.tools/lazy_deps.py(_is_satisfied) falls back to "installed counts as satisfied", defeating every version-constraint check on lazy extras.hermes_cli/main.pydrops to naivename==versionrequirement parsing.This promotes
packagingto a declared core dep pinned topackaging==26.0— the exact version already resolved inuv.lock— so there is zero resolution churn (the lock change is two edge annotations marking it transitive→direct). It's a pure-Pythonpy3-none-anywheel with no compiled extensions, so it's safe to ship on every platform — the same fix-shape as the mergedMarkdownpromotion (commit b434f8c, #32486). Declaring it also wires it into the_verify_core_dependencies_installed()update-repair guard, which reinstalls missing[project.dependencies]onhermes update.Related Issue
Fixes #40503
Type of Change
Changes Made
pyproject.toml: add"packaging==26.0"to[project.dependencies]with an explanatory comment.uv.lock: regenerated viauv lock— two edge annotations promotingpackagingfrom transitive to a direct dependency ofhermes-agent(no version or resolution change).tests/test_packaging_metadata.py: addtest_packaging_declared_as_core_dependency, a hermetictomllib-parse regression guard.How to Test
pyproject.tomlchange,test_packaging_declared_as_core_dependencyfails withAssertionError: packaging ... must be a declared core dependency.packagingis installed:Checklist
Code
fix(scope):,feat(scope):, etc.)Documentation & Housekeeping
docs/, docstrings) — or N/Acli-config.yaml.exampleif I added/changed config keys — or N/ACONTRIBUTING.mdorAGENTS.mdif I changed architecture or workflows — or N/Apy3-none-anywheel, no platform constraints