agentops-rpc 0.1.0__py3-none-any.whl

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.
@@ -0,0 +1,393 @@
1
+ from __future__ import annotations
2
+
3
+ from .a2a import A2AMessage, A2AMessagePart, A2AMessageRequest, A2ATask, A2ATaskResponse
4
+ from .agent_create import (
5
+ CreateAgentMcpServer,
6
+ CreateAgentRequest,
7
+ CreateAgentResponse,
8
+ CreateAgentSkill,
9
+ CreateWorkerTokenRequest,
10
+ CreateWorkerTokenResponse,
11
+ WorkerTokenStatusResponse,
12
+ WorkerTokenSummaryResponse,
13
+ )
14
+ from .agents import (
15
+ BUILTIN_USE_CASE_SLUGS,
16
+ BUILTIN_USE_CASES,
17
+ CONNECTIVITY_CHECK_USE_CASE,
18
+ LABEL_MAX_LENGTH,
19
+ LABEL_RULE_HINT,
20
+ PUSH_CAPABILITY,
21
+ REVIEW_CAPABILITY,
22
+ SAMPLE_RUN_PROMPT,
23
+ SAMPLE_RUN_USE_CASE,
24
+ USE_CASE_INPUT_KEY,
25
+ USE_CASE_SLUG_HINT,
26
+ USE_CASE_SLUG_MAX_LENGTH,
27
+ AgentGenerationInfo,
28
+ AgentInstanceResponse,
29
+ AgentManifest,
30
+ BuiltinUseCaseSlug,
31
+ CreateAgentDraftRequest,
32
+ HeartbeatRequest,
33
+ HeartbeatResponse,
34
+ ScheduleTriggerManifest,
35
+ SkillDetail,
36
+ SkillReference,
37
+ SkillSourceAgent,
38
+ SkillSummary,
39
+ SlackKeywordTriggerManifest,
40
+ UpdateAgentDraftRequest,
41
+ UpdateAgentLabelsRequest,
42
+ UseCase,
43
+ WorkerInstanceInfo,
44
+ label_violations,
45
+ )
46
+ from .artifacts import ArtifactDetail, ArtifactSummary
47
+ from .credentials import (
48
+ SECRETS_METADATA_KEY,
49
+ BindCredentialRequest,
50
+ CreateCredentialRequest,
51
+ CredentialBinding,
52
+ CredentialMetadata,
53
+ CredentialStatus,
54
+ ReplaceCredentialValueRequest,
55
+ UpdateCredentialMetadataRequest,
56
+ WorkerSecretsResponse,
57
+ )
58
+ from .events import (
59
+ HANDLER_FAILED_LOG_PREFIX,
60
+ BatchEventsRequest,
61
+ IngestRunEventsRequest,
62
+ IngestRunEventsResponse,
63
+ LogEventIn,
64
+ LogEventResponse,
65
+ RunEventIn,
66
+ RunMessageResponse,
67
+ TraceEventIn,
68
+ TraceEventResponse,
69
+ )
70
+ from .grader import (
71
+ GRADER_CAPABILITY,
72
+ GradeContext,
73
+ GradeResult,
74
+ )
75
+ from .hosted_agents import (
76
+ HostedAgentCreateRequest,
77
+ HostedAgentImage,
78
+ HostedAgentMcpServer,
79
+ HostedAgentResponse,
80
+ HostedAgentSkill,
81
+ HostedAgentUpdateRequest,
82
+ )
83
+ from .insights import (
84
+ FailureCategoryCount,
85
+ IssueRecommendation,
86
+ RunIssue,
87
+ SaveIssueRecommendationRequest,
88
+ WorkspaceIssuesResponse,
89
+ )
90
+ from .integrations import (
91
+ CreateIntegrationConnectionRequest,
92
+ CreateIntegrationConnectionResponse,
93
+ IntegrationAuthMode,
94
+ IntegrationCatalogEntry,
95
+ IntegrationConnection,
96
+ IntegrationConnectionDetail,
97
+ IntegrationCredentialField,
98
+ IntegrationFieldType,
99
+ IntegrationOAuthMetadata,
100
+ IntegrationProviderCredentials,
101
+ IntegrationStatus,
102
+ StartIntegrationAppRequest,
103
+ StartIntegrationAppResponse,
104
+ StartIntegrationOAuthRequest,
105
+ StartIntegrationOAuthResponse,
106
+ TestIntegrationConnectionResponse,
107
+ )
108
+ from .issues import (
109
+ IssueDetail,
110
+ IssueGrouping,
111
+ IssueListResponse,
112
+ IssueLog,
113
+ IssueRca,
114
+ IssueSignal,
115
+ IssueSource,
116
+ IssueTimelineEvent,
117
+ IssueWorker,
118
+ UpdateIssueRequest,
119
+ )
120
+ from .knowledge_base import (
121
+ AttachAgentToKBRequest,
122
+ CreateKnowledgeBaseRequest,
123
+ KBAgentGrant,
124
+ KBChunkSummary,
125
+ KBDocumentDetail,
126
+ KBDocumentMetadata,
127
+ KBDocumentOrigin,
128
+ KBDocumentStatus,
129
+ KBDocumentSummary,
130
+ KBSearchFilters,
131
+ KBSearchHit,
132
+ KBSearchRequest,
133
+ KBSearchResponse,
134
+ KnowledgeBaseDetail,
135
+ KnowledgeBaseSummary,
136
+ )
137
+ from .quality import (
138
+ AgentQualityRollup,
139
+ AutoScoreSeries,
140
+ RunQualityRow,
141
+ SkillQualityRollup,
142
+ UngradedQueueResponse,
143
+ )
144
+ from .runs import (
145
+ ActivityBucket,
146
+ AddRunMessageRequest,
147
+ AgentActivitySummary,
148
+ ClaimRunResponse,
149
+ CompleteRunRequest,
150
+ CreateRunRequest,
151
+ FailRunRequest,
152
+ InvokeAgentRequest,
153
+ InvokeAgentResponse,
154
+ ModelActivity,
155
+ PollNextRunRequest,
156
+ RunDetail,
157
+ RunJob,
158
+ RunSummary,
159
+ RunUsage,
160
+ StartRunDirectRequest,
161
+ StartRunRequest,
162
+ TopCostRunSummary,
163
+ UpdateRunOutputRequest,
164
+ WorkerInvokeAgent,
165
+ WorkerInvokeRequest,
166
+ WorkerInvokeResponse,
167
+ WorkspaceActivityResponse,
168
+ )
169
+ from .schedules import CreateScheduleRequest, ScheduleResponse, UpdateScheduleRequest
170
+ from .scores import (
171
+ CreateScoreRequest,
172
+ ScoreListResponse,
173
+ ScoreResponse,
174
+ ScoreTargetType,
175
+ ScoreValue,
176
+ UpdateScoreRequest,
177
+ )
178
+ from .session import SESSION_SNAPSHOT_METADATA_KEY, SessionSnapshot, SnapshotComponent, SnapshotComponentKind
179
+ from .types import (
180
+ ACTIVE_RUN_STATUSES,
181
+ OBSERVABILITY_EVENT_TYPES,
182
+ OWNED_RUN_STATUSES,
183
+ TERMINAL_RUN_STATUSES,
184
+ AgentStatus,
185
+ DeployStatus,
186
+ EventType,
187
+ HookEventName,
188
+ HookEventType,
189
+ LogLevel,
190
+ MessageRole,
191
+ MessageType,
192
+ RunChannel,
193
+ RunEventType,
194
+ RunStatus,
195
+ RunTransport,
196
+ SpanKind,
197
+ TraceEventType,
198
+ )
199
+ from .worker_catalog import (
200
+ WorkerCatalogCredential,
201
+ WorkerCatalogDeployRequest,
202
+ WorkerCatalogEntryResponse,
203
+ WorkerCatalogIntegration,
204
+ WorkerCatalogSelfHostedDeployRequest,
205
+ WorkerCatalogSelfHostedDeployResponse,
206
+ )
207
+
208
+ __all__ = [
209
+ "ACTIVE_RUN_STATUSES",
210
+ "HANDLER_FAILED_LOG_PREFIX",
211
+ "OWNED_RUN_STATUSES",
212
+ "TERMINAL_RUN_STATUSES",
213
+ "ArtifactDetail",
214
+ "ArtifactSummary",
215
+ "A2AMessage",
216
+ "A2AMessagePart",
217
+ "A2AMessageRequest",
218
+ "A2ATask",
219
+ "A2ATaskResponse",
220
+ "AddRunMessageRequest",
221
+ "AgentActivitySummary",
222
+ "AgentInstanceResponse",
223
+ "AgentGenerationInfo",
224
+ "AgentManifest",
225
+ "AgentStatus",
226
+ "DeployStatus",
227
+ "PUSH_CAPABILITY",
228
+ "REVIEW_CAPABILITY",
229
+ "UseCase",
230
+ "BuiltinUseCaseSlug",
231
+ "BUILTIN_USE_CASES",
232
+ "BUILTIN_USE_CASE_SLUGS",
233
+ "CONNECTIVITY_CHECK_USE_CASE",
234
+ "SAMPLE_RUN_USE_CASE",
235
+ "SAMPLE_RUN_PROMPT",
236
+ "USE_CASE_INPUT_KEY",
237
+ "USE_CASE_SLUG_HINT",
238
+ "USE_CASE_SLUG_MAX_LENGTH",
239
+ "ActivityBucket",
240
+ "BatchEventsRequest",
241
+ "ClaimRunResponse",
242
+ "CompleteRunRequest",
243
+ "BindCredentialRequest",
244
+ "CreateAgentDraftRequest",
245
+ "CreateAgentMcpServer",
246
+ "CreateAgentRequest",
247
+ "CreateAgentResponse",
248
+ "CreateAgentSkill",
249
+ "CreateWorkerTokenRequest",
250
+ "CreateWorkerTokenResponse",
251
+ "WorkerTokenStatusResponse",
252
+ "WorkerTokenSummaryResponse",
253
+ "CreateCredentialRequest",
254
+ "CreateRunRequest",
255
+ "CredentialBinding",
256
+ "CredentialMetadata",
257
+ "CredentialStatus",
258
+ "ReplaceCredentialValueRequest",
259
+ "WorkerSecretsResponse",
260
+ "WorkerInstanceInfo",
261
+ "SECRETS_METADATA_KEY",
262
+ "SESSION_SNAPSHOT_METADATA_KEY",
263
+ "SessionSnapshot",
264
+ "SnapshotComponent",
265
+ "SnapshotComponentKind",
266
+ "UpdateCredentialMetadataRequest",
267
+ "EventType",
268
+ "FailRunRequest",
269
+ "GRADER_CAPABILITY",
270
+ "GradeContext",
271
+ "GradeResult",
272
+ "HeartbeatRequest",
273
+ "HeartbeatResponse",
274
+ "HookEventName",
275
+ "HookEventType",
276
+ "HostedAgentCreateRequest",
277
+ "HostedAgentImage",
278
+ "HostedAgentMcpServer",
279
+ "HostedAgentResponse",
280
+ "HostedAgentSkill",
281
+ "HostedAgentUpdateRequest",
282
+ "WorkerCatalogCredential",
283
+ "WorkerCatalogDeployRequest",
284
+ "WorkerCatalogEntryResponse",
285
+ "WorkerCatalogIntegration",
286
+ "WorkerCatalogSelfHostedDeployRequest",
287
+ "WorkerCatalogSelfHostedDeployResponse",
288
+ "IngestRunEventsRequest",
289
+ "IngestRunEventsResponse",
290
+ "InvokeAgentRequest",
291
+ "InvokeAgentResponse",
292
+ "LogEventIn",
293
+ "LogEventResponse",
294
+ "LogLevel",
295
+ "MessageRole",
296
+ "MessageType",
297
+ "ModelActivity",
298
+ "OBSERVABILITY_EVENT_TYPES",
299
+ "PollNextRunRequest",
300
+ "RunDetail",
301
+ "RunEventIn",
302
+ "RunChannel",
303
+ "RunEventType",
304
+ "RunJob",
305
+ "RunMessageResponse",
306
+ "RunStatus",
307
+ "RunSummary",
308
+ "RunTransport",
309
+ "RunUsage",
310
+ "ScheduleResponse",
311
+ "ScheduleTriggerManifest",
312
+ "SlackKeywordTriggerManifest",
313
+ "CreateIntegrationConnectionRequest",
314
+ "CreateIntegrationConnectionResponse",
315
+ "CreateScheduleRequest",
316
+ "IntegrationAuthMode",
317
+ "IntegrationCatalogEntry",
318
+ "IntegrationConnection",
319
+ "IntegrationConnectionDetail",
320
+ "IntegrationCredentialField",
321
+ "IntegrationFieldType",
322
+ "IntegrationOAuthMetadata",
323
+ "IntegrationProviderCredentials",
324
+ "IntegrationStatus",
325
+ "StartIntegrationAppRequest",
326
+ "StartIntegrationAppResponse",
327
+ "StartIntegrationOAuthRequest",
328
+ "StartIntegrationOAuthResponse",
329
+ "TestIntegrationConnectionResponse",
330
+ "UpdateScheduleRequest",
331
+ "SkillDetail",
332
+ "SkillReference",
333
+ "SkillSourceAgent",
334
+ "SkillSummary",
335
+ "SpanKind",
336
+ "StartRunDirectRequest",
337
+ "StartRunRequest",
338
+ "TraceEventIn",
339
+ "TraceEventResponse",
340
+ "TraceEventType",
341
+ "TopCostRunSummary",
342
+ "AttachAgentToKBRequest",
343
+ "CreateKnowledgeBaseRequest",
344
+ "KBAgentGrant",
345
+ "KBChunkSummary",
346
+ "KBDocumentDetail",
347
+ "KBDocumentMetadata",
348
+ "KBDocumentOrigin",
349
+ "KBDocumentStatus",
350
+ "KBDocumentSummary",
351
+ "KBSearchFilters",
352
+ "KBSearchHit",
353
+ "KBSearchRequest",
354
+ "KBSearchResponse",
355
+ "KnowledgeBaseDetail",
356
+ "KnowledgeBaseSummary",
357
+ "UpdateAgentDraftRequest",
358
+ "UpdateAgentLabelsRequest",
359
+ "LABEL_MAX_LENGTH",
360
+ "LABEL_RULE_HINT",
361
+ "label_violations",
362
+ "UpdateRunOutputRequest",
363
+ "WorkerInvokeAgent",
364
+ "WorkerInvokeRequest",
365
+ "WorkerInvokeResponse",
366
+ "WorkspaceActivityResponse",
367
+ "FailureCategoryCount",
368
+ "IssueRecommendation",
369
+ "RunIssue",
370
+ "SaveIssueRecommendationRequest",
371
+ "WorkspaceIssuesResponse",
372
+ "IssueDetail",
373
+ "IssueGrouping",
374
+ "IssueListResponse",
375
+ "IssueLog",
376
+ "IssueRca",
377
+ "IssueSignal",
378
+ "IssueSource",
379
+ "IssueTimelineEvent",
380
+ "IssueWorker",
381
+ "UpdateIssueRequest",
382
+ "CreateScoreRequest",
383
+ "ScoreListResponse",
384
+ "ScoreResponse",
385
+ "ScoreTargetType",
386
+ "ScoreValue",
387
+ "UpdateScoreRequest",
388
+ "AgentQualityRollup",
389
+ "AutoScoreSeries",
390
+ "RunQualityRow",
391
+ "SkillQualityRollup",
392
+ "UngradedQueueResponse",
393
+ ]
agentops_rpc/a2a.py ADDED
@@ -0,0 +1,75 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import Any
4
+
5
+ from pydantic import BaseModel, ConfigDict, Field
6
+
7
+
8
+ class A2AMessagePart(BaseModel):
9
+ model_config = ConfigDict(populate_by_name=True)
10
+
11
+ kind: str = "text"
12
+ text: str | None = None
13
+ data: dict[str, Any] | None = None
14
+
15
+ def to_dict(self) -> dict[str, Any]:
16
+ return self.model_dump(exclude_none=True)
17
+
18
+ @classmethod
19
+ def from_dict(cls, d: dict[str, Any]) -> A2AMessagePart:
20
+ return cls.model_validate(d)
21
+
22
+
23
+ class A2AMessage(BaseModel):
24
+ model_config = ConfigDict(populate_by_name=True)
25
+
26
+ role: str = "user"
27
+ message_id: str | None = Field(default=None, alias="messageId")
28
+ task_id: str | None = Field(default=None, alias="taskId")
29
+ context_id: str | None = Field(default=None, alias="contextId")
30
+ parts: list[A2AMessagePart] = Field(default_factory=list)
31
+ metadata: dict[str, Any] = Field(default_factory=dict)
32
+
33
+ def to_dict(self) -> dict[str, Any]:
34
+ d: dict[str, Any] = {"kind": "message", "role": self.role}
35
+ if self.message_id is not None:
36
+ d["messageId"] = self.message_id
37
+ if self.task_id is not None:
38
+ d["taskId"] = self.task_id
39
+ if self.context_id is not None:
40
+ d["contextId"] = self.context_id
41
+ d["parts"] = [p.to_dict() for p in self.parts]
42
+ d["metadata"] = self.metadata
43
+ return d
44
+
45
+ @classmethod
46
+ def from_dict(cls, d: dict[str, Any]) -> A2AMessage:
47
+ return cls.model_validate(d)
48
+
49
+
50
+ class A2AMessageRequest(BaseModel):
51
+ message: A2AMessage
52
+ metadata: dict[str, Any] = Field(default_factory=dict, title="A2AMessageMetadata")
53
+
54
+
55
+ class A2ATask(BaseModel):
56
+ model_config = ConfigDict(populate_by_name=True)
57
+
58
+ id: str
59
+ context_id: str | None = Field(default=None, alias="contextId")
60
+ status: dict[str, Any] = Field(default_factory=dict)
61
+ artifacts: list[dict[str, Any]] = Field(default_factory=list)
62
+ metadata: dict[str, Any] = Field(default_factory=dict, title="A2ATaskMetadata")
63
+
64
+ def to_dict(self) -> dict[str, Any]:
65
+ return {
66
+ "kind": "task",
67
+ "id": self.id,
68
+ "contextId": self.context_id,
69
+ "status": self.status,
70
+ "artifacts": self.artifacts,
71
+ "metadata": self.metadata,
72
+ }
73
+
74
+
75
+ A2ATaskResponse = A2ATask
@@ -0,0 +1,176 @@
1
+ from __future__ import annotations
2
+
3
+ import re
4
+ from typing import Literal
5
+
6
+ from pydantic import BaseModel, ConfigDict, Field, field_validator, model_validator
7
+
8
+ # agentId is used as a Helm release name, a Kubernetes resource name, and the
9
+ # worker-token credential suffix, so it must be a valid RFC 1123 DNS label and fit
10
+ # Helm's 53-char release-name limit.
11
+ _AGENT_ID_RE = re.compile(r"^[a-z0-9]([-a-z0-9]*[a-z0-9])?$")
12
+ _AGENT_ID_MAX_LEN = 53
13
+
14
+
15
+ def validate_agent_id(v: str) -> str:
16
+ """Validate & normalize an operator-chosen ``agentId`` (the friendly per-account ``id_slug``).
17
+
18
+ Shared by every agent-creation entry point — full self-deploy create, worker-token mint, and the
19
+ Add-Agent wizard draft — so the same shape rule guards them all (the draft is the *first* step the
20
+ operator hits, so skipping it there let invalid slugs like ``"Bad Slug"`` reach the DB). Strips
21
+ surrounding whitespace, then enforces the RFC 1123 DNS-label shape + 53-char Helm limit.
22
+ """
23
+ v = v.strip()
24
+ if not v:
25
+ raise ValueError("must not be blank")
26
+ if len(v) > _AGENT_ID_MAX_LEN or not _AGENT_ID_RE.match(v):
27
+ raise ValueError(
28
+ f"must be a valid RFC 1123 DNS label (lowercase alphanumeric and '-', start/end alphanumeric, <= {_AGENT_ID_MAX_LEN} chars)"
29
+ )
30
+ return v
31
+
32
+
33
+ def _camel(s: str) -> str:
34
+ head, *rest = s.split("_")
35
+ return head + "".join(w.capitalize() for w in rest)
36
+
37
+
38
+ class _Camel(BaseModel):
39
+ model_config = ConfigDict(alias_generator=_camel, populate_by_name=True, extra="forbid")
40
+
41
+
42
+ class CreateAgentSkill(_Camel):
43
+ id: str
44
+ content: str
45
+
46
+
47
+ class CreateAgentMcpServer(_Camel):
48
+ # Transport-specific keys (type/command/args/env/url) vary; allow extras.
49
+ model_config = ConfigDict(alias_generator=_camel, populate_by_name=True, extra="allow")
50
+
51
+ name: str
52
+
53
+
54
+ class CreateAgentRequest(_Camel):
55
+ agent_id: str
56
+ instructions: str
57
+ display_name: str = ""
58
+ model: str = "claude-sonnet-4-6"
59
+ skills: list[CreateAgentSkill] = Field(default_factory=list)
60
+ mcp_servers: list[CreateAgentMcpServer] = Field(default_factory=list)
61
+ replica_count: int = Field(default=1, ge=1)
62
+ # LLM key — provide EXACTLY ONE mode:
63
+ # BYO: anthropic_api_key rendered into the chart Secret (lives in the user's cluster).
64
+ # CP-managed: credential_ref the agent fetches from the control plane at boot.
65
+ anthropic_api_key: str | None = None
66
+ credential_ref: str | None = None
67
+ # Optional TTL for the minted worker token, so local/dev deploys don't need a permanent
68
+ # credential. None keeps today's non-expiring behavior.
69
+ ttl_seconds: int | None = Field(default=None, ge=1, le=86400)
70
+
71
+ @field_validator("instructions")
72
+ @classmethod
73
+ def _not_blank(cls, v: str) -> str:
74
+ if not v.strip():
75
+ raise ValueError("must not be blank")
76
+ return v
77
+
78
+ @field_validator("agent_id")
79
+ @classmethod
80
+ def _valid_agent_id(cls, v: str) -> str:
81
+ return validate_agent_id(v)
82
+
83
+ @field_validator("anthropic_api_key", "credential_ref")
84
+ @classmethod
85
+ def _blank_to_none(cls, v: str | None) -> str | None:
86
+ # Normalize blank/whitespace to None so downstream code can rely on truthiness.
87
+ return v.strip() if v and v.strip() else None
88
+
89
+ @model_validator(mode="after")
90
+ def _exactly_one_llm_mode(self) -> CreateAgentRequest:
91
+ byo = self.anthropic_api_key is not None
92
+ cp = self.credential_ref is not None
93
+ if byo and cp:
94
+ raise ValueError("provide exactly one of anthropicApiKey (BYO) or credentialRef (CP-managed), not both")
95
+ if not byo and not cp:
96
+ raise ValueError("provide an LLM key via anthropicApiKey (BYO) or credentialRef (CP-managed)")
97
+ return self
98
+
99
+
100
+ class CreateAgentResponse(BaseModel):
101
+ model_config = ConfigDict(populate_by_name=True)
102
+
103
+ command: str
104
+ values: str
105
+ agent_id: str = Field(serialization_alias="agentId", validation_alias="agentId")
106
+ worker_token_hint: str = Field(serialization_alias="workerTokenHint", validation_alias="workerTokenHint")
107
+ expires_at: str | None = Field(default=None, serialization_alias="expiresAt", validation_alias="expiresAt")
108
+
109
+
110
+ class CreateWorkerTokenRequest(_Camel):
111
+ agent_id: str
112
+ credential_ref: str | None = None
113
+ ttl_seconds: int | None = Field(default=None, ge=1, le=86400)
114
+
115
+ @field_validator("agent_id")
116
+ @classmethod
117
+ def _valid_agent_id(cls, v: str) -> str:
118
+ return validate_agent_id(v)
119
+
120
+
121
+ class CreateWorkerTokenResponse(BaseModel):
122
+ model_config = ConfigDict(populate_by_name=True)
123
+
124
+ token: str
125
+ agent_id: str = Field(serialization_alias="agentId", validation_alias="agentId")
126
+ worker_token_hint: str = Field(serialization_alias="workerTokenHint", validation_alias="workerTokenHint")
127
+ expires_at: str | None = Field(default=None, serialization_alias="expiresAt", validation_alias="expiresAt")
128
+
129
+
130
+ class WorkerTokenStatusResponse(BaseModel):
131
+ """Read-only lifecycle status of an agent's worker token. Carries NO secret material — the token
132
+ plaintext is shown only once, on create/rotate. Powers the "view + revoke" UI surface."""
133
+
134
+ model_config = ConfigDict(populate_by_name=True)
135
+
136
+ # The friendly agent handle the caller asked about, echoed back for correlation in list views.
137
+ agent_id: str = Field(serialization_alias="agentId", validation_alias="agentId")
138
+ exists: bool
139
+ created_at: str | None = Field(default=None, serialization_alias="createdAt", validation_alias="createdAt")
140
+ last_rotated_at: str | None = Field(default=None, serialization_alias="lastRotatedAt", validation_alias="lastRotatedAt")
141
+ expires_at: str | None = Field(default=None, serialization_alias="expiresAt", validation_alias="expiresAt")
142
+ # True when the token carried a TTL that has already elapsed (it no longer authenticates).
143
+ expired: bool = False
144
+
145
+
146
+ # The lifecycle of the *agent* a worker token belongs to, as far as the control plane can see it.
147
+ # "unregistered" is the not-yet-installed case: the token was minted (e.g. by the self-deploy render)
148
+ # but no agent_instances row exists yet — the worker has never heart-beaten. draft/online/offline
149
+ # mirror the agent's computed status once a row exists.
150
+ WorkerTokenAgentStatus = Literal["online", "offline", "draft", "unregistered"]
151
+
152
+
153
+ class WorkerTokenSummaryResponse(BaseModel):
154
+ """One worker token in an account, for the Settings token table. Carries NO secret material.
155
+
156
+ Sourced from the ``worker-token-<agent_id>`` credentials directly (not from agent_instances), so
157
+ tokens of not-yet-installed agents — minted but never heart-beaten, hence with no agent row —
158
+ still appear. Agent metadata (``id_slug``/``name``/``is_archived``/``agent_status``) is null-ish
159
+ for those orphan tokens: only the opaque ``agent_id`` is known until the worker registers.
160
+ """
161
+
162
+ model_config = ConfigDict(populate_by_name=True)
163
+
164
+ # Opaque internal agent id (``agt_…``) the token is bound to — the stable handle for revoke.
165
+ agent_id: str = Field(serialization_alias="agentId", validation_alias="agentId")
166
+ # Friendly per-account handle; null when the agent has never registered (rotate needs it, so a
167
+ # null slug means the UI offers revoke-only — you cannot safely rotate a token with no slug).
168
+ id_slug: str | None = Field(default=None, serialization_alias="idSlug", validation_alias="idSlug")
169
+ name: str | None = None
170
+ is_archived: bool = Field(default=False, serialization_alias="isArchived", validation_alias="isArchived")
171
+ agent_status: WorkerTokenAgentStatus = Field(default="unregistered", serialization_alias="agentStatus", validation_alias="agentStatus")
172
+ exists: bool = True
173
+ created_at: str | None = Field(default=None, serialization_alias="createdAt", validation_alias="createdAt")
174
+ last_rotated_at: str | None = Field(default=None, serialization_alias="lastRotatedAt", validation_alias="lastRotatedAt")
175
+ expires_at: str | None = Field(default=None, serialization_alias="expiresAt", validation_alias="expiresAt")
176
+ expired: bool = False