agentpool-cli 0.1.7__tar.gz → 0.1.8__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.
- {agentpool_cli-0.1.7 → agentpool_cli-0.1.8}/CHANGELOG.md +8 -0
- {agentpool_cli-0.1.7 → agentpool_cli-0.1.8}/PKG-INFO +7 -3
- {agentpool_cli-0.1.7 → agentpool_cli-0.1.8}/README.md +6 -2
- {agentpool_cli-0.1.7 → agentpool_cli-0.1.8}/docs/agentpool-skill.md +3 -0
- {agentpool_cli-0.1.7 → agentpool_cli-0.1.8}/docs/usage-detection.md +9 -2
- {agentpool_cli-0.1.7 → agentpool_cli-0.1.8}/pyproject.toml +1 -1
- {agentpool_cli-0.1.7 → agentpool_cli-0.1.8}/server.json +2 -2
- {agentpool_cli-0.1.7 → agentpool_cli-0.1.8}/src/agentpool/__init__.py +1 -1
- {agentpool_cli-0.1.7 → agentpool_cli-0.1.8}/src/agentpool/config.py +8 -0
- {agentpool_cli-0.1.7 → agentpool_cli-0.1.8}/src/agentpool/mcp/tools.py +1 -1
- {agentpool_cli-0.1.7 → agentpool_cli-0.1.8}/src/agentpool/onboarding.py +2 -1
- {agentpool_cli-0.1.7 → agentpool_cli-0.1.8}/src/agentpool/session_manager.py +34 -1
- {agentpool_cli-0.1.7 → agentpool_cli-0.1.8}/src/agentpool/stats/compute.py +2 -3
- {agentpool_cli-0.1.7 → agentpool_cli-0.1.8}/src/agentpool/usage/summary.py +1 -4
- {agentpool_cli-0.1.7 → agentpool_cli-0.1.8}/tests/unit/test_models_config_store.py +79 -0
- {agentpool_cli-0.1.7 → agentpool_cli-0.1.8}/tests/unit/test_usage_summary_enrichment.py +5 -4
- {agentpool_cli-0.1.7 → agentpool_cli-0.1.8}/uv.lock +1 -1
- {agentpool_cli-0.1.7 → agentpool_cli-0.1.8}/.cursor/mcp.json.example +0 -0
- {agentpool_cli-0.1.7 → agentpool_cli-0.1.8}/.github/CODEOWNERS +0 -0
- {agentpool_cli-0.1.7 → agentpool_cli-0.1.8}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
- {agentpool_cli-0.1.7 → agentpool_cli-0.1.8}/.github/ISSUE_TEMPLATE/provider_probe.md +0 -0
- {agentpool_cli-0.1.7 → agentpool_cli-0.1.8}/.github/dependabot.yml +0 -0
- {agentpool_cli-0.1.7 → agentpool_cli-0.1.8}/.github/workflows/ci.yml +0 -0
- {agentpool_cli-0.1.7 → agentpool_cli-0.1.8}/.github/workflows/release.yml +0 -0
- {agentpool_cli-0.1.7 → agentpool_cli-0.1.8}/.gitignore +0 -0
- {agentpool_cli-0.1.7 → agentpool_cli-0.1.8}/.mcp.json.example +0 -0
- {agentpool_cli-0.1.7 → agentpool_cli-0.1.8}/AGENTS.md +0 -0
- {agentpool_cli-0.1.7 → agentpool_cli-0.1.8}/CONTRIBUTING.md +0 -0
- {agentpool_cli-0.1.7 → agentpool_cli-0.1.8}/LICENSE +0 -0
- {agentpool_cli-0.1.7 → agentpool_cli-0.1.8}/SECURITY.md +0 -0
- {agentpool_cli-0.1.7 → agentpool_cli-0.1.8}/docs/agent-cli-and-mcp.md +0 -0
- {agentpool_cli-0.1.7 → agentpool_cli-0.1.8}/docs/architecture.md +0 -0
- {agentpool_cli-0.1.7 → agentpool_cli-0.1.8}/docs/examples/README.md +0 -0
- {agentpool_cli-0.1.7 → agentpool_cli-0.1.8}/docs/examples.md +0 -0
- {agentpool_cli-0.1.7 → agentpool_cli-0.1.8}/docs/install.md +0 -0
- {agentpool_cli-0.1.7 → agentpool_cli-0.1.8}/docs/mcp-clients.md +0 -0
- {agentpool_cli-0.1.7 → agentpool_cli-0.1.8}/docs/mcp-tools.md +0 -0
- {agentpool_cli-0.1.7 → agentpool_cli-0.1.8}/docs/model-catalog.md +0 -0
- {agentpool_cli-0.1.7 → agentpool_cli-0.1.8}/docs/onboarding.md +0 -0
- {agentpool_cli-0.1.7 → agentpool_cli-0.1.8}/docs/provider-adapters.md +0 -0
- {agentpool_cli-0.1.7 → agentpool_cli-0.1.8}/docs/provider-lifecycle-matrix.md +0 -0
- {agentpool_cli-0.1.7 → agentpool_cli-0.1.8}/docs/quickstart.md +0 -0
- {agentpool_cli-0.1.7 → agentpool_cli-0.1.8}/docs/release.md +0 -0
- {agentpool_cli-0.1.7 → agentpool_cli-0.1.8}/docs/security.md +0 -0
- {agentpool_cli-0.1.7 → agentpool_cli-0.1.8}/docs/setup-claude-code.md +0 -0
- {agentpool_cli-0.1.7 → agentpool_cli-0.1.8}/docs/setup-codex.md +0 -0
- {agentpool_cli-0.1.7 → agentpool_cli-0.1.8}/docs/setup-copilot.md +0 -0
- {agentpool_cli-0.1.7 → agentpool_cli-0.1.8}/docs/setup-cursor-cli.md +0 -0
- {agentpool_cli-0.1.7 → agentpool_cli-0.1.8}/docs/setup-cursor.md +0 -0
- {agentpool_cli-0.1.7 → agentpool_cli-0.1.8}/docs/setup-devin.md +0 -0
- {agentpool_cli-0.1.7 → agentpool_cli-0.1.8}/docs/setup-droid.md +0 -0
- {agentpool_cli-0.1.7 → agentpool_cli-0.1.8}/docs/stats.md +0 -0
- {agentpool_cli-0.1.7 → agentpool_cli-0.1.8}/docs/usage-probe-matrix.md +0 -0
- {agentpool_cli-0.1.7 → agentpool_cli-0.1.8}/scripts/install.sh +0 -0
- {agentpool_cli-0.1.7 → agentpool_cli-0.1.8}/src/agentpool/agent_io.py +0 -0
- {agentpool_cli-0.1.7 → agentpool_cli-0.1.8}/src/agentpool/artifacts.py +0 -0
- {agentpool_cli-0.1.7 → agentpool_cli-0.1.8}/src/agentpool/cli.py +0 -0
- {agentpool_cli-0.1.7 → agentpool_cli-0.1.8}/src/agentpool/event_detection.py +0 -0
- {agentpool_cli-0.1.7 → agentpool_cli-0.1.8}/src/agentpool/fixtures/__init__.py +0 -0
- {agentpool_cli-0.1.7 → agentpool_cli-0.1.8}/src/agentpool/fixtures/fake_agents/__init__.py +0 -0
- {agentpool_cli-0.1.7 → agentpool_cli-0.1.8}/src/agentpool/fixtures/fake_agents/fake_approval_agent.py +0 -0
- {agentpool_cli-0.1.7 → agentpool_cli-0.1.8}/src/agentpool/fixtures/fake_agents/fake_common.py +0 -0
- {agentpool_cli-0.1.7 → agentpool_cli-0.1.8}/src/agentpool/fixtures/fake_agents/fake_completed_agent.py +0 -0
- {agentpool_cli-0.1.7 → agentpool_cli-0.1.8}/src/agentpool/fixtures/fake_agents/fake_idle_agent.py +0 -0
- {agentpool_cli-0.1.7 → agentpool_cli-0.1.8}/src/agentpool/fixtures/fake_agents/fake_limit_agent.py +0 -0
- {agentpool_cli-0.1.7 → agentpool_cli-0.1.8}/src/agentpool/fixtures/fake_agents/fake_patch_agent.py +0 -0
- {agentpool_cli-0.1.7 → agentpool_cli-0.1.8}/src/agentpool/fixtures/fake_agents/fake_question_agent.py +0 -0
- {agentpool_cli-0.1.7 → agentpool_cli-0.1.8}/src/agentpool/git_worktree.py +0 -0
- {agentpool_cli-0.1.7 → agentpool_cli-0.1.8}/src/agentpool/mcp/__init__.py +0 -0
- {agentpool_cli-0.1.7 → agentpool_cli-0.1.8}/src/agentpool/mcp/resources.py +0 -0
- {agentpool_cli-0.1.7 → agentpool_cli-0.1.8}/src/agentpool/mcp_server.py +0 -0
- {agentpool_cli-0.1.7 → agentpool_cli-0.1.8}/src/agentpool/models.py +0 -0
- {agentpool_cli-0.1.7 → agentpool_cli-0.1.8}/src/agentpool/policy.py +0 -0
- {agentpool_cli-0.1.7 → agentpool_cli-0.1.8}/src/agentpool/preferences.py +0 -0
- {agentpool_cli-0.1.7 → agentpool_cli-0.1.8}/src/agentpool/provider_model_catalog.json +0 -0
- {agentpool_cli-0.1.7 → agentpool_cli-0.1.8}/src/agentpool/providers/__init__.py +0 -0
- {agentpool_cli-0.1.7 → agentpool_cli-0.1.8}/src/agentpool/providers/base.py +0 -0
- {agentpool_cli-0.1.7 → agentpool_cli-0.1.8}/src/agentpool/providers/registry.py +0 -0
- {agentpool_cli-0.1.7 → agentpool_cli-0.1.8}/src/agentpool/redaction.py +0 -0
- {agentpool_cli-0.1.7 → agentpool_cli-0.1.8}/src/agentpool/runtimes/__init__.py +0 -0
- {agentpool_cli-0.1.7 → agentpool_cli-0.1.8}/src/agentpool/runtimes/base.py +0 -0
- {agentpool_cli-0.1.7 → agentpool_cli-0.1.8}/src/agentpool/runtimes/tmux.py +0 -0
- {agentpool_cli-0.1.7 → agentpool_cli-0.1.8}/src/agentpool/stats/__init__.py +0 -0
- {agentpool_cli-0.1.7 → agentpool_cli-0.1.8}/src/agentpool/stats/card.py +0 -0
- {agentpool_cli-0.1.7 → agentpool_cli-0.1.8}/src/agentpool/stats/queries.py +0 -0
- {agentpool_cli-0.1.7 → agentpool_cli-0.1.8}/src/agentpool/stats/render.py +0 -0
- {agentpool_cli-0.1.7 → agentpool_cli-0.1.8}/src/agentpool/stats/window.py +0 -0
- {agentpool_cli-0.1.7 → agentpool_cli-0.1.8}/src/agentpool/store.py +0 -0
- {agentpool_cli-0.1.7 → agentpool_cli-0.1.8}/src/agentpool/usage/__init__.py +0 -0
- {agentpool_cli-0.1.7 → agentpool_cli-0.1.8}/src/agentpool/usage/_common.py +0 -0
- {agentpool_cli-0.1.7 → agentpool_cli-0.1.8}/src/agentpool/usage/ccusage.py +0 -0
- {agentpool_cli-0.1.7 → agentpool_cli-0.1.8}/src/agentpool/usage/claude.py +0 -0
- {agentpool_cli-0.1.7 → agentpool_cli-0.1.8}/src/agentpool/usage/codex.py +0 -0
- {agentpool_cli-0.1.7 → agentpool_cli-0.1.8}/src/agentpool/usage/codexbar.py +0 -0
- {agentpool_cli-0.1.7 → agentpool_cli-0.1.8}/src/agentpool/usage/combine.py +0 -0
- {agentpool_cli-0.1.7 → agentpool_cli-0.1.8}/src/agentpool/usage/copilot.py +0 -0
- {agentpool_cli-0.1.7 → agentpool_cli-0.1.8}/src/agentpool/usage/devin.py +0 -0
- {agentpool_cli-0.1.7 → agentpool_cli-0.1.8}/src/agentpool/usage/parsers.py +0 -0
- {agentpool_cli-0.1.7 → agentpool_cli-0.1.8}/src/agentpool/usage/probes.py +0 -0
- {agentpool_cli-0.1.7 → agentpool_cli-0.1.8}/src/agentpool/usage/provider_parsers.py +0 -0
- {agentpool_cli-0.1.7 → agentpool_cli-0.1.8}/src/agentpool/utils.py +0 -0
- {agentpool_cli-0.1.7 → agentpool_cli-0.1.8}/tests/fixtures/provider_model_catalog_golden.json +0 -0
- {agentpool_cli-0.1.7 → agentpool_cli-0.1.8}/tests/fixtures/stats_seed.py +0 -0
- {agentpool_cli-0.1.7 → agentpool_cli-0.1.8}/tests/fixtures/usage/claude_usage.txt +0 -0
- {agentpool_cli-0.1.7 → agentpool_cli-0.1.8}/tests/fixtures/usage/codex_rate_limits.json +0 -0
- {agentpool_cli-0.1.7 → agentpool_cli-0.1.8}/tests/fixtures/usage/copilot_user.json +0 -0
- {agentpool_cli-0.1.7 → agentpool_cli-0.1.8}/tests/fixtures/usage/devin_plan_status.json +0 -0
- {agentpool_cli-0.1.7 → agentpool_cli-0.1.8}/tests/integration/test_fake_tmux_flow.py +0 -0
- {agentpool_cli-0.1.7 → agentpool_cli-0.1.8}/tests/unit/test_agent_io.py +0 -0
- {agentpool_cli-0.1.7 → agentpool_cli-0.1.8}/tests/unit/test_cli.py +0 -0
- {agentpool_cli-0.1.7 → agentpool_cli-0.1.8}/tests/unit/test_event_policy.py +0 -0
- {agentpool_cli-0.1.7 → agentpool_cli-0.1.8}/tests/unit/test_mcp_surface.py +0 -0
- {agentpool_cli-0.1.7 → agentpool_cli-0.1.8}/tests/unit/test_mcp_tools.py +0 -0
- {agentpool_cli-0.1.7 → agentpool_cli-0.1.8}/tests/unit/test_onboarding.py +0 -0
- {agentpool_cli-0.1.7 → agentpool_cli-0.1.8}/tests/unit/test_redaction.py +0 -0
- {agentpool_cli-0.1.7 → agentpool_cli-0.1.8}/tests/unit/test_stats_cli.py +0 -0
- {agentpool_cli-0.1.7 → agentpool_cli-0.1.8}/tests/unit/test_stats_mcp.py +0 -0
- {agentpool_cli-0.1.7 → agentpool_cli-0.1.8}/tests/unit/test_stats_window.py +0 -0
- {agentpool_cli-0.1.7 → agentpool_cli-0.1.8}/tests/unit/test_subprocess_safety.py +0 -0
- {agentpool_cli-0.1.7 → agentpool_cli-0.1.8}/tests/unit/test_usage_probes.py +0 -0
- {agentpool_cli-0.1.7 → agentpool_cli-0.1.8}/tests/unit/test_usage_provider_parsers.py +0 -0
|
@@ -2,6 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
## Unreleased
|
|
4
4
|
|
|
5
|
+
## 0.1.8 - 2026-06-01
|
|
6
|
+
|
|
7
|
+
- Treat stale usage snapshots as age metadata, not as an unusable-provider
|
|
8
|
+
reason. `usage-summary` still reports `stale` and `age_seconds`, but a stale
|
|
9
|
+
cache entry no longer overrides usable quota/status data.
|
|
10
|
+
- Add optional `policy.usage_auto_refresh_after_seconds` for users who want
|
|
11
|
+
cached usage summaries to refresh automatically after a configured age.
|
|
12
|
+
|
|
5
13
|
## 0.1.7 - 2026-06-01
|
|
6
14
|
|
|
7
15
|
- Centralize non-interactive subprocess execution behind terminal-safe helpers.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: agentpool-cli
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.8
|
|
4
4
|
Summary: Make full use of every coding-agent subscription you pay for: a local CLI + MCP server that surfaces live usage limits and offloads work to providers with headroom.
|
|
5
5
|
Author: AgentPool contributors
|
|
6
6
|
License-Expression: MIT
|
|
@@ -171,14 +171,18 @@ agentpool usage-summary --refresh --backend ccusage --provider claude-code --jso
|
|
|
171
171
|
|
|
172
172
|
`usage-summary` returns a `providers` object keyed by provider id. It is not
|
|
173
173
|
ordered and it is not a recommendation list. Each row includes `usable`,
|
|
174
|
-
`unusable_reason`, quota windows, confidence, staleness, and reset timing when
|
|
174
|
+
`unusable_reason`, quota windows, confidence, age/staleness, and reset timing when
|
|
175
175
|
the provider exposes it. The older CLI `capacity-summary` command is retained
|
|
176
176
|
as a human convenience alias; the MCP surface only exposes `get_usage_snapshot`
|
|
177
177
|
and the opt-in `get_usage_summary` tool.
|
|
178
178
|
|
|
179
179
|
The default buffer is `policy.min_remaining_percent = 10`. If any reported
|
|
180
180
|
quota window is below that buffer, the provider row is marked unusable for the
|
|
181
|
-
summary.
|
|
181
|
+
summary. Staleness is reported as age information only; it does not by itself
|
|
182
|
+
make a provider unusable. If you want cached summary reads to refresh
|
|
183
|
+
automatically after a threshold, set `policy.usage_auto_refresh_after_seconds`
|
|
184
|
+
in `~/.agentpool/config.yaml`.
|
|
185
|
+
AgentPool still does not pick an alternative provider for you.
|
|
182
186
|
MCP usage refreshes are intentionally bounded and may return `partial=true`;
|
|
183
187
|
use the CLI commands above when a shell-capable agent needs a complete live
|
|
184
188
|
refresh.
|
|
@@ -150,14 +150,18 @@ agentpool usage-summary --refresh --backend ccusage --provider claude-code --jso
|
|
|
150
150
|
|
|
151
151
|
`usage-summary` returns a `providers` object keyed by provider id. It is not
|
|
152
152
|
ordered and it is not a recommendation list. Each row includes `usable`,
|
|
153
|
-
`unusable_reason`, quota windows, confidence, staleness, and reset timing when
|
|
153
|
+
`unusable_reason`, quota windows, confidence, age/staleness, and reset timing when
|
|
154
154
|
the provider exposes it. The older CLI `capacity-summary` command is retained
|
|
155
155
|
as a human convenience alias; the MCP surface only exposes `get_usage_snapshot`
|
|
156
156
|
and the opt-in `get_usage_summary` tool.
|
|
157
157
|
|
|
158
158
|
The default buffer is `policy.min_remaining_percent = 10`. If any reported
|
|
159
159
|
quota window is below that buffer, the provider row is marked unusable for the
|
|
160
|
-
summary.
|
|
160
|
+
summary. Staleness is reported as age information only; it does not by itself
|
|
161
|
+
make a provider unusable. If you want cached summary reads to refresh
|
|
162
|
+
automatically after a threshold, set `policy.usage_auto_refresh_after_seconds`
|
|
163
|
+
in `~/.agentpool/config.yaml`.
|
|
164
|
+
AgentPool still does not pick an alternative provider for you.
|
|
161
165
|
MCP usage refreshes are intentionally bounded and may return `partial=true`;
|
|
162
166
|
use the CLI commands above when a shell-capable agent needs a complete live
|
|
163
167
|
refresh.
|
|
@@ -27,6 +27,9 @@ need to delegate coding-agent work.
|
|
|
27
27
|
live refresh. MCP `refresh=true` is bounded and can return `partial=true`
|
|
28
28
|
with unknown rows for slow providers.
|
|
29
29
|
- Treat usage rows as a provider-id map. They are not ordered and not ranked.
|
|
30
|
+
- Treat `stale` and `age_seconds` as age metadata, not as an instruction to
|
|
31
|
+
avoid a provider. If the user configured usage auto-refresh, cached summary
|
|
32
|
+
reads may refresh themselves before returning.
|
|
30
33
|
- Inspect provider models before spawning when the model is not already chosen:
|
|
31
34
|
- CLI: `agentpool models --provider <provider-id>`
|
|
32
35
|
- MCP: `get_provider_models(provider_id=...)`
|
|
@@ -9,11 +9,18 @@ Live probes are only run by explicit usage requests. Inventory remains non-invas
|
|
|
9
9
|
`agentpool usage-summary` returns a `providers` map keyed by provider id. The
|
|
10
10
|
CLI `capacity-summary` command is a human convenience alias; MCP does not expose
|
|
11
11
|
a capacity alias. Each row includes `usable`, `unusable_reason`, `stale`, and
|
|
12
|
-
`age_seconds`. `
|
|
13
|
-
|
|
12
|
+
`age_seconds`. `stale` is informational age metadata only; it does not by
|
|
13
|
+
itself make a provider unusable. `usable` is derived from install/auth status,
|
|
14
|
+
provider usage status, confidence, and reported quota windows. The default
|
|
14
15
|
buffer is `policy.min_remaining_percent = 10`, and it applies to every reported
|
|
15
16
|
quota window.
|
|
16
17
|
|
|
18
|
+
Cached summary reads can optionally refresh themselves when old enough. Set
|
|
19
|
+
`policy.usage_auto_refresh_after_seconds` to a non-negative number in
|
|
20
|
+
`~/.agentpool/config.yaml` to refresh `usage-summary` / `get_usage_summary`
|
|
21
|
+
when the cached summary data is missing or older than that threshold. Leave it
|
|
22
|
+
as `null` to keep refreshes explicit.
|
|
23
|
+
|
|
17
24
|
Usage windows carry a stable `kind` in addition to provider-specific names:
|
|
18
25
|
|
|
19
26
|
- `daily`
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "agentpool-cli"
|
|
7
|
-
version = "0.1.
|
|
7
|
+
version = "0.1.8"
|
|
8
8
|
description = "Make full use of every coding-agent subscription you pay for: a local CLI + MCP server that surfaces live usage limits and offloads work to providers with headroom."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.11"
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"name": "io.github.sidduHERE/agentpool",
|
|
4
4
|
"title": "AgentPool",
|
|
5
5
|
"description": "See each coding-agent subscription's live limits and offload work to one with headroom.",
|
|
6
|
-
"version": "0.1.
|
|
6
|
+
"version": "0.1.8",
|
|
7
7
|
"repository": {
|
|
8
8
|
"url": "https://github.com/sidduHERE/agentpool",
|
|
9
9
|
"source": "github"
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
{
|
|
14
14
|
"registryType": "pypi",
|
|
15
15
|
"identifier": "agentpool-cli",
|
|
16
|
-
"version": "0.1.
|
|
16
|
+
"version": "0.1.8",
|
|
17
17
|
"transport": {
|
|
18
18
|
"type": "stdio"
|
|
19
19
|
},
|
|
@@ -68,6 +68,7 @@ class PolicyConfig(BaseModel):
|
|
|
68
68
|
default_isolation: str = "read_only"
|
|
69
69
|
min_remaining_percent: int = 10
|
|
70
70
|
usage_stale_after_seconds: int = 1800
|
|
71
|
+
usage_auto_refresh_after_seconds: int | None = None
|
|
71
72
|
allowed_providers: list[str] = Field(default_factory=list)
|
|
72
73
|
denied_providers: list[str] = Field(default_factory=list)
|
|
73
74
|
block_on_usage_statuses: list[str] = Field(
|
|
@@ -326,6 +327,13 @@ def validate_config(config: AgentPoolConfig) -> dict[str, Any]:
|
|
|
326
327
|
errors.append("policy.allow_auto_routing must stay false in AgentPool v0.1")
|
|
327
328
|
if "auto" in config.providers:
|
|
328
329
|
errors.append("providers.auto is not allowed")
|
|
330
|
+
if config.policy.usage_stale_after_seconds < 0:
|
|
331
|
+
errors.append("policy.usage_stale_after_seconds must be non-negative")
|
|
332
|
+
if (
|
|
333
|
+
config.policy.usage_auto_refresh_after_seconds is not None
|
|
334
|
+
and config.policy.usage_auto_refresh_after_seconds < 0
|
|
335
|
+
):
|
|
336
|
+
errors.append("policy.usage_auto_refresh_after_seconds must be non-negative or null")
|
|
329
337
|
if "factory-droid" in config.providers:
|
|
330
338
|
warnings.append("factory-droid is a PRD compatibility name; use droid-cli for the droid binary")
|
|
331
339
|
for provider_id, reason in DEPRECATED_PROVIDER_IDS.items():
|
|
@@ -492,7 +492,8 @@ def privacy_doctor(manager: SessionManager) -> dict[str, Any]:
|
|
|
492
492
|
},
|
|
493
493
|
"usage_refresh": {
|
|
494
494
|
"inventory_runs_live_usage_probes": False,
|
|
495
|
-
"live_usage_requires_explicit_refresh":
|
|
495
|
+
"live_usage_requires_explicit_refresh": manager.config.policy.usage_auto_refresh_after_seconds is None,
|
|
496
|
+
"summary_auto_refresh_after_seconds": manager.config.policy.usage_auto_refresh_after_seconds,
|
|
496
497
|
"default_backend": "combined",
|
|
497
498
|
"optional_backends": {
|
|
498
499
|
"codexbar": detect_codexbar(),
|
|
@@ -155,9 +155,22 @@ class SessionManager:
|
|
|
155
155
|
for snapshot in snapshots:
|
|
156
156
|
self.store.save_usage_snapshot(snapshot)
|
|
157
157
|
source = "live_probe"
|
|
158
|
+
summary_backend = backend
|
|
158
159
|
else:
|
|
159
160
|
snapshots = self._configured_usage_snapshots(self.store.latest_usage_snapshots(provider_id), provider_id)
|
|
160
161
|
source = "sqlite_cache"
|
|
162
|
+
summary_backend = "cache"
|
|
163
|
+
if self._usage_summary_should_auto_refresh(snapshots, provider_id):
|
|
164
|
+
snapshots = self.registry.usage(
|
|
165
|
+
provider_id,
|
|
166
|
+
backend=backend,
|
|
167
|
+
allow_interactive=allow_interactive,
|
|
168
|
+
timeout_seconds=_remaining_seconds(deadline),
|
|
169
|
+
)
|
|
170
|
+
for snapshot in snapshots:
|
|
171
|
+
self.store.save_usage_snapshot(snapshot)
|
|
172
|
+
source = "live_probe"
|
|
173
|
+
summary_backend = backend
|
|
161
174
|
return {
|
|
162
175
|
**build_usage_summary(
|
|
163
176
|
snapshots,
|
|
@@ -167,7 +180,7 @@ class SessionManager:
|
|
|
167
180
|
),
|
|
168
181
|
"preferences": preferences_reference(),
|
|
169
182
|
"source": source,
|
|
170
|
-
"backend":
|
|
183
|
+
"backend": summary_backend,
|
|
171
184
|
"partial": _has_partial_usage(snapshots) or _has_partial_descriptors(descriptors),
|
|
172
185
|
}
|
|
173
186
|
|
|
@@ -865,6 +878,26 @@ class SessionManager:
|
|
|
865
878
|
configured = set(self.config.providers)
|
|
866
879
|
return [snapshot for snapshot in snapshots if snapshot.provider_id in configured]
|
|
867
880
|
|
|
881
|
+
def _usage_summary_should_auto_refresh(self, snapshots: list[Any], provider_id: str | None = None) -> bool:
|
|
882
|
+
threshold = self.config.policy.usage_auto_refresh_after_seconds
|
|
883
|
+
if threshold is None:
|
|
884
|
+
return False
|
|
885
|
+
if provider_id and not snapshots:
|
|
886
|
+
return True
|
|
887
|
+
if not provider_id:
|
|
888
|
+
configured = set(self.config.providers)
|
|
889
|
+
seen = {snapshot.provider_id for snapshot in snapshots}
|
|
890
|
+
if not configured.issubset(seen):
|
|
891
|
+
return True
|
|
892
|
+
now = datetime.now(timezone.utc)
|
|
893
|
+
for snapshot in snapshots:
|
|
894
|
+
checked_at = snapshot.checked_at
|
|
895
|
+
if checked_at.tzinfo is None:
|
|
896
|
+
checked_at = checked_at.replace(tzinfo=timezone.utc)
|
|
897
|
+
if max(0.0, (now - checked_at).total_seconds()) > threshold:
|
|
898
|
+
return True
|
|
899
|
+
return False
|
|
900
|
+
|
|
868
901
|
def _enforce_deadline(self, session: AgentSession) -> ObserveWorkerResponse | None:
|
|
869
902
|
deadline_at = session.metadata.get("deadline_at")
|
|
870
903
|
if not deadline_at or not active_state(session.state):
|
|
@@ -457,9 +457,8 @@ def _snapshot_usability(
|
|
|
457
457
|
}
|
|
458
458
|
for window in snapshot.windows
|
|
459
459
|
]
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
return _usable_reason(snapshot, windows, min_remaining_percent, descriptor, stale)
|
|
460
|
+
_ = stale_after_seconds
|
|
461
|
+
return _usable_reason(snapshot, windows, min_remaining_percent, descriptor)
|
|
463
462
|
|
|
464
463
|
|
|
465
464
|
def _is_quota_unusable_reason(reason: str | None) -> bool:
|
|
@@ -65,7 +65,7 @@ def _provider_summary(
|
|
|
65
65
|
]
|
|
66
66
|
age_seconds = max(0.0, (now - snapshot.checked_at).total_seconds())
|
|
67
67
|
stale = age_seconds > stale_after_seconds
|
|
68
|
-
usable, unusable_reason = _usable_reason(snapshot, windows, min_remaining_percent, descriptor
|
|
68
|
+
usable, unusable_reason = _usable_reason(snapshot, windows, min_remaining_percent, descriptor)
|
|
69
69
|
return {
|
|
70
70
|
"provider_id": snapshot.provider_id,
|
|
71
71
|
"status": snapshot.status.value if hasattr(snapshot.status, "value") else str(snapshot.status),
|
|
@@ -90,7 +90,6 @@ def _usable_reason(
|
|
|
90
90
|
windows: list[dict[str, Any]],
|
|
91
91
|
min_remaining_percent: int,
|
|
92
92
|
descriptor: ProviderDescriptor | None,
|
|
93
|
-
stale: bool,
|
|
94
93
|
) -> tuple[bool, str | None]:
|
|
95
94
|
if descriptor and not descriptor.installed:
|
|
96
95
|
return False, "not_installed"
|
|
@@ -110,8 +109,6 @@ def _usable_reason(
|
|
|
110
109
|
}
|
|
111
110
|
if confidence not in allowed_confidence:
|
|
112
111
|
return False, f"confidence_{confidence}"
|
|
113
|
-
if stale:
|
|
114
|
-
return False, "usage_stale"
|
|
115
112
|
for window in windows:
|
|
116
113
|
remaining = window["remaining_percent"]
|
|
117
114
|
if remaining is not None and remaining < min_remaining_percent:
|
|
@@ -842,6 +842,85 @@ def test_cached_usage_summary_filters_removed_provider_aliases(tmp_path: Path) -
|
|
|
842
842
|
assert "factory-droid" not in provider_ids
|
|
843
843
|
|
|
844
844
|
|
|
845
|
+
def test_usage_summary_auto_refresh_is_opt_in_and_bounded(tmp_path: Path) -> None:
|
|
846
|
+
class RefreshingRegistry:
|
|
847
|
+
def __init__(self) -> None:
|
|
848
|
+
self.calls: list[dict[str, object]] = []
|
|
849
|
+
|
|
850
|
+
def descriptors(self, include_usage: bool = False, timeout_seconds: float | None = None): # type: ignore[no-untyped-def]
|
|
851
|
+
return []
|
|
852
|
+
|
|
853
|
+
def usage(
|
|
854
|
+
self,
|
|
855
|
+
provider_id: str | None = None,
|
|
856
|
+
*,
|
|
857
|
+
backend: str = "combined",
|
|
858
|
+
allow_interactive: bool = True,
|
|
859
|
+
timeout_seconds: float | None = None,
|
|
860
|
+
): # type: ignore[no-untyped-def]
|
|
861
|
+
self.calls.append(
|
|
862
|
+
{
|
|
863
|
+
"provider_id": provider_id,
|
|
864
|
+
"backend": backend,
|
|
865
|
+
"allow_interactive": allow_interactive,
|
|
866
|
+
"timeout_seconds": timeout_seconds,
|
|
867
|
+
}
|
|
868
|
+
)
|
|
869
|
+
return [
|
|
870
|
+
CapacitySnapshot(
|
|
871
|
+
provider_id="codex-cli",
|
|
872
|
+
status=UsageStatus.AVAILABLE,
|
|
873
|
+
confidence=Confidence.OFFICIAL,
|
|
874
|
+
windows=[
|
|
875
|
+
UsageWindow(
|
|
876
|
+
name="5h",
|
|
877
|
+
kind=UsageWindowKind.FIVE_HOUR,
|
|
878
|
+
remaining_percent=90,
|
|
879
|
+
confidence=Confidence.OFFICIAL,
|
|
880
|
+
)
|
|
881
|
+
],
|
|
882
|
+
)
|
|
883
|
+
]
|
|
884
|
+
|
|
885
|
+
config = load_config(Path("__missing_agentpool_config__.yaml"))
|
|
886
|
+
config.storage = StorageConfig(db_path=str(tmp_path / "agentpool.sqlite"), artifact_root=str(tmp_path / "artifacts"))
|
|
887
|
+
config.policy.usage_auto_refresh_after_seconds = 1800
|
|
888
|
+
store = Store(tmp_path / "agentpool.sqlite")
|
|
889
|
+
store.save_usage_snapshot(
|
|
890
|
+
CapacitySnapshot(
|
|
891
|
+
provider_id="codex-cli",
|
|
892
|
+
status=UsageStatus.AVAILABLE,
|
|
893
|
+
confidence=Confidence.OFFICIAL,
|
|
894
|
+
checked_at=now_utc() - timedelta(hours=2),
|
|
895
|
+
windows=[
|
|
896
|
+
UsageWindow(
|
|
897
|
+
name="5h",
|
|
898
|
+
kind=UsageWindowKind.FIVE_HOUR,
|
|
899
|
+
remaining_percent=80,
|
|
900
|
+
confidence=Confidence.OFFICIAL,
|
|
901
|
+
)
|
|
902
|
+
],
|
|
903
|
+
)
|
|
904
|
+
)
|
|
905
|
+
registry = RefreshingRegistry()
|
|
906
|
+
manager = SessionManager(config=config, store=store, registry=registry, runtime=RecordingRuntime()) # type: ignore[arg-type]
|
|
907
|
+
|
|
908
|
+
summary = manager.usage_summary(provider_id="codex-cli", refresh=False, backend="native", timeout_seconds=7)
|
|
909
|
+
|
|
910
|
+
assert summary["source"] == "live_probe"
|
|
911
|
+
assert summary["backend"] == "native"
|
|
912
|
+
assert summary["providers"]["codex-cli"]["usable"] is True
|
|
913
|
+
assert summary["providers"]["codex-cli"]["stale"] is False
|
|
914
|
+
assert registry.calls == [
|
|
915
|
+
{
|
|
916
|
+
"provider_id": "codex-cli",
|
|
917
|
+
"backend": "native",
|
|
918
|
+
"allow_interactive": True,
|
|
919
|
+
"timeout_seconds": pytest.approx(7, abs=0.1),
|
|
920
|
+
}
|
|
921
|
+
]
|
|
922
|
+
|
|
923
|
+
|
|
845
924
|
def test_failed_worktree_spawn_rolls_back_worktree_and_branch(tmp_path: Path) -> None:
|
|
846
925
|
repo = tmp_path / "repo"
|
|
847
926
|
repo.mkdir()
|
|
@@ -120,7 +120,7 @@ def test_usage_summary_auth_install_and_unknown_confidence_block_usable() -> Non
|
|
|
120
120
|
assert summary["counts"]["usable"] == 0
|
|
121
121
|
|
|
122
122
|
|
|
123
|
-
def
|
|
123
|
+
def test_usage_summary_reports_stale_without_blocking_usable() -> None:
|
|
124
124
|
snapshot = CapacitySnapshot(
|
|
125
125
|
provider_id="codex-cli",
|
|
126
126
|
status=UsageStatus.AVAILABLE,
|
|
@@ -138,9 +138,10 @@ def test_usage_summary_marks_stale_snapshots_unusable() -> None:
|
|
|
138
138
|
|
|
139
139
|
summary = build_usage_summary([snapshot], stale_after_seconds=1800, provider_descriptors=[_descriptor("codex-cli")])
|
|
140
140
|
|
|
141
|
-
|
|
142
|
-
assert
|
|
143
|
-
assert
|
|
141
|
+
row = summary["providers"]["codex-cli"]
|
|
142
|
+
assert row["usable"] is True
|
|
143
|
+
assert row["unusable_reason"] is None
|
|
144
|
+
assert row["stale"] is True
|
|
144
145
|
|
|
145
146
|
|
|
146
147
|
def _descriptor(provider_id: str, installed: bool = True, auth_status: str = "authenticated") -> ProviderDescriptor:
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{agentpool_cli-0.1.7 → agentpool_cli-0.1.8}/src/agentpool/fixtures/fake_agents/fake_common.py
RENAMED
|
File without changes
|
|
File without changes
|
{agentpool_cli-0.1.7 → agentpool_cli-0.1.8}/src/agentpool/fixtures/fake_agents/fake_idle_agent.py
RENAMED
|
File without changes
|
{agentpool_cli-0.1.7 → agentpool_cli-0.1.8}/src/agentpool/fixtures/fake_agents/fake_limit_agent.py
RENAMED
|
File without changes
|
{agentpool_cli-0.1.7 → agentpool_cli-0.1.8}/src/agentpool/fixtures/fake_agents/fake_patch_agent.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{agentpool_cli-0.1.7 → agentpool_cli-0.1.8}/tests/fixtures/provider_model_catalog_golden.json
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|