Skip to content

Commit 548ee38

Browse files
authored
Expose refreshable on CredentialItem (#67)
## Summary `GET /v1/credentials` returns a new `refreshable` field per credential: `True` when a live refresh token can still mint a replacement for that credential. The endpoint also now includes an expired credential while that flag holds. Previously an agent idle past its 24h access expiry disappeared from the list entirely even though its 90-day refresh token could still renew it, so there was nothing left to revoke. Callers that render the list should treat `refreshable` as "this credential can still come back", and can filter on `expires_at` themselves if they want only currently-valid access tokens. Version bumped to 2.6.0: additive optional field, no caller has to change anything. Matches the node-sdk change field for field. ## Type of change - [ ] Bug fix (no breaking change) - [x] New feature (no breaking change) - [ ] Breaking change (existing callers must update) - [ ] Docs, tests, or internal maintenance only ## Public API `CredentialItem` gains `refreshable: NotRequired[bool]`. Optional, so a client pointed at a build that does not emit it still type-checks and reads as absent. No signature or wire-format change; no migration required. Worth knowing even though it is not a type change: the list can now contain credentials whose `expires_at` is in the past. Code that assumed every returned credential was currently valid should check `expires_at` rather than assume. ## Test plan `test_list_credentials_success` covers both wire shapes: a credential carrying `"refreshable": False`, and one omitting the key entirely (asserting `.get()` returns `None` rather than raising). Reproduce with `uv run pytest`. ## Checklist - [x] Tests cover the new behavior, and the suite passes locally - [x] Lint, format, and type checks pass - [x] Docs and README examples updated if the public surface changed - [x] No secrets, credentials, or personal data in the diff or the tests
1 parent 3846bb0 commit 548ee38

3 files changed

Lines changed: 10 additions & 1 deletion

File tree

agentscore/types.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -420,6 +420,10 @@ class CredentialItem(TypedDict):
420420
created_at: str
421421
expires_at: str | None
422422
last_used_at: str | None
423+
# True when a live refresh token can still mint a replacement for this
424+
# credential. An expired credential is listed only while this holds, since
425+
# revoking it is what stops the renewal.
426+
refreshable: NotRequired[bool]
423427

424428

425429
class _CredentialCreateResponseRequired(TypedDict):

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
44

55
[project]
66
name = "agentscore-py"
7-
version = "2.5.3"
7+
version = "2.6.0"
88
description = "Python client for the AgentScore APIs"
99
readme = "README.md"
1010
license = "MIT"

tests/test_client.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1245,6 +1245,7 @@ def test_create_credential_raises_on_error():
12451245
"created_at": "2024-01-01T00:00:00Z",
12461246
"expires_at": "2024-04-01T00:00:00Z",
12471247
"last_used_at": None,
1248+
"refreshable": False,
12481249
},
12491250
{
12501251
"id": "cred_def456",
@@ -1266,6 +1267,10 @@ def test_list_credentials_success():
12661267
assert len(result["credentials"]) == 2
12671268
assert result["credentials"][0]["id"] == "cred_abc123"
12681269
assert result["credentials"][1]["id"] == "cred_def456"
1270+
assert result["credentials"][0]["refreshable"] is False
1271+
# Optional on the wire: a credential listed by an older API build carries no
1272+
# such key, and reading it must not raise.
1273+
assert result["credentials"][1].get("refreshable") is None
12691274

12701275

12711276
@respx.mock

0 commit comments

Comments
 (0)