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.
- agentplane_control_plane-0.1.0/.github/workflows/ci.yml +29 -0
- agentplane_control_plane-0.1.0/.github/workflows/release.yml +129 -0
- agentplane_control_plane-0.1.0/.gitignore +14 -0
- agentplane_control_plane-0.1.0/CHANGELOG.md +9 -0
- agentplane_control_plane-0.1.0/LICENSE +21 -0
- agentplane_control_plane-0.1.0/PKG-INFO +95 -0
- agentplane_control_plane-0.1.0/README.md +57 -0
- agentplane_control_plane-0.1.0/RELEASING.md +80 -0
- agentplane_control_plane-0.1.0/config/default.yaml +37 -0
- agentplane_control_plane-0.1.0/control_plane/__init__.py +3 -0
- agentplane_control_plane-0.1.0/control_plane/app.py +494 -0
- agentplane_control_plane-0.1.0/control_plane/auth.py +102 -0
- agentplane_control_plane-0.1.0/control_plane/cli.py +42 -0
- agentplane_control_plane-0.1.0/control_plane/envelope_store.py +254 -0
- agentplane_control_plane-0.1.0/control_plane/governance_loader.py +31 -0
- agentplane_control_plane-0.1.0/control_plane/models.py +106 -0
- agentplane_control_plane-0.1.0/control_plane/remote_store.py +228 -0
- agentplane_control_plane-0.1.0/control_plane/serde.py +135 -0
- agentplane_control_plane-0.1.0/control_plane/settings.py +31 -0
- agentplane_control_plane-0.1.0/control_plane/store.py +583 -0
- agentplane_control_plane-0.1.0/control_plane/store_factory.py +36 -0
- agentplane_control_plane-0.1.0/control_plane/store_protocol.py +68 -0
- agentplane_control_plane-0.1.0/control_plane/templates.py +21 -0
- agentplane_control_plane-0.1.0/control_plane/web/static/logo-mark.png +0 -0
- agentplane_control_plane-0.1.0/control_plane/web/static/plane.css +437 -0
- agentplane_control_plane-0.1.0/control_plane/web/static/plane.js +409 -0
- agentplane_control_plane-0.1.0/control_plane/web/templates/index.html +49 -0
- agentplane_control_plane-0.1.0/control_plane/web.py +28 -0
- agentplane_control_plane-0.1.0/docs/DESIGN.md +77 -0
- agentplane_control_plane-0.1.0/pyproject.toml +76 -0
- agentplane_control_plane-0.1.0/tests/test_envelopes.py +142 -0
- agentplane_control_plane-0.1.0/tests/test_health.py +20 -0
- agentplane_control_plane-0.1.0/ui/__init__.py +0 -0
- agentplane_control_plane-0.1.0/ui/app.py +24 -0
- agentplane_control_plane-0.1.0/ui/run_detail.py +120 -0
- agentplane_control_plane-0.1.0/ui/store_client.py +24 -0
- agentplane_control_plane-0.1.0/ui/theme.py +145 -0
- agentplane_control_plane-0.1.0/ui/views/__init__.py +0 -0
- agentplane_control_plane-0.1.0/ui/views/admin.py +275 -0
- 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,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
|