diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..f0b05af --- /dev/null +++ b/.env.example @@ -0,0 +1,3 @@ +# Optional. Echo mode needs no environment variables. +OPENAI_API_KEY=replace-with-a-provider-key +SAMSARIX_LLM_BASE_URL=https://api.openai.com/v1 diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..bb15852 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,117 @@ +name: CI + +on: + push: + branches: [main] + pull_request: + +permissions: + contents: read + +jobs: + quality: + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + python-version: ["3.11", "3.12", "3.13", "3.14"] + steps: + - uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6 + - uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6 + with: + python-version: ${{ matrix.python-version }} + cache: pip + - name: Install product and development tools + run: python -m pip install -e ".[dev]" + - name: Lint + run: python -m ruff check src tests examples + - name: Format check + run: python -m ruff format --check src tests examples + - name: Type check + run: python -m mypy src/samsarix_agent_engine + - name: Static security check + run: python -m bandit -r src/samsarix_agent_engine -q + - name: Test + run: >- + python -m pytest + --cov=samsarix_agent_engine + --cov-report=term-missing + --cov-report=xml + + dependency-audit: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6 + - uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6 + with: + python-version: "3.11" + cache: pip + - name: Install audit tool + run: python -m pip install "pip-audit>=2.10,<3" + - name: Audit runtime requirements + run: python -m pip_audit -r requirements.txt + + package: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6 + - uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6 + with: + python-version: "3.11" + cache: pip + - name: Install build tools + run: python -m pip install "build>=1.2,<2" "twine>=6,<7" + - name: Build distributions + run: python -m build + - name: Check metadata + run: python -m twine check dist/* + - name: Reject legacy package contents + shell: python + run: | + import pathlib + import sys + import tarfile + import zipfile + + wheel = next(pathlib.Path("dist").glob("*.whl")) + with zipfile.ZipFile(wheel) as archive: + names = archive.namelist() + required = { + "samsarix_agent_engine/__init__.py", + "samsarix_agent_engine/py.typed", + } + missing = required.difference(names) + forbidden = [ + name + for name in names + if name.startswith(("agents/", "services/", "helix_llm_agent_engine/")) + ] + sdist = next(pathlib.Path("dist").glob("*.tar.gz")) + with tarfile.open(sdist) as archive: + sdist_names = archive.getnames() + sdist_required_suffixes = { + "/CITATION.cff", + "/LICENSING.md", + "/docs/GETTING_STARTED.md", + "/examples/basic_agent.py", + } + missing_sdist = { + suffix + for suffix in sdist_required_suffixes + if not any(name.endswith(suffix) for name in sdist_names) + } + forbidden.extend( + name + for name in sdist_names + if len(name.split("/")) > 1 and name.split("/")[1] in {"agents", "services"} + ) + if missing or missing_sdist or forbidden: + print("Missing required wheel files:", *sorted(missing), sep="\n", file=sys.stderr) + print( + "Missing required sdist files:", + *sorted(missing_sdist), + sep="\n", + file=sys.stderr, + ) + print("Forbidden files entered artifacts:", *forbidden, sep="\n", file=sys.stderr) + raise SystemExit(1) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..e33f92f --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,115 @@ +name: Publish to PyPI + +on: + release: + types: [published] + +permissions: + contents: read + +concurrency: + group: pypi-${{ github.event.release.tag_name }} + cancel-in-progress: false + +jobs: + build: + name: Build release artifacts + runs-on: ubuntu-latest + timeout-minutes: 15 + steps: + - uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6 + with: + ref: ${{ github.event.release.tag_name }} + - uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6 + with: + python-version: "3.11" + cache: pip + - name: Verify release tag matches package version + env: + RELEASE_TAG: ${{ github.event.release.tag_name }} + shell: python + run: | + import os + import pathlib + import tomllib + + metadata = tomllib.loads(pathlib.Path("pyproject.toml").read_text(encoding="utf-8")) + expected = f"v{metadata['project']['version']}" + actual = os.environ["RELEASE_TAG"] + if actual != expected: + raise SystemExit(f"release tag {actual!r} must equal {expected!r}") + - name: Install release tools + run: python -m pip install "build==1.5.0" "twine==6.2.0" + - name: Build distributions + run: python -m build + - name: Verify metadata + run: python -m twine check dist/* + - name: Verify artifact contents + shell: python + run: | + import pathlib + import tarfile + import zipfile + + wheel = next(pathlib.Path("dist").glob("*.whl")) + with zipfile.ZipFile(wheel) as archive: + wheel_names = archive.namelist() + required_wheel = { + "samsarix_agent_engine/__init__.py", + "samsarix_agent_engine/py.typed", + } + missing_wheel = required_wheel.difference(wheel_names) + forbidden = [ + name + for name in wheel_names + if name.startswith(("agents/", "services/", "helix_llm_agent_engine/")) + ] + + sdist = next(pathlib.Path("dist").glob("*.tar.gz")) + with tarfile.open(sdist) as archive: + sdist_names = archive.getnames() + required_sdist = { + "/CITATION.cff", + "/LICENSING.md", + "/docs/GETTING_STARTED.md", + "/examples/basic_agent.py", + } + missing_sdist = { + suffix + for suffix in required_sdist + if not any(name.endswith(suffix) for name in sdist_names) + } + forbidden.extend( + name + for name in sdist_names + if len(name.split("/")) > 1 and name.split("/")[1] in {"agents", "services"} + ) + if missing_wheel or missing_sdist or forbidden: + raise SystemExit( + f"artifact contract failed: missing wheel={sorted(missing_wheel)}, " + f"missing sdist={sorted(missing_sdist)}, forbidden={forbidden}" + ) + - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 + with: + name: python-distributions + path: dist/ + if-no-files-found: error + retention-days: 7 + + publish: + name: Publish distributions + needs: build + runs-on: ubuntu-latest + timeout-minutes: 10 + environment: + name: pypi + url: https://pypi.org/p/samsarix-agent-engine + permissions: + id-token: write + steps: + - uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4 + with: + name: python-distributions + path: dist/ + - name: Publish with PyPI Trusted Publishing + uses: pypa/gh-action-pypi-publish@ba38be9e461d3875417946c167d0b5f3d385a247 # release/v1 diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..d57cb47 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,28 @@ +# Changelog + +All notable changes will be documented here. The project uses semantic versioning +once releases begin. + +## 0.1.0 - Unreleased + +### Added + +- Standalone `src/` package with a deliberate public API. +- Bounded in-memory agents and sequential orchestration. +- Deterministic offline echo provider. +- Bounded OpenAI-compatible HTTP provider. +- Non-interactive CLI with JSON output and meaningful exit codes. +- Real unit, integration, packaging, lint, type, and security checks. +- Productization, security, legacy-boundary, setup, and release documentation. +- Samsarix package, import, CLI, environment-variable, and company branding. +- MPL-2.0 licensing, source notices, trademark guidance, and citation metadata. +- PEP 561 `py.typed` marker for downstream type checkers. +- Environment-protected PyPI Trusted Publishing workflow with isolated build and + publish jobs, artifact guards, tag/version matching, and no long-lived token. + +### Removed + +- Orphaned root LLM modules that required private `helix-unified` imports. +- Mock-only tests and examples for APIs that did not exist. +- Unverifiable model-pricing, free-credit, and production-readiness claims. +- Contradictory BSL/proprietary license files for the current source tree. diff --git a/CITATION.cff b/CITATION.cff new file mode 100644 index 0000000..addbaee --- /dev/null +++ b/CITATION.cff @@ -0,0 +1,13 @@ +cff-version: 1.2.0 +message: "If you use this software, please cite it using the metadata below." +title: "Samsarix Agent Engine" +type: software +version: 0.1.0 +authors: + - name: "Samsarix LLC" +contact: + - name: "Samsarix LLC" + email: "contact@samsarix.com" +repository-code: "https://github.com/Deathcharge/helix-hub-shared" +url: "https://www.samsarix.com" +license: MPL-2.0 diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index 5afd3b5..50c45f6 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -29,7 +29,9 @@ Unacceptable behaviors include: ## Reporting -If you experience or witness unacceptable behavior, please report it by contacting the project maintainers at [conduct@helix-hub-shared.dev](mailto:conduct@helix-hub-shared.dev). +If you experience or witness unacceptable behavior, email contact@samsarix.com +with `[CONDUCT]` in the subject. Do not include sensitive conduct reports in a +public issue. All reports will be reviewed and investigated promptly. The project team is committed to maintaining confidentiality with regard to the reporter of an incident. @@ -53,4 +55,4 @@ This Code of Conduct is adapted from the Contributor Covenant, version 2.0, avai ## Questions? -If you have questions about this Code of Conduct, please open an issue or contact the maintainers. +If you have questions about this Code of Conduct, email contact@samsarix.com. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index a1e9884..bc5be27 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,108 +1,59 @@ -# Contributing to Helix Hub Shared +# Contributing -We welcome contributions to the Helix Hub Shared infrastructure! This guide explains how to get started. +Contributions should keep the supported product small, independently installable, +and free of runtime dependencies on private Samsarix repositories. -## Getting Started - -1. Fork the repository -2. Clone your fork: `git clone https://github.com/YOUR_USERNAME/helix-hub-shared.git` -3. Create a branch: `git checkout -b feature/your-feature` -4. Make changes and commit: `git commit -am 'Add feature'` -5. Push to branch: `git push origin feature/your-feature` -6. Submit a pull request - -## Development Setup +## Setup ```bash git clone https://github.com/Deathcharge/helix-hub-shared.git cd helix-hub-shared -pip install -e ".[dev]" -pip install -r requirements-test.txt +python -m venv .venv +# macOS/Linux: source .venv/bin/activate +# Windows PowerShell: .venv\Scripts\Activate.ps1 +python -m pip install --upgrade pip +python -m pip install -e ".[dev]" ``` -## Running Tests +## Required checks ```bash -pytest tests/ -v -pytest tests/ --cov -pytest tests/ -m engine # Run specific marker -pytest tests/ -m integration # Run integration tests -``` - -## Coding Standards - -- Follow PEP 8 -- Use type hints -- Write comprehensive docstrings -- Keep lines under 100 characters -- Use meaningful variable names -- Add tests for new features (minimum 80% coverage) - -## Documentation - -Update documentation for new features: - -- Update README.md for major changes -- Update GETTING_STARTED.md for new patterns -- Add examples for new features -- Update API documentation -- Add inline code comments for complex logic - -## Pull Request Process - -1. Ensure all tests pass: `pytest tests/` -2. Add tests for new functionality -3. Update documentation as needed -4. Provide a clear description of changes -5. Reference any related issues -6. Wait for review and feedback - -## Code Review Guidelines - -- Be respectful and constructive -- Focus on the code, not the person -- Suggest improvements, don't demand -- Acknowledge good work -- Help reviewees improve - -## Testing Requirements - -- Minimum 80% code coverage -- All tests must pass -- Add tests for edge cases -- Test error conditions -- Include integration tests - -## Commit Message Format - +python -m ruff check src tests examples +python -m ruff format --check src tests examples +python -m mypy src/samsarix_agent_engine +python -m bandit -r src/samsarix_agent_engine -q +python -m pip_audit -r requirements.txt +python -m pytest --cov=samsarix_agent_engine --cov-report=term-missing +python -m build +python -m twine check dist/* ``` -: - +Add tests that exercise implementation, not mocks of the object under test. Network +tests must use deterministic local transports or fixtures and must not require paid +credentials. -