agentplane-control-plane 0.1.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.
Files changed (40) hide show
  1. agentplane_control_plane-0.1.0/.github/workflows/ci.yml +29 -0
  2. agentplane_control_plane-0.1.0/.github/workflows/release.yml +129 -0
  3. agentplane_control_plane-0.1.0/.gitignore +14 -0
  4. agentplane_control_plane-0.1.0/CHANGELOG.md +9 -0
  5. agentplane_control_plane-0.1.0/LICENSE +21 -0
  6. agentplane_control_plane-0.1.0/PKG-INFO +95 -0
  7. agentplane_control_plane-0.1.0/README.md +57 -0
  8. agentplane_control_plane-0.1.0/RELEASING.md +80 -0
  9. agentplane_control_plane-0.1.0/config/default.yaml +37 -0
  10. agentplane_control_plane-0.1.0/control_plane/__init__.py +3 -0
  11. agentplane_control_plane-0.1.0/control_plane/app.py +494 -0
  12. agentplane_control_plane-0.1.0/control_plane/auth.py +102 -0
  13. agentplane_control_plane-0.1.0/control_plane/cli.py +42 -0
  14. agentplane_control_plane-0.1.0/control_plane/envelope_store.py +254 -0
  15. agentplane_control_plane-0.1.0/control_plane/governance_loader.py +31 -0
  16. agentplane_control_plane-0.1.0/control_plane/models.py +106 -0
  17. agentplane_control_plane-0.1.0/control_plane/remote_store.py +228 -0
  18. agentplane_control_plane-0.1.0/control_plane/serde.py +135 -0
  19. agentplane_control_plane-0.1.0/control_plane/settings.py +31 -0
  20. agentplane_control_plane-0.1.0/control_plane/store.py +583 -0
  21. agentplane_control_plane-0.1.0/control_plane/store_factory.py +36 -0
  22. agentplane_control_plane-0.1.0/control_plane/store_protocol.py +68 -0
  23. agentplane_control_plane-0.1.0/control_plane/templates.py +21 -0
  24. agentplane_control_plane-0.1.0/control_plane/web/static/logo-mark.png +0 -0
  25. agentplane_control_plane-0.1.0/control_plane/web/static/plane.css +437 -0
  26. agentplane_control_plane-0.1.0/control_plane/web/static/plane.js +409 -0
  27. agentplane_control_plane-0.1.0/control_plane/web/templates/index.html +49 -0
  28. agentplane_control_plane-0.1.0/control_plane/web.py +28 -0
  29. agentplane_control_plane-0.1.0/docs/DESIGN.md +77 -0
  30. agentplane_control_plane-0.1.0/pyproject.toml +76 -0
  31. agentplane_control_plane-0.1.0/tests/test_envelopes.py +142 -0
  32. agentplane_control_plane-0.1.0/tests/test_health.py +20 -0
  33. agentplane_control_plane-0.1.0/ui/__init__.py +0 -0
  34. agentplane_control_plane-0.1.0/ui/app.py +24 -0
  35. agentplane_control_plane-0.1.0/ui/run_detail.py +120 -0
  36. agentplane_control_plane-0.1.0/ui/store_client.py +24 -0
  37. agentplane_control_plane-0.1.0/ui/theme.py +145 -0
  38. agentplane_control_plane-0.1.0/ui/views/__init__.py +0 -0
  39. agentplane_control_plane-0.1.0/ui/views/admin.py +275 -0
  40. agentplane_control_plane-0.1.0/ui/views/dashboard.py +171 -0
