lynx-agent 1.0.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- lynx_agent-1.0.0/.gitignore +42 -0
- lynx_agent-1.0.0/CHANGELOG.md +92 -0
- lynx_agent-1.0.0/CODE_OF_CONDUCT.md +35 -0
- lynx_agent-1.0.0/CONTRIBUTING.md +89 -0
- lynx_agent-1.0.0/LICENSE +17 -0
- lynx_agent-1.0.0/Makefile +23 -0
- lynx_agent-1.0.0/PKG-INFO +343 -0
- lynx_agent-1.0.0/README.md +268 -0
- lynx_agent-1.0.0/SECURITY.md +51 -0
- lynx_agent-1.0.0/benchmarks/README.md +56 -0
- lynx_agent-1.0.0/benchmarks/bench_end_to_end.py +97 -0
- lynx_agent-1.0.0/benchmarks/bench_pdp.py +82 -0
- lynx_agent-1.0.0/docs/00-execution-plan.md +201 -0
- lynx_agent-1.0.0/docs/01-data-model.md +371 -0
- lynx_agent-1.0.0/docs/02-policy-language.md +318 -0
- lynx_agent-1.0.0/docs/03-sdk-and-cli.md +290 -0
- lynx_agent-1.0.0/docs/concepts.md +176 -0
- lynx_agent-1.0.0/docs/cookbook.md +294 -0
- lynx_agent-1.0.0/docs/faq.md +158 -0
- lynx_agent-1.0.0/docs/getting-started.md +147 -0
- lynx_agent-1.0.0/docs/threat-model.md +132 -0
- lynx_agent-1.0.0/docs/why-lynx.md +68 -0
- lynx_agent-1.0.0/examples/01_hello_allow.py +131 -0
- lynx_agent-1.0.0/examples/02_block_dangerous.py +164 -0
- lynx_agent-1.0.0/examples/03_preview_writes.py +197 -0
- lynx_agent-1.0.0/examples/04_human_approval.py +190 -0
- lynx_agent-1.0.0/examples/05_real_llm_blocked.py +197 -0
- lynx_agent-1.0.0/examples/06_compliance_audit.py +202 -0
- lynx_agent-1.0.0/examples/07_refund_workflow.py +176 -0
- lynx_agent-1.0.0/examples/08_sql_transform.py +150 -0
- lynx_agent-1.0.0/examples/09_fastapi_service.py +235 -0
- lynx_agent-1.0.0/examples/10_devops_assistant.py +263 -0
- lynx_agent-1.0.0/examples/11_flask_service.py +209 -0
- lynx_agent-1.0.0/examples/12_django_service.py +246 -0
- lynx_agent-1.0.0/examples/README.md +98 -0
- lynx_agent-1.0.0/examples/policies/devops.yaml +103 -0
- lynx_agent-1.0.0/examples/policies/refund.yaml +55 -0
- lynx_agent-1.0.0/examples/policies/sql-transform.yaml +37 -0
- lynx_agent-1.0.0/pyproject.toml +173 -0
- lynx_agent-1.0.0/src/lynx/__init__.py +60 -0
- lynx_agent-1.0.0/src/lynx/adapters/__init__.py +1 -0
- lynx_agent-1.0.0/src/lynx/adapters/anthropic_sdk.py +174 -0
- lynx_agent-1.0.0/src/lynx/adapters/crewai_adapter.py +71 -0
- lynx_agent-1.0.0/src/lynx/adapters/langgraph_adapter.py +80 -0
- lynx_agent-1.0.0/src/lynx/adapters/mcp.py +93 -0
- lynx_agent-1.0.0/src/lynx/adapters/openai_sdk.py +113 -0
- lynx_agent-1.0.0/src/lynx/cli/__init__.py +1 -0
- lynx_agent-1.0.0/src/lynx/cli/main.py +382 -0
- lynx_agent-1.0.0/src/lynx/core/__init__.py +1 -0
- lynx_agent-1.0.0/src/lynx/core/mediator.py +228 -0
- lynx_agent-1.0.0/src/lynx/core/policy.py +514 -0
- lynx_agent-1.0.0/src/lynx/core/scheduler.py +484 -0
- lynx_agent-1.0.0/src/lynx/core/types.py +327 -0
- lynx_agent-1.0.0/src/lynx/decorators.py +109 -0
- lynx_agent-1.0.0/src/lynx/observability.py +106 -0
- lynx_agent-1.0.0/src/lynx/policy.py +31 -0
- lynx_agent-1.0.0/src/lynx/py.typed +0 -0
- lynx_agent-1.0.0/src/lynx/runtime.py +184 -0
- lynx_agent-1.0.0/src/lynx/sandbox.py +130 -0
- lynx_agent-1.0.0/src/lynx/sdk.py +51 -0
- lynx_agent-1.0.0/src/lynx/shadows/__init__.py +33 -0
- lynx_agent-1.0.0/src/lynx/shadows/filesystem.py +35 -0
- lynx_agent-1.0.0/src/lynx/shadows/http.py +44 -0
- lynx_agent-1.0.0/src/lynx/shadows/shell.py +61 -0
- lynx_agent-1.0.0/src/lynx/shadows/sql.py +56 -0
- lynx_agent-1.0.0/src/lynx/stores/__init__.py +5 -0
- lynx_agent-1.0.0/src/lynx/stores/postgres.py +271 -0
- lynx_agent-1.0.0/src/lynx/stores/sqlite.py +467 -0
- lynx_agent-1.0.0/tests/__init__.py +0 -0
- lynx_agent-1.0.0/tests/test_adapters.py +159 -0
- lynx_agent-1.0.0/tests/test_audit_chain.py +71 -0
- lynx_agent-1.0.0/tests/test_cli.py +49 -0
- lynx_agent-1.0.0/tests/test_end_to_end.py +116 -0
- lynx_agent-1.0.0/tests/test_framework_adapters.py +69 -0
- lynx_agent-1.0.0/tests/test_mediator.py +146 -0
- lynx_agent-1.0.0/tests/test_policy.py +169 -0
- lynx_agent-1.0.0/tests/test_resume.py +114 -0
- lynx_agent-1.0.0/tests/test_sandbox.py +54 -0
- lynx_agent-1.0.0/tests/test_security.py +55 -0
- lynx_agent-1.0.0/tests/test_shadows.py +99 -0
- lynx_agent-1.0.0/tests/test_types.py +51 -0
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
__pycache__/
|
|
2
|
+
*.py[cod]
|
|
3
|
+
*.egg-info/
|
|
4
|
+
build/
|
|
5
|
+
dist/
|
|
6
|
+
.eggs/
|
|
7
|
+
|
|
8
|
+
.pytest_cache/
|
|
9
|
+
.mypy_cache/
|
|
10
|
+
.ruff_cache/
|
|
11
|
+
.coverage
|
|
12
|
+
htmlcov/
|
|
13
|
+
|
|
14
|
+
.venv/
|
|
15
|
+
venv/
|
|
16
|
+
env/
|
|
17
|
+
|
|
18
|
+
.lynx/
|
|
19
|
+
*.db
|
|
20
|
+
*.db-journal
|
|
21
|
+
*.db-wal
|
|
22
|
+
*.db-shm
|
|
23
|
+
|
|
24
|
+
.DS_Store
|
|
25
|
+
.idea/
|
|
26
|
+
.vscode/
|
|
27
|
+
|
|
28
|
+
# Locally generated by `lynx init` in the repo root — never commit.
|
|
29
|
+
/policy.yaml
|
|
30
|
+
/lynx.toml
|
|
31
|
+
/demo-workspace/
|
|
32
|
+
/data/
|
|
33
|
+
|
|
34
|
+
# Wheel/sdist build outputs
|
|
35
|
+
dist/
|
|
36
|
+
*.whl
|
|
37
|
+
*.tar.gz
|
|
38
|
+
|
|
39
|
+
# Internal working docs — research notes, planning, scratch.
|
|
40
|
+
# Never gets committed; see internal/README.md for what lives there.
|
|
41
|
+
/internal/
|
|
42
|
+
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to Lynx will be documented here. The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/); versioning follows [SemVer](https://semver.org/spec/v2.0.0.html).
|
|
4
|
+
|
|
5
|
+
## [Unreleased]
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
- (nothing yet — open a PR!)
|
|
9
|
+
|
|
10
|
+
### Changed
|
|
11
|
+
- (nothing yet)
|
|
12
|
+
|
|
13
|
+
### Fixed
|
|
14
|
+
- (nothing yet)
|
|
15
|
+
|
|
16
|
+
## [1.0.0] — 2026-06-09
|
|
17
|
+
|
|
18
|
+
First public release. All of the below shipped together; this is the surface area covered by the v1.0 SemVer commitment.
|
|
19
|
+
|
|
20
|
+
### Core
|
|
21
|
+
|
|
22
|
+
- Kernel types: `Task`, `Run`, `Step`, `ActionRequest`, `Decision`, `AuditEvent`, `ToolMetadata`, `ExecutionContext`, `Principal`, `Budget`, `ModelCall`, `ActionResult`.
|
|
23
|
+
- Policy compiler + Policy Decision Point (PDP) — pure, deterministic, content-addressed bundles.
|
|
24
|
+
- Action Mediator (PEP) — five verdicts: `allow`, `deny`, `dry_run`, `approve_required`, `transform`.
|
|
25
|
+
- Scheduler with pre-execution checkpointing; crash-resume + approval-resume both correctly implemented.
|
|
26
|
+
- Hash-chained audit log with tamper detection (`lynx audit verify`).
|
|
27
|
+
|
|
28
|
+
### Public SDK
|
|
29
|
+
|
|
30
|
+
- `@tool` decorator + `.shadow` for dry-run twins.
|
|
31
|
+
- `Runtime.run / resume / replay / approve / deny`.
|
|
32
|
+
- `Agent` protocol — one method, `async def step(conversation) -> ToolCall | FinalAnswer`.
|
|
33
|
+
|
|
34
|
+
### Adapters
|
|
35
|
+
|
|
36
|
+
- `lynx.adapters.anthropic_sdk.ClaudeAgent`
|
|
37
|
+
- `lynx.adapters.openai_sdk.OpenAIAgent`
|
|
38
|
+
- `lynx.adapters.langgraph_adapter.LangGraphAgent`
|
|
39
|
+
- `lynx.adapters.crewai_adapter.CrewAIAgent`
|
|
40
|
+
- `lynx.adapters.mcp.register_mcp_server`
|
|
41
|
+
|
|
42
|
+
### Storage
|
|
43
|
+
|
|
44
|
+
- `lynx.stores.sqlite.SQLiteStore` — full implementation, default.
|
|
45
|
+
- `lynx.stores.postgres.PostgresStore` — production backend (Tasks / Runs / Audit; Steps + Approvals follow same translation pattern).
|
|
46
|
+
|
|
47
|
+
### Shadow library
|
|
48
|
+
|
|
49
|
+
- `lynx.shadows.shell_shadow`
|
|
50
|
+
- `lynx.shadows.write_file_shadow`, `delete_file_shadow`
|
|
51
|
+
- `lynx.shadows.sql_shadow`
|
|
52
|
+
- `lynx.shadows.http_shadow` (with built-in `Authorization` header redaction)
|
|
53
|
+
|
|
54
|
+
### Sandbox
|
|
55
|
+
|
|
56
|
+
- `lynx.sandbox.run_in_subprocess` — POSIX subprocess sandbox with `RLIMIT_CPU`, `RLIMIT_AS`, stripped env, timeout.
|
|
57
|
+
|
|
58
|
+
### Observability
|
|
59
|
+
|
|
60
|
+
- `lynx.observability.enable_prometheus`
|
|
61
|
+
- `lynx.observability.enable_otel`
|
|
62
|
+
|
|
63
|
+
### CLI
|
|
64
|
+
|
|
65
|
+
- `lynx init / run / resume / ps / trace / approvals / approve / deny / audit verify / audit export / policy lint / policy bundle-id`
|
|
66
|
+
|
|
67
|
+
### Documentation
|
|
68
|
+
|
|
69
|
+
- Onboarding: `why-lynx.md`, `getting-started.md`, `concepts.md`, `cookbook.md`, `faq.md`.
|
|
70
|
+
- Reference: `01-data-model.md`, `02-policy-language.md`, `03-sdk-and-cli.md`.
|
|
71
|
+
- Threat model: `threat-model.md` (STRIDE-style).
|
|
72
|
+
- 12 runnable examples: simple → complex → advanced → complete → integrations (FastAPI / Flask / Django).
|
|
73
|
+
|
|
74
|
+
### Quality bar
|
|
75
|
+
|
|
76
|
+
- 57 tests, ~1.2s suite, 9-job CI matrix (Linux / macOS / Windows × Python 3.11 / 3.12 / 3.13) + coverage.
|
|
77
|
+
- ruff lint + format clean. `mypy --strict` runs (currently soft-gated on the test matrix; will be hard-gated in v1.1).
|
|
78
|
+
- Apache-2.0 licensed. PEP 561 typed (`py.typed`). PEP 639 license metadata.
|
|
79
|
+
|
|
80
|
+
### Public API surface guaranteed by SemVer
|
|
81
|
+
|
|
82
|
+
- `lynx.tool`, `lynx.runtime`, `lynx.Runtime`, `lynx.shadow`, `lynx.allow/deny/dry_run/approve_required/transform/rule`.
|
|
83
|
+
- `lynx.Agent`, `lynx.Message`, `lynx.ToolCall`, `lynx.FinalAnswer`, `lynx.AgentAction`.
|
|
84
|
+
- `lynx.Task`, `lynx.Run`, `lynx.Step`, `lynx.ActionRequest`, `lynx.Decision`, `lynx.AuditEvent`, `lynx.Verdict`, `lynx.RunStatus`, `lynx.Principal`, `lynx.Budget`, `lynx.ToolMetadata`, `lynx.ExecutionContext`, `lynx.ModelCall`, `lynx.ActionResult`.
|
|
85
|
+
- Adapters and stores have their own public-class interfaces guaranteed.
|
|
86
|
+
- The YAML policy v1 grammar.
|
|
87
|
+
- The CLI command surface and exit codes.
|
|
88
|
+
|
|
89
|
+
Internal modules (`lynx.core.*`) are NOT part of the public API and may change in any minor release.
|
|
90
|
+
|
|
91
|
+
[Unreleased]: https://github.com/hadihonarvar/lynx/compare/v1.0.0...HEAD
|
|
92
|
+
[1.0.0]: https://github.com/hadihonarvar/lynx/releases/tag/v1.0.0
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# Contributor Covenant Code of Conduct
|
|
2
|
+
|
|
3
|
+
## Our pledge
|
|
4
|
+
|
|
5
|
+
We as members, contributors, and leaders pledge to make participation in the Lynx community a harassment-free experience for everyone, regardless of age, body size, visible or invisible disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, religion, or sexual identity and orientation.
|
|
6
|
+
|
|
7
|
+
We pledge to act and interact in ways that contribute to an open, welcoming, diverse, inclusive, and healthy community.
|
|
8
|
+
|
|
9
|
+
## Our standards
|
|
10
|
+
|
|
11
|
+
Examples of behavior that contributes to a positive environment:
|
|
12
|
+
|
|
13
|
+
- Demonstrating empathy and kindness toward other people
|
|
14
|
+
- Being respectful of differing opinions, viewpoints, and experiences
|
|
15
|
+
- Giving and gracefully accepting constructive feedback
|
|
16
|
+
- Accepting responsibility and apologizing to those affected by our mistakes, and learning from the experience
|
|
17
|
+
- Focusing on what is best not just for us as individuals, but for the overall community
|
|
18
|
+
|
|
19
|
+
Examples of unacceptable behavior:
|
|
20
|
+
|
|
21
|
+
- The use of sexualized language or imagery, and sexual attention or advances of any kind
|
|
22
|
+
- Trolling, insulting or derogatory comments, and personal or political attacks
|
|
23
|
+
- Public or private harassment
|
|
24
|
+
- Publishing others' private information, such as a physical or email address, without their explicit permission
|
|
25
|
+
- Other conduct which could reasonably be considered inappropriate in a professional setting
|
|
26
|
+
|
|
27
|
+
## Enforcement
|
|
28
|
+
|
|
29
|
+
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to the project maintainers via [GitHub Security Advisories](https://github.com/hadihonarvar/lynx/security/advisories/new) (any "Conduct" report routes to maintainers privately). All complaints will be reviewed and investigated promptly and fairly.
|
|
30
|
+
|
|
31
|
+
All community leaders are obligated to respect the privacy and security of the reporter of any incident.
|
|
32
|
+
|
|
33
|
+
## Attribution
|
|
34
|
+
|
|
35
|
+
This Code of Conduct is adapted from the [Contributor Covenant](https://www.contributor-covenant.org), version 2.1, available at https://www.contributor-covenant.org/version/2/1/code_of_conduct.html.
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# Contributing to Lynx
|
|
2
|
+
|
|
3
|
+
Thanks for considering a contribution. This document covers what to do and what to expect.
|
|
4
|
+
|
|
5
|
+
## Quick setup
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
git clone https://github.com/<your-fork>/lynx
|
|
9
|
+
cd lynx
|
|
10
|
+
python3 -m venv .venv
|
|
11
|
+
source .venv/bin/activate
|
|
12
|
+
pip install -e ".[dev]"
|
|
13
|
+
make all # fmt + lint + type + test — should be green
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## Filing issues
|
|
17
|
+
|
|
18
|
+
Open an issue using one of the [templates](.github/ISSUE_TEMPLATE/) — `bug`, `feature`, or `question`. The more reproduction info you include, the faster we can act.
|
|
19
|
+
|
|
20
|
+
## Proposing changes
|
|
21
|
+
|
|
22
|
+
1. **Open an issue first** for non-trivial changes — saves you wasted effort if the design doesn't fit. Trivial fixes (typos, small bugs, clearer error messages) can skip straight to a PR.
|
|
23
|
+
2. **Branch from `main`**, with a name like `fix/policy-regex-timeout` or `feat/postgres-store`.
|
|
24
|
+
3. **One logical change per PR.** Smaller PRs land faster.
|
|
25
|
+
4. **Tests required** for new behavior. Bug fixes should include a regression test.
|
|
26
|
+
5. **Update docs** when you change the public API or CLI surface.
|
|
27
|
+
6. **Run `make all` locally** before pushing. CI runs the same checks; saving a round-trip helps.
|
|
28
|
+
|
|
29
|
+
## Architecture rules
|
|
30
|
+
|
|
31
|
+
These are load-bearing — please don't break them:
|
|
32
|
+
|
|
33
|
+
1. **`core/` has zero I/O.** It's pure functions and state machines. If you need to do I/O, it goes in `stores/`, `adapters/`, `transports/`, or `cli/`.
|
|
34
|
+
2. **The PDP is deterministic.** No network, no clocks read inside policy evaluation, no random. Same input must always produce the same `Decision`.
|
|
35
|
+
3. **The audit chain is append-only.** If you find yourself wanting to mutate a past `AuditEvent`, you're solving the wrong problem.
|
|
36
|
+
4. **Tools must be async.** A sync tool is `asyncio.to_thread(...)` away from being async; please wrap rather than introducing sync paths into the kernel.
|
|
37
|
+
5. **No breaking changes to the public API in patch releases.** We follow SemVer strictly from v1.0 onwards.
|
|
38
|
+
|
|
39
|
+
## Adding a new adapter
|
|
40
|
+
|
|
41
|
+
The smallest viable PR for a new framework adapter:
|
|
42
|
+
|
|
43
|
+
1. `src/lynx/adapters/<framework>.py` — a class that satisfies the `Agent` protocol (`async def step(conversation) -> ToolCall | FinalAnswer`).
|
|
44
|
+
2. `tests/test_adapter_<framework>.py` — mock the framework's client; assert the message translation + tool-call extraction.
|
|
45
|
+
3. `examples/<framework>_demo.py` — a runnable demo using the new adapter.
|
|
46
|
+
4. Update the README's adapter list.
|
|
47
|
+
|
|
48
|
+
## Adding a new shadow
|
|
49
|
+
|
|
50
|
+
`src/lynx/shadows/<name>.py` — one or more `async def name_shadow(*args) -> dict` functions that return previews without side effects.
|
|
51
|
+
|
|
52
|
+
## Coding style
|
|
53
|
+
|
|
54
|
+
- Python 3.11+
|
|
55
|
+
- `ruff` for lint + format
|
|
56
|
+
- `mypy --strict` for types
|
|
57
|
+
- One blank line between methods, two between top-level definitions
|
|
58
|
+
- Docstrings on every public function — short and useful, not ceremonial
|
|
59
|
+
|
|
60
|
+
## Commit messages
|
|
61
|
+
|
|
62
|
+
Conventional Commits style:
|
|
63
|
+
|
|
64
|
+
- `fix: policy regex denial-of-service guard`
|
|
65
|
+
- `feat(stores): postgres backend`
|
|
66
|
+
- `docs: clarify approve flow`
|
|
67
|
+
- `test: cover budget exhaustion`
|
|
68
|
+
- `chore: bump ruff to 0.6`
|
|
69
|
+
|
|
70
|
+
## Release process
|
|
71
|
+
|
|
72
|
+
Maintainers only:
|
|
73
|
+
|
|
74
|
+
1. Update `CHANGELOG.md`
|
|
75
|
+
2. Bump version in `pyproject.toml` and `src/lynx/__init__.py`
|
|
76
|
+
3. `git tag vX.Y.Z` + push tag
|
|
77
|
+
4. GitHub Actions handles wheel build + PyPI publish via OIDC trusted publishing
|
|
78
|
+
|
|
79
|
+
## Code of conduct
|
|
80
|
+
|
|
81
|
+
By participating you agree to follow the [Contributor Covenant](CODE_OF_CONDUCT.md). In short: be kind, be patient, assume good intent, and call out behavior that crosses lines.
|
|
82
|
+
|
|
83
|
+
## Security issues
|
|
84
|
+
|
|
85
|
+
Do **not** open public issues for security vulnerabilities. Follow the process in [SECURITY.md](SECURITY.md).
|
|
86
|
+
|
|
87
|
+
## Licensing
|
|
88
|
+
|
|
89
|
+
By contributing, you agree your contributions will be licensed under the Apache License 2.0.
|
lynx_agent-1.0.0/LICENSE
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
Apache License
|
|
2
|
+
Version 2.0, January 2004
|
|
3
|
+
http://www.apache.org/licenses/
|
|
4
|
+
|
|
5
|
+
Copyright 2026 Lynx contributors
|
|
6
|
+
|
|
7
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
8
|
+
you may not use this file except in compliance with the License.
|
|
9
|
+
You may obtain a copy of the License at
|
|
10
|
+
|
|
11
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
12
|
+
|
|
13
|
+
Unless required by applicable law or agreed to in writing, software
|
|
14
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
15
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
16
|
+
See the License for the specific language governing permissions and
|
|
17
|
+
limitations under the License.
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
.PHONY: install test lint type fmt all clean
|
|
2
|
+
|
|
3
|
+
install:
|
|
4
|
+
pip install -e ".[dev]"
|
|
5
|
+
|
|
6
|
+
test:
|
|
7
|
+
pytest -v
|
|
8
|
+
|
|
9
|
+
lint:
|
|
10
|
+
ruff check src tests
|
|
11
|
+
|
|
12
|
+
fmt:
|
|
13
|
+
ruff format src tests
|
|
14
|
+
ruff check --fix src tests
|
|
15
|
+
|
|
16
|
+
type:
|
|
17
|
+
mypy src
|
|
18
|
+
|
|
19
|
+
all: fmt lint type test
|
|
20
|
+
|
|
21
|
+
clean:
|
|
22
|
+
rm -rf build dist *.egg-info .pytest_cache .mypy_cache .ruff_cache
|
|
23
|
+
find . -type d -name __pycache__ -exec rm -rf {} +
|
|
@@ -0,0 +1,343 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: lynx-agent
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: Framework-agnostic runtime that makes any AI agent safe and reliable enough to put in production: policy-gated execution, durable checkpointing, hash-chained audit.
|
|
5
|
+
Project-URL: Homepage, https://github.com/hadihonarvar/lynx
|
|
6
|
+
Project-URL: Repository, https://github.com/hadihonarvar/lynx
|
|
7
|
+
Project-URL: Documentation, https://github.com/hadihonarvar/lynx/tree/main/docs
|
|
8
|
+
Project-URL: Issues, https://github.com/hadihonarvar/lynx/issues
|
|
9
|
+
Project-URL: Changelog, https://github.com/hadihonarvar/lynx/blob/main/CHANGELOG.md
|
|
10
|
+
Author: Lynx contributors
|
|
11
|
+
License-Expression: Apache-2.0
|
|
12
|
+
License-File: LICENSE
|
|
13
|
+
Keywords: agentic,agents,ai,audit,claude,crewai,durable-execution,langgraph,llm,mcp,openai,policy,reliability
|
|
14
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
15
|
+
Classifier: Framework :: AsyncIO
|
|
16
|
+
Classifier: Intended Audience :: Developers
|
|
17
|
+
Classifier: Intended Audience :: Information Technology
|
|
18
|
+
Classifier: Intended Audience :: System Administrators
|
|
19
|
+
Classifier: Operating System :: OS Independent
|
|
20
|
+
Classifier: Programming Language :: Python
|
|
21
|
+
Classifier: Programming Language :: Python :: 3
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
23
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
24
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
25
|
+
Classifier: Programming Language :: Python :: Implementation :: CPython
|
|
26
|
+
Classifier: Topic :: Security
|
|
27
|
+
Classifier: Topic :: Software Development :: Libraries
|
|
28
|
+
Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
|
|
29
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
30
|
+
Classifier: Topic :: System :: Monitoring
|
|
31
|
+
Classifier: Typing :: Typed
|
|
32
|
+
Requires-Python: >=3.11
|
|
33
|
+
Requires-Dist: click>=8.1
|
|
34
|
+
Requires-Dist: msgpack>=1.0
|
|
35
|
+
Requires-Dist: python-ulid>=2.2
|
|
36
|
+
Requires-Dist: pyyaml>=6.0
|
|
37
|
+
Requires-Dist: rich>=13.7
|
|
38
|
+
Requires-Dist: typing-extensions>=4.5
|
|
39
|
+
Provides-Extra: all
|
|
40
|
+
Requires-Dist: anthropic>=0.30; extra == 'all'
|
|
41
|
+
Requires-Dist: crewai>=0.40; extra == 'all'
|
|
42
|
+
Requires-Dist: langgraph>=0.2; extra == 'all'
|
|
43
|
+
Requires-Dist: mcp>=1.0; extra == 'all'
|
|
44
|
+
Requires-Dist: openai>=1.40; extra == 'all'
|
|
45
|
+
Requires-Dist: opentelemetry-exporter-otlp-proto-http>=1.27; extra == 'all'
|
|
46
|
+
Requires-Dist: opentelemetry-sdk>=1.27; extra == 'all'
|
|
47
|
+
Requires-Dist: prometheus-client>=0.20; extra == 'all'
|
|
48
|
+
Requires-Dist: psycopg[binary]>=3.1; extra == 'all'
|
|
49
|
+
Provides-Extra: anthropic
|
|
50
|
+
Requires-Dist: anthropic>=0.30; extra == 'anthropic'
|
|
51
|
+
Provides-Extra: crewai
|
|
52
|
+
Requires-Dist: crewai>=0.40; extra == 'crewai'
|
|
53
|
+
Provides-Extra: dev
|
|
54
|
+
Requires-Dist: build>=1.2; extra == 'dev'
|
|
55
|
+
Requires-Dist: hypothesis>=6.100; extra == 'dev'
|
|
56
|
+
Requires-Dist: mypy>=1.10; extra == 'dev'
|
|
57
|
+
Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
|
|
58
|
+
Requires-Dist: pytest>=8.0; extra == 'dev'
|
|
59
|
+
Requires-Dist: ruff>=0.5; extra == 'dev'
|
|
60
|
+
Requires-Dist: twine>=5.0; extra == 'dev'
|
|
61
|
+
Provides-Extra: langgraph
|
|
62
|
+
Requires-Dist: langgraph>=0.2; extra == 'langgraph'
|
|
63
|
+
Provides-Extra: mcp
|
|
64
|
+
Requires-Dist: mcp>=1.0; extra == 'mcp'
|
|
65
|
+
Provides-Extra: openai
|
|
66
|
+
Requires-Dist: openai>=1.40; extra == 'openai'
|
|
67
|
+
Provides-Extra: otel
|
|
68
|
+
Requires-Dist: opentelemetry-exporter-otlp-proto-http>=1.27; extra == 'otel'
|
|
69
|
+
Requires-Dist: opentelemetry-sdk>=1.27; extra == 'otel'
|
|
70
|
+
Provides-Extra: postgres
|
|
71
|
+
Requires-Dist: psycopg[binary]>=3.1; extra == 'postgres'
|
|
72
|
+
Provides-Extra: prometheus
|
|
73
|
+
Requires-Dist: prometheus-client>=0.20; extra == 'prometheus'
|
|
74
|
+
Description-Content-Type: text/markdown
|
|
75
|
+
|
|
76
|
+
# Lynx
|
|
77
|
+
|
|
78
|
+
**Make any AI agent safe and reliable enough to put in production.** Open-source Python runtime that wraps any agent (LangGraph, CrewAI, OpenAI Agents SDK, Anthropic Agent SDK, or a plain Python loop) and gives you three things every team currently rebuilds from scratch:
|
|
79
|
+
|
|
80
|
+
1. **Policy-gated execution** — every tool call passes through a declarative YAML policy engine. Dry-run, deny, transform, or require human approval.
|
|
81
|
+
2. **Durable execution** — every step is checkpointed before its side effect. Crash mid-run, resume exactly where you left off, no double-execution.
|
|
82
|
+
3. **Hash-chained audit log** — content-addressed, tamper-evident, regulator-grade trail of every decision and action.
|
|
83
|
+
|
|
84
|
+
> Think *Envoy + Temporal + OPA, but for AI agents.*
|
|
85
|
+
|
|
86
|
+
---
|
|
87
|
+
|
|
88
|
+
## Why
|
|
89
|
+
|
|
90
|
+
Agent reliability is the #1 unmet need in 2026 (Gartner: 40% of agentic AI projects will fail). Capabilities are up, reliability is lagging. Real incidents from the last 12 months:
|
|
91
|
+
|
|
92
|
+
- An AI agent **deleted a developer's entire `D:` drive** when asked to clear a cache folder.
|
|
93
|
+
- An AI agent **wiped a production AWS environment**, causing a 13-hour outage.
|
|
94
|
+
- Meta's AI safety director was unable to stop her own agent from **deleting her inbox**.
|
|
95
|
+
- An n8n v2.4.7→v2.6.3 upgrade silently **broke function-calling schemas** across the user base.
|
|
96
|
+
|
|
97
|
+
Every team building agents reinvents the same scaffolding: retry logic, dry-runs, approval flows, audit trails. **Lynx is the missing layer.**
|
|
98
|
+
|
|
99
|
+
---
|
|
100
|
+
|
|
101
|
+
## Quickstart (under 2 minutes)
|
|
102
|
+
|
|
103
|
+
```bash
|
|
104
|
+
pip install lynx-agent
|
|
105
|
+
lynx init
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
```python
|
|
109
|
+
# my_agent.py
|
|
110
|
+
import asyncio
|
|
111
|
+
from lynx import tool, runtime, ToolCall, FinalAnswer, Message
|
|
112
|
+
|
|
113
|
+
@tool(cost="low", reversible=False, scope=["filesystem:write"])
|
|
114
|
+
async def shell(cmd: str) -> str:
|
|
115
|
+
proc = await asyncio.create_subprocess_shell(
|
|
116
|
+
cmd, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE
|
|
117
|
+
)
|
|
118
|
+
out, err = await proc.communicate()
|
|
119
|
+
return (out + err).decode()
|
|
120
|
+
|
|
121
|
+
@shell.shadow
|
|
122
|
+
async def _shell_shadow(cmd: str) -> dict:
|
|
123
|
+
return {"would_run": cmd}
|
|
124
|
+
|
|
125
|
+
class MyAgent:
|
|
126
|
+
"""Replace with any LLM-backed agent."""
|
|
127
|
+
async def step(self, conversation):
|
|
128
|
+
# Pretend the LLM proposed a dangerous command
|
|
129
|
+
return ToolCall(tool="shell", args={"cmd": "rm -rf /"}, call_id="c1")
|
|
130
|
+
|
|
131
|
+
async def main():
|
|
132
|
+
result = await runtime.run(
|
|
133
|
+
MyAgent(),
|
|
134
|
+
task="clean up the workspace",
|
|
135
|
+
policy="./policy.yaml",
|
|
136
|
+
)
|
|
137
|
+
print(result.status, result.final_answer)
|
|
138
|
+
|
|
139
|
+
asyncio.run(main())
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
```bash
|
|
143
|
+
$ python my_agent.py
|
|
144
|
+
$ lynx ps # list runs
|
|
145
|
+
$ lynx trace <run_id> # see every step + policy decision
|
|
146
|
+
$ lynx audit verify <run_id> # verify the hash chain
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
The default policy will **deny** the `rm -rf /` and feed the denial back to the agent as a tool result, so the agent can retry with something safer.
|
|
150
|
+
|
|
151
|
+
---
|
|
152
|
+
|
|
153
|
+
## How it works
|
|
154
|
+
|
|
155
|
+
```
|
|
156
|
+
┌────────────────────────────────────────────────┐
|
|
157
|
+
│ Agent (LangGraph / CrewAI / SDK / any) │
|
|
158
|
+
└──────────────────┬─────────────────────────────┘
|
|
159
|
+
│ proposed tool call
|
|
160
|
+
▼
|
|
161
|
+
╔════════════════════════════════════════════════╗
|
|
162
|
+
║ AGENT RUNTIME ║
|
|
163
|
+
║ ┌────────────┐ ┌────────────┐ ┌──────────┐ ║
|
|
164
|
+
║ │ Scheduler │→ │ Policy PDP │→ │ Mediator │ ║
|
|
165
|
+
║ │ (durable) │ │ (pure) │ │ (PEP) │ ║
|
|
166
|
+
║ └────────────┘ └────────────┘ └──────────┘ ║
|
|
167
|
+
║ ↓ ↓ ↓ ║
|
|
168
|
+
║ ┌──────────────────────────────────────────┐ ║
|
|
169
|
+
║ │ SQLite journal + audit chain │ ║
|
|
170
|
+
║ └──────────────────────────────────────────┘ ║
|
|
171
|
+
╚════════════════════════════════════════════════╝
|
|
172
|
+
│ approved + recorded
|
|
173
|
+
▼
|
|
174
|
+
┌────────────────────────────────────────────────┐
|
|
175
|
+
│ Real world (shell, browser, DB, AWS, etc.) │
|
|
176
|
+
└────────────────────────────────────────────────┘
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
Every action passes through the **Mediator**. The PDP returns one of five verdicts (`allow`, `deny`, `dry_run`, `approve_required`, `transform`). The Mediator dispatches accordingly. Before any side effect, a checkpoint is written. Every step emits a hash-chained audit event.
|
|
180
|
+
|
|
181
|
+
---
|
|
182
|
+
|
|
183
|
+
## Policy example
|
|
184
|
+
|
|
185
|
+
```yaml
|
|
186
|
+
# policy.yaml
|
|
187
|
+
version: 1
|
|
188
|
+
defaults:
|
|
189
|
+
on_missing_shadow: approve_required
|
|
190
|
+
on_no_match: deny
|
|
191
|
+
|
|
192
|
+
rules:
|
|
193
|
+
- id: read-only-allow
|
|
194
|
+
match: { declared.scope.contains_any: ["filesystem:read", "net:read"] }
|
|
195
|
+
decision: allow
|
|
196
|
+
|
|
197
|
+
- id: shell-rm-rf-root
|
|
198
|
+
match:
|
|
199
|
+
tool: shell
|
|
200
|
+
args.cmd.matches: '^\s*rm\s+(-[rRf]+\s+)+/(\s|$)'
|
|
201
|
+
decision: deny
|
|
202
|
+
reason: "rm -rf / is never allowed"
|
|
203
|
+
|
|
204
|
+
- id: prod-mutations-need-approval
|
|
205
|
+
match:
|
|
206
|
+
context.environment: prod
|
|
207
|
+
declared.scope.contains_any: ["filesystem:write", "db:write", "cloud:write"]
|
|
208
|
+
decision: approve_required
|
|
209
|
+
approvers: ["@oncall"]
|
|
210
|
+
|
|
211
|
+
- id: irreversible-dry-run-first
|
|
212
|
+
match: { declared.reversible: false }
|
|
213
|
+
decision: dry_run
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
Three layers, increasing expressiveness:
|
|
217
|
+
1. **YAML rules** — 80% of cases
|
|
218
|
+
2. **Predicates** — reusable named patterns
|
|
219
|
+
3. **Python escape hatch** — `@policy.rule` for edge cases
|
|
220
|
+
|
|
221
|
+
See `docs/02-policy-language.md` for the full grammar.
|
|
222
|
+
|
|
223
|
+
---
|
|
224
|
+
|
|
225
|
+
## CLI
|
|
226
|
+
|
|
227
|
+
```
|
|
228
|
+
lynx init # set up a project
|
|
229
|
+
lynx run <script> # run an agent script
|
|
230
|
+
lynx ps # list recent runs
|
|
231
|
+
lynx trace <run-id> # step-by-step trace
|
|
232
|
+
lynx approvals # list pending approvals
|
|
233
|
+
lynx approve <approval-id> # approve a pending request
|
|
234
|
+
lynx audit verify <run-id> # verify the hash chain
|
|
235
|
+
lynx audit export <run-id> # emit jsonl for compliance
|
|
236
|
+
lynx policy lint # validate policy.yaml
|
|
237
|
+
lynx policy bundle-id # content-addressed bundle ID
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
---
|
|
241
|
+
|
|
242
|
+
## Repo layout
|
|
243
|
+
|
|
244
|
+
```
|
|
245
|
+
lynx/
|
|
246
|
+
├── docs/
|
|
247
|
+
│ ├── 00-execution-plan.md ← read first
|
|
248
|
+
│ ├── 01-data-model.md
|
|
249
|
+
│ ├── 02-policy-language.md
|
|
250
|
+
│ └── 03-sdk-and-cli.md
|
|
251
|
+
├── src/lynx/
|
|
252
|
+
│ ├── core/ ← pure kernel, no I/O
|
|
253
|
+
│ │ ├── types.py
|
|
254
|
+
│ │ ├── policy.py ← PDP
|
|
255
|
+
│ │ ├── mediator.py ← PEP
|
|
256
|
+
│ │ └── scheduler.py ← step loop
|
|
257
|
+
│ ├── stores/ ← pluggable I/O
|
|
258
|
+
│ │ └── sqlite.py
|
|
259
|
+
│ ├── cli/main.py
|
|
260
|
+
│ ├── decorators.py ← @tool, @shadow
|
|
261
|
+
│ ├── policy.py ← top-level re-exports
|
|
262
|
+
│ ├── runtime.py ← public Runtime facade
|
|
263
|
+
│ └── sdk.py ← Agent protocol + Message types
|
|
264
|
+
├── tests/
|
|
265
|
+
├── examples/
|
|
266
|
+
│ └── hello_agent.py
|
|
267
|
+
└── pyproject.toml
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
**Architectural rule:** `core/` has zero I/O. All I/O lives in `stores/`, `adapters/`, and `cli/`. This is why the PDP runs in microseconds, why tests are flake-free, and why upgrading from SQLite to Postgres to a gRPC sidecar is a deployment change, not a rewrite.
|
|
271
|
+
|
|
272
|
+
---
|
|
273
|
+
|
|
274
|
+
## Roadmap
|
|
275
|
+
|
|
276
|
+
- **v0.1 (this release)** — MVP: SQLite, YAML policy, allow/deny/approve/dry_run/transform, audit chain, CLI, scripted agent example.
|
|
277
|
+
- **v0.5** — Crash-resume durability, shadow library (shell, SQL, HTTP, AWS), `replay --edit`.
|
|
278
|
+
- **v1.0** — LangGraph / CrewAI / OpenAI Agents SDK / MCP adapters; Postgres store; webhook & Slack approval transports; gRPC sidecar mode; HSM-signed audit.
|
|
279
|
+
- **v1.5** — Control plane: multi-tenant policy distribution, dashboards, cross-run analytics, governance. (Commercial layer.)
|
|
280
|
+
|
|
281
|
+
See `docs/00-execution-plan.md` for the week-by-week plan.
|
|
282
|
+
|
|
283
|
+
---
|
|
284
|
+
|
|
285
|
+
## Performance
|
|
286
|
+
|
|
287
|
+
| What | Number |
|
|
288
|
+
|------|--------|
|
|
289
|
+
| Policy evaluation (typical, ≤100 rules) | ~100 µs / call |
|
|
290
|
+
| Policy evaluation (worst case, 1000 rules) | ~1 ms / call |
|
|
291
|
+
| End-to-end overhead per step | ~3 ms (SQLite-bound) |
|
|
292
|
+
| Test suite | 57 tests in 1.1 s |
|
|
293
|
+
|
|
294
|
+
For real agents where each step is a 500 ms – 5 s LLM call, Lynx's overhead is under 1%. Reproducible numbers in [`benchmarks/`](benchmarks/README.md).
|
|
295
|
+
|
|
296
|
+
---
|
|
297
|
+
|
|
298
|
+
## Documentation
|
|
299
|
+
|
|
300
|
+
Start here if you're new:
|
|
301
|
+
|
|
302
|
+
| Doc | What it answers |
|
|
303
|
+
|-----|----------------|
|
|
304
|
+
| [Why Lynx](docs/why-lynx.md) | When should I use this? When shouldn't I? |
|
|
305
|
+
| [Getting started](docs/getting-started.md) | 5-minute walkthrough from install to first denial |
|
|
306
|
+
| [Concepts](docs/concepts.md) | Vocabulary: Tool, Policy, Verdict, Run, AuditEvent |
|
|
307
|
+
| [Policy cookbook](docs/cookbook.md) | Copy-pasteable rules for common patterns |
|
|
308
|
+
| [FAQ](docs/faq.md) | Common first-time questions |
|
|
309
|
+
|
|
310
|
+
Reference docs:
|
|
311
|
+
|
|
312
|
+
| Doc | What it covers |
|
|
313
|
+
|-----|----------------|
|
|
314
|
+
| [Data model](docs/01-data-model.md) | The six core types + SQLite schema |
|
|
315
|
+
| [Policy language](docs/02-policy-language.md) | Full YAML grammar + predicates + Python escape hatch |
|
|
316
|
+
| [SDK + CLI](docs/03-sdk-and-cli.md) | The public Python API + every CLI command |
|
|
317
|
+
| [Threat model](docs/threat-model.md) | STRIDE analysis + guarantees + non-goals |
|
|
318
|
+
| [How v0.1 was built](docs/00-execution-plan.md) | The MVP execution plan (historical) |
|
|
319
|
+
|
|
320
|
+
Examples:
|
|
321
|
+
|
|
322
|
+
| Demo | What it shows |
|
|
323
|
+
|------|--------------|
|
|
324
|
+
| [`hello_agent.py`](examples/hello_agent.py) | Minimal scripted agent — the smallest end-to-end loop |
|
|
325
|
+
| [`file_janitor.py`](examples/file_janitor.py) | Real filesystem demo: allow / deny / dry_run with real files |
|
|
326
|
+
| [`refund_agent.py`](examples/refund_agent.py) | Customer-support refund agent — demonstrates `approve_required` |
|
|
327
|
+
| [`claude_janitor.py`](examples/claude_janitor.py) | Real Claude agent driving the file demo |
|
|
328
|
+
| [`openai_janitor.py`](examples/openai_janitor.py) | Same demo, OpenAI GPT |
|
|
329
|
+
| [`fastapi_server.py`](examples/fastapi_server.py) | Drop-in FastAPI integration |
|
|
330
|
+
|
|
331
|
+
See [`examples/README.md`](examples/README.md) for the full index + how to run them.
|
|
332
|
+
|
|
333
|
+
---
|
|
334
|
+
|
|
335
|
+
## Status
|
|
336
|
+
|
|
337
|
+
Alpha. APIs may change before v1.0. Use in production at your own risk; report issues liberally.
|
|
338
|
+
|
|
339
|
+
---
|
|
340
|
+
|
|
341
|
+
## License
|
|
342
|
+
|
|
343
|
+
Apache 2.0.
|