@@ -0,0 +1,29 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+
8
+ concurrency:
9
+ group: ci-${{ github.ref }}
10
+ cancel-in-progress: true
11
+
12
+ jobs:
13
+ test:
14
+ runs-on: ubuntu-latest
15
+ strategy:
16
+ fail-fast: false
17
+ matrix:
18
+ python-version: ["3.10", "3.11", "3.12"]
19
+ steps:
20
+ - uses: actions/checkout@v4
21
+ - uses: actions/setup-python@v5
22
+ with:
23
+ python-version: ${{ matrix.python-version }}
24
+ - name: Install
25
+ run: pip install -e ".[dev]"
26
+ - name: Lint
27
+ run: ruff check control_plane tests
28
+ - name: Test
29
+ run: pytest -v
@@ -0,0 +1,129 @@
1
+ name: Release
2
+
3
+ # One-shot release: optional tag input → verify → build → tag → GitHub Release → PyPI.
4
+ # See RELEASING.md for Trusted Publisher setup.
5
+ on:
6
+ workflow_dispatch:
7
+ inputs:
8
+ tag:
9
+ description: "Release tag (e.g. v0.1.0). Leave empty to use v{version} from pyproject.toml."
10
+ required: false
11
+ type: string
12
+
13
+ concurrency:
14
+ group: release
15
+ cancel-in-progress: false
16
+
17
+ permissions:
18
+ contents: read
19
+
20
+ jobs:
21
+ release:
22
+ runs-on: ubuntu-latest
23
+ permissions:
24
+ contents: write
25
+ outputs:
26
+ tag: ${{ steps.meta.outputs.tag }}
27
+ version: ${{ steps.meta.outputs.version }}
28
+ steps:
29
+ - uses: actions/checkout@v4
30
+ with:
31
+ fetch-depth: 0
32
+
33
+ - uses: actions/setup-python@v5
34
+ with:
35
+ python-version: "3.12"
36
+
37
+ - name: Resolve tag and verify against pyproject
38
+ id: meta
39
+ env:
40
+ INPUT_TAG: ${{ inputs.tag }}
41
+ run: |
42
+ set -euo pipefail
43
+ version="$(grep -m1 '^version' pyproject.toml | sed -E 's/.*"([^"]+)".*/\1/')"
44
+ init="$(grep -m1 '__version__' control_plane/__init__.py | sed -E 's/.*"([^"]+)".*/\1/')"
45
+ if [ "$init" != "$version" ]; then
46
+ echo "::error::__version__ ($init) does not match pyproject version ($version)."
47
+ exit 1
48
+ fi
49
+ if [ -n "${INPUT_TAG}" ]; then
50
+ tag="${INPUT_TAG}"
51
+ else
52
+ tag="v${version}"
53
+ fi
54
+ case "$tag" in
55
+ v*) ;;
56
+ *) tag="v${tag}" ;;
57
+ esac
58
+ tag_version="${tag#v}"
59
+ echo "tag: $tag | pyproject version: $version | sha: $GITHUB_SHA"
60
+ if [ "$tag_version" != "$version" ]; then
61
+ echo "::error::Tag $tag does not match pyproject version $version. Bump version (and __version__) and merge before releasing."
62
+ exit 1
63
+ fi
64
+ echo "tag=$tag" >> "$GITHUB_OUTPUT"
65
+ echo "version=$version" >> "$GITHUB_OUTPUT"
66
+
67
+ - name: Build sdist and wheel
68
+ run: |
69
+ pip install "build>=1.2"
70
+ python -m build
71
+
72
+ - name: Check metadata
73
+ run: |
74
+ pip install "twine>=6"
75
+ twine check dist/*
76
+
77
+ - name: Create tag and GitHub Release
78
+ env:
79
+ GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
80
+ TAG: ${{ steps.meta.outputs.tag }}
81
+ VERSION: ${{ steps.meta.outputs.version }}
82
+ run: |
83
+ set -euo pipefail
84
+ git config user.name "github-actions[bot]"
85
+ git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
86
+
87
+ if git rev-parse "refs/tags/${TAG}" >/dev/null 2>&1; then
88
+ existing="$(git rev-list -n 1 "${TAG}")"
89
+ if [ "$existing" != "$GITHUB_SHA" ]; then
90
+ echo "::error::Tag ${TAG} already exists on $existing, not $GITHUB_SHA."
91
+ exit 1
92
+ fi
93
+ echo "Tag ${TAG} already points at this commit; reusing."
94
+ else
95
+ git tag "${TAG}" "$GITHUB_SHA"
96
+ git push origin "refs/tags/${TAG}"
97
+ fi
98
+
99
+ if gh release view "${TAG}" >/dev/null 2>&1; then
100
+ echo "::error::GitHub Release ${TAG} already exists. Delete it or bump the version."
101
+ exit 1
102
+ fi
103
+
104
+ gh release create "${TAG}" dist/* \
105
+ --title "agentplane-control-plane ${VERSION}" \
106
+ --generate-notes \
107
+ --target "$GITHUB_SHA"
108
+
109
+ - uses: actions/upload-artifact@v4
110
+ with:
111
+ name: dist
112
+ path: dist/
113
+
114
+ publish:
115
+ needs: release
116
+ runs-on: ubuntu-latest
117
+ environment:
118
+ name: pypi
119
+ url: https://pypi.org/project/agentplane-control-plane/
120
+ permissions:
121
+ id-token: write
122
+ steps:
123
+ - uses: actions/download-artifact@v4
124
+ with:
125
+ name: dist
126
+ path: dist/
127
+
128
+ - name: Publish to PyPI
129
+ uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,14 @@
1
+ __pycache__/
2
+ *.py[cod]
3
+ *.egg-info/
4
+ .venv/
5
+ venv/
6
+ dist/
7
+ build/
8
+ .pytest_cache/
9
+ .ruff_cache/
10
+ *.db
11
+ *.db-*
12
+ .DS_Store
13
+ .env
14
+ .streamlit/secrets.toml
@@ -0,0 +1,9 @@
1
+ # Changelog
2
+
3
+ ## [Unreleased]
4
+
5
+ ## [0.1.0] — 2026-08-14
6
+
7
+ - First PyPI release of the shared AgentPlane control plane.
8
+ - SQLite stays in this process; Chronicle and TokenOps talk over HTTP.
9
+ - HTML UI (Admin, Chronicle waterfall, TokenOps) served with the API.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 agentplane
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,95 @@
1
+ Metadata-Version: 2.5
2
+ Name: agentplane-control-plane
3
+ Version: 0.1.0
4
+ Summary: Shared AgentPlane control plane: Chronicle envelopes + TokenOps governance over HTTP.
5
+ Project-URL: Homepage, https://github.com/theagentplane/control-plane
6
+ Project-URL: Repository, https://github.com/theagentplane/control-plane
7
+ Project-URL: Issues, https://github.com/theagentplane/control-plane/issues
8
+ Project-URL: Changelog, https://github.com/theagentplane/control-plane/blob/main/CHANGELOG.md
9
+ Author: Susheem Koul, Tisha Chawla
10
+ License-Expression: MIT
11
+ License-File: LICENSE
12
+ Keywords: agents,chronicle,control-plane,governance,llm,observability,tokenops
13
+ Classifier: Development Status :: 3 - Alpha
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.10
17
+ Classifier: Programming Language :: Python :: 3.11
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
20
+ Classifier: Topic :: System :: Systems Administration
21
+ Requires-Python: >=3.10
22
+ Requires-Dist: fastapi>=0.115
23
+ Requires-Dist: httpx>=0.27
24
+ Requires-Dist: jinja2>=3.1
25
+ Requires-Dist: pydantic>=2
26
+ Requires-Dist: pyyaml>=6
27
+ Requires-Dist: uvicorn[standard]>=0.32
28
+ Provides-Extra: dev
29
+ Requires-Dist: build>=1.2; extra == 'dev'
30
+ Requires-Dist: httpx>=0.27; extra == 'dev'
31
+ Requires-Dist: pytest>=8; extra == 'dev'
32
+ Requires-Dist: ruff==0.15.22; extra == 'dev'
33
+ Requires-Dist: twine>=6; extra == 'dev'
34
+ Provides-Extra: ui
35
+ Requires-Dist: pandas>=2; extra == 'ui'
36
+ Requires-Dist: streamlit>=1.32; extra == 'ui'
37
+ Description-Content-Type: text/markdown
38
+
39
+ # AgentPlane Control Plane
40
+
41
+ Shared HTTP control plane for **Chronicle** and **TokenOps**. SQLite lives
42
+ **only** in this process. Agents never open the database file.
43
+
44
+ Design: [`docs/DESIGN.md`](docs/DESIGN.md) · Issue: [#2](https://github.com/theagentplane/control-plane/issues/2)
45
+
46
+ ## Install
47
+
48
+ ```bash
49
+ pip install agentplane-control-plane
50
+ control-plane serve --port 8800 --db control_plane.db
51
+ ```
52
+
53
+ From a clone:
54
+
55
+ ```bash
56
+ python -m venv .venv && source .venv/bin/activate
57
+ pip install -e ".[dev]"
58
+ control-plane serve --port 8800 --db control_plane.db
59
+ ```
60
+
61
+ Open http://127.0.0.1:8800/ — Admin, Chronicle, TokenOps tabs. No login.
62
+
63
+ Sidecars:
64
+
65
+ ```bash
66
+ export CONTROL_PLANE_URL=http://127.0.0.1:8800
67
+ # Chronicle
68
+ chronicle.record("run", store=RemoteStore(os.environ["CONTROL_PLANE_URL"], batch_size=1))
69
+ # TokenOps
70
+ export TOKENOPS_URL=http://127.0.0.1:8800
71
+ # TOKENOPS_EMBEDDED must be unset
72
+ ```
73
+
74
+ ## API callers
75
+
76
+ | Route | Caller |
77
+ |---|---|
78
+ | `POST /v1/envelopes:batch` | Chronicle sidecar (only ingest API; `batch_size=1` = immediate flush) |
79
+ | `GET /v1/traces/{id}/envelopes` | Chronicle sidecar (fixture replay) and Chronicle UI waterfall |
80
+ | `GET /v1/traces` | Chronicle UI search |
81
+ | `POST /v1/runs`, ledger, governance, run-records | TokenOps sidecar |
82
+ | segments / budgets / policies, admin keys | UI |
83
+
84
+ Auth off when no keys exist (local). Create keys in Admin, or:
85
+
86
+ ```bash
87
+ export CONTROL_PLANE_API_KEYS='chron:local:ingest+read,tops:local:ingest+read,admin:local:read+admin'
88
+ export CONTROL_PLANE_API_KEY=chron # sidecar
89
+ ```
90
+
91
+ ## Local SQLite
92
+
93
+ Default is a file next to the server (`CONTROL_PLANE_DB`). Do not point TokenOps
94
+ or Chronicle at that file. Unit tests in those repos keep their own sqlite/jsonl
95
+ when `TOKENOPS_EMBEDDED=1` / local Chronicle stores.
@@ -0,0 +1,57 @@
1
+ # AgentPlane Control Plane
2
+
3
+ Shared HTTP control plane for **Chronicle** and **TokenOps**. SQLite lives
4
+ **only** in this process. Agents never open the database file.
5
+
6
+ Design: [`docs/DESIGN.md`](docs/DESIGN.md) · Issue: [#2](https://github.com/theagentplane/control-plane/issues/2)
7
+
8
+ ## Install
9
+
10
+ ```bash
11
+ pip install agentplane-control-plane
12
+ control-plane serve --port 8800 --db control_plane.db
13
+ ```
14
+
15
+ From a clone:
16
+
17
+ ```bash
18
+ python -m venv .venv && source .venv/bin/activate
19
+ pip install -e ".[dev]"
20
+ control-plane serve --port 8800 --db control_plane.db
21
+ ```
22
+
23
+ Open http://127.0.0.1:8800/ — Admin, Chronicle, TokenOps tabs. No login.
24
+
25
+ Sidecars:
26
+
27
+ ```bash
28
+ export CONTROL_PLANE_URL=http://127.0.0.1:8800
29
+ # Chronicle
30
+ chronicle.record("run", store=RemoteStore(os.environ["CONTROL_PLANE_URL"], batch_size=1))
31
+ # TokenOps
32
+ export TOKENOPS_URL=http://127.0.0.1:8800
33
+ # TOKENOPS_EMBEDDED must be unset
34
+ ```
35
+
36
+ ## API callers
37
+
38
+ | Route | Caller |
39
+ |---|---|
40
+ | `POST /v1/envelopes:batch` | Chronicle sidecar (only ingest API; `batch_size=1` = immediate flush) |
41
+ | `GET /v1/traces/{id}/envelopes` | Chronicle sidecar (fixture replay) and Chronicle UI waterfall |
42
+ | `GET /v1/traces` | Chronicle UI search |
43
+ | `POST /v1/runs`, ledger, governance, run-records | TokenOps sidecar |
44
+ | segments / budgets / policies, admin keys | UI |
45
+
46
+ Auth off when no keys exist (local). Create keys in Admin, or:
47
+
48
+ ```bash
49
+ export CONTROL_PLANE_API_KEYS='chron:local:ingest+read,tops:local:ingest+read,admin:local:read+admin'
50
+ export CONTROL_PLANE_API_KEY=chron # sidecar
51
+ ```
52
+
53
+ ## Local SQLite
54
+
55
+ Default is a file next to the server (`CONTROL_PLANE_DB`). Do not point TokenOps
56
+ or Chronicle at that file. Unit tests in those repos keep their own sqlite/jsonl
57
+ when `TOKENOPS_EMBEDDED=1` / local Chronicle stores.
@@ -0,0 +1,80 @@
1
+ # Releasing
2
+
3
+ The control plane publishes to PyPI as **`agentplane-control-plane`**
4
+ (CLI: `control-plane`, import: `control_plane`) using GitHub Actions with
5
+ **Trusted Publishing (OIDC)** — no API tokens are stored anywhere. Same
6
+ pattern as Chronicle (`agent-chronicle`) and TokenOps (`agent-tokenops`).
7
+
8
+ One workflow run is atomic: **verify → build → git tag → GitHub Release → PyPI**.
9
+
10
+ ## One-time setup (Trusted Publishing)
11
+
12
+ ### 1. Pending publisher on PyPI
13
+
14
+ 1. Sign in at [pypi.org](https://pypi.org) (account that will own the project).
15
+ 2. Open [Publishing](https://pypi.org/manage/account/publishing/)
16
+ (*Your account* → *Publishing*).
17
+ 3. Under **Add a new pending publisher**, choose **GitHub** and fill in:
18
+ - PyPI project name: `agentplane-control-plane`
19
+ - Owner: `theagentplane`
20
+ - Repository: `control-plane`
21
+ - Workflow filename: `release.yml`
22
+ - Environment name: `pypi`
23
+ 4. Save. The project does **not** exist yet; the first successful CI upload
24
+ creates it and binds that publisher permanently.
25
+
26
+ Optional: do the same on [TestPyPI](https://test.pypi.org/manage/account/publishing/)
27
+ for a dry run (same project name `agentplane-control-plane`).
28
+
29
+ ### 2. GitHub Environment
30
+
31
+ In the `theagentplane/control-plane` repo:
32
+
33
+ 1. **Settings → Environments → New environment**
34
+ 2. Name it exactly `pypi` (must match `environment: pypi` in `release.yml` and
35
+ the PyPI publisher form).
36
+ 3. Optional: add required reviewers so a human must approve before upload.
37
+
38
+ No secrets needed — OIDC uses `permissions: id-token: write` in `release.yml`.
39
+
40
+ ## Cutting a release
41
+
42
+ 1. Move the `CHANGELOG.md` `[Unreleased]` items under a new version heading with
43
+ today's date; start a fresh empty `[Unreleased]`.
44
+ 2. Bump `version` in `pyproject.toml` **and** `__version__` in
45
+ `control_plane/__init__.py` following SemVer. Commit and merge to `main`.
46
+ 3. In GitHub: **Actions → Release → Run workflow**
47
+ - Branch: `main` (the commit that has the version bump)
48
+ - Tag (optional): e.g. `v0.1.0` — leave empty to use `v{version}` from
49
+ `pyproject.toml`
50
+ 4. The workflow will, in one run:
51
+ 1. Require the tag to match `pyproject.toml` and `__version__`
52
+ 2. Build sdist/wheel and `twine check`
53
+ 3. Create and push the git tag
54
+ 4. Create the GitHub Release (attaches dist artifacts)
55
+ 5. Upload to PyPI (may wait on `pypi` environment approval)
56
+ 5. Verify:
57
+ ```bash
58
+ pip install agentplane-control-plane==X.Y.Z
59
+ python -c "import control_plane; print(control_plane.__version__)"
60
+ control-plane --help
61
+ ```
62
+
63
+ ## Version matching (why the workflow checks)
64
+
65
+ PyPI versions are **immutable** and come from metadata inside the built wheel —
66
+ that metadata is read from `pyproject.toml` at build time. The git tag
67
+ (`v0.1.0`) is only a label; it does **not** set the package version.
68
+
69
+ If you passed tag `v0.1.1` but `pyproject.toml` still says `0.1.0`, the workflow
70
+ refuses to continue. Empty tag input defaults to `v` + the pyproject version.
71
+
72
+ Also keep `control_plane.__version__` in sync so runtime version matches pip.
73
+
74
+ ## Versioning notes
75
+
76
+ - Stay in `0.x` while the HTTP contract may still change.
77
+ - Install name: `agentplane-control-plane`. Import name: `control_plane`.
78
+ CLI: `control-plane`.
79
+ - Do not create tags or GitHub Releases by hand for normal cuts — use
80
+ **Run workflow** so tag, Release, and PyPI stay in lockstep.
@@ -0,0 +1,37 @@
1
+ # TokenOps control plane seed. Consumed by tokenops.control.build_governor / Store auto-seed.
2
+ # Budgets are spend caps bound to a segment; policies are the (detect, fix) templates.
3
+ governance:
4
+ budgets:
5
+ - id: run_llm_cap
6
+ limit_micros: 2000000 # $2.00 per run; null/omitted -> unlimited accumulator
7
+ dimension: run
8
+ policies:
9
+ cost_budget:
10
+ budget: run_llm_cap
11
+ pre_call_worst_case:
12
+ budget: run_llm_cap
13
+ default_max_output: 1024
14
+ step_cap:
15
+ max_steps: 20
16
+ concurrency_cap:
17
+ max_concurrent: 4
18
+ mode: reject
19
+ tool_fix:
20
+ registry: [search]
21
+ k: 3
22
+ tool_output_cap:
23
+ cap_tokens: 8000
24
+ progress_guard:
25
+ window: 6
26
+ repeats: 3
27
+ max_corrections: 2
28
+ cost_guard:
29
+ budget: run_llm_cap
30
+ threshold: 0.8
31
+ mode: minimize
32
+ context_compaction:
33
+ ctx_max: 100000
34
+ has_hook: false
35
+ output_runaway:
36
+ repeats: 4
37
+ max_retries: 2
@@ -0,0 +1,3 @@
1
+ """AgentPlane shared control plane — governance (TokenOps) + Chronicle envelopes."""
2
+
3
+ __version__ = "0.1.0"