agentic-runner-contracts 3.4.2__tar.gz → 3.6.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.
- {agentic_runner_contracts-3.4.2 → agentic_runner_contracts-3.6.0}/PKG-INFO +1 -1
- {agentic_runner_contracts-3.4.2 → agentic_runner_contracts-3.6.0}/src/agentic_runner_contracts/__init__.py +11 -1
- {agentic_runner_contracts-3.4.2 → agentic_runner_contracts-3.6.0}/src/agentic_runner_contracts/activity_io.py +12 -1
- {agentic_runner_contracts-3.4.2 → agentic_runner_contracts-3.6.0}/src/agentic_runner_contracts/runner_registration.py +20 -7
- {agentic_runner_contracts-3.4.2 → agentic_runner_contracts-3.6.0}/src/agentic_runner_contracts/runtime_context.py +14 -4
- {agentic_runner_contracts-3.4.2 → agentic_runner_contracts-3.6.0}/src/agentic_runner_contracts/sealed_credential.py +69 -0
- {agentic_runner_contracts-3.4.2 → agentic_runner_contracts-3.6.0}/.gitignore +0 -0
- {agentic_runner_contracts-3.4.2 → agentic_runner_contracts-3.6.0}/LICENSE +0 -0
- {agentic_runner_contracts-3.4.2 → agentic_runner_contracts-3.6.0}/README.md +0 -0
- {agentic_runner_contracts-3.4.2 → agentic_runner_contracts-3.6.0}/pyproject.toml +0 -0
- {agentic_runner_contracts-3.4.2 → agentic_runner_contracts-3.6.0}/src/agentic_runner_contracts/channel_messages.py +0 -0
- {agentic_runner_contracts-3.4.2 → agentic_runner_contracts-3.6.0}/src/agentic_runner_contracts/epics.py +0 -0
- {agentic_runner_contracts-3.4.2 → agentic_runner_contracts-3.6.0}/src/agentic_runner_contracts/github_port.py +0 -0
- {agentic_runner_contracts-3.4.2 → agentic_runner_contracts-3.6.0}/src/agentic_runner_contracts/grants/__init__.py +0 -0
- {agentic_runner_contracts-3.4.2 → agentic_runner_contracts-3.6.0}/src/agentic_runner_contracts/grants/catalogue.py +0 -0
- {agentic_runner_contracts-3.4.2 → agentic_runner_contracts-3.6.0}/src/agentic_runner_contracts/grants/evaluator.py +0 -0
- {agentic_runner_contracts-3.4.2 → agentic_runner_contracts-3.6.0}/src/agentic_runner_contracts/grants/model.py +0 -0
- {agentic_runner_contracts-3.4.2 → agentic_runner_contracts-3.6.0}/src/agentic_runner_contracts/grants/seam.py +0 -0
- {agentic_runner_contracts-3.4.2 → agentic_runner_contracts-3.6.0}/src/agentic_runner_contracts/grants/snapshot.py +0 -0
- {agentic_runner_contracts-3.4.2 → agentic_runner_contracts-3.6.0}/src/agentic_runner_contracts/incidents.py +0 -0
- {agentic_runner_contracts-3.4.2 → agentic_runner_contracts-3.6.0}/src/agentic_runner_contracts/llm_usage.py +0 -0
- {agentic_runner_contracts-3.4.2 → agentic_runner_contracts-3.6.0}/src/agentic_runner_contracts/public_metadata.py +0 -0
- {agentic_runner_contracts-3.4.2 → agentic_runner_contracts-3.6.0}/src/agentic_runner_contracts/py.typed +0 -0
- {agentic_runner_contracts-3.4.2 → agentic_runner_contracts-3.6.0}/src/agentic_runner_contracts/questions.py +0 -0
- {agentic_runner_contracts-3.4.2 → agentic_runner_contracts-3.6.0}/src/agentic_runner_contracts/redaction.py +0 -0
- {agentic_runner_contracts-3.4.2 → agentic_runner_contracts-3.6.0}/src/agentic_runner_contracts/routing.py +0 -0
- {agentic_runner_contracts-3.4.2 → agentic_runner_contracts-3.6.0}/src/agentic_runner_contracts/runner_signature.py +0 -0
- {agentic_runner_contracts-3.4.2 → agentic_runner_contracts-3.6.0}/src/agentic_runner_contracts/swarm.py +0 -0
- {agentic_runner_contracts-3.4.2 → agentic_runner_contracts-3.6.0}/src/agentic_runner_contracts/triage.py +0 -0
- {agentic_runner_contracts-3.4.2 → agentic_runner_contracts-3.6.0}/src/agentic_runner_contracts/user_sources.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: agentic-runner-contracts
|
|
3
|
-
Version: 3.
|
|
3
|
+
Version: 3.6.0
|
|
4
4
|
Summary: Activity I/O contracts and Public Metadata name builders shared by the Agentic OS platform and the installable Runner
|
|
5
5
|
License-Expression: AGPL-3.0-only
|
|
6
6
|
License-File: LICENSE
|
|
@@ -32,6 +32,16 @@ the package the floor is *about*, and read back through ``agentic-runner --versi
|
|
|
32
32
|
# Additive; an older payload parses with no Skills. But the context forbids extra keys,
|
|
33
33
|
# so a Runner one minor behind refuses a context that carries `skills` -- the platform
|
|
34
34
|
# must not send the field until its Runners are on 2.7.
|
|
35
|
-
|
|
35
|
+
# 3.5.0 (local-agents 12): `WorkerRuntimeContext.cli_kind` widens from the two known
|
|
36
|
+
# kinds to the registration pattern `^[a-z][a-z0-9_]{0,31}$`. Additive: every payload that
|
|
37
|
+
# parsed still parses; a Runner one minor behind refuses a context naming a new kind, which
|
|
38
|
+
# fails that Directive closed, and routing sends a kind only to a Runner that reports it.
|
|
39
|
+
# 3.6.0 (local-agents 05): `ContractDeviceLoginInput.method`,
|
|
40
|
+
# `ContractDeviceLoginResult.sign_in_id`, `ContractDeviceLoginStatusResult.auth_method` /
|
|
41
|
+
# `subscription_type`, `SealedSignInCode` on `HeartbeatAck.sign_in_codes` and
|
|
42
|
+
# `SignInCodeRelay` on `HeartbeatEnvelope.sign_in_codes`. Additive; the envelope omits the
|
|
43
|
+
# key while it is empty. But the ack forbids extra keys, so the platform must not relay a
|
|
44
|
+
# code to a Runner below 3.6.
|
|
45
|
+
__version__ = "3.6.0"
|
|
36
46
|
|
|
37
47
|
__all__ = ["__version__"]
|
|
@@ -1237,6 +1237,10 @@ class ContractDeviceLoginInput:
|
|
|
1237
1237
|
# The Runner whose disk holds that harness root, and so whose queue the activity is
|
|
1238
1238
|
# dispatched on (PRD issue 37). Optional only for a start that predates the split.
|
|
1239
1239
|
runner_id: str | None = None
|
|
1240
|
+
# Claude Code only (local-agents 05): `oauth_token` (`claude setup-token`, the
|
|
1241
|
+
# long-lived token) or `claude_ai` (`claude auth login --claudeai`). None means
|
|
1242
|
+
# `oauth_token`, and the only one a shared Runner accepts.
|
|
1243
|
+
method: str | None = None
|
|
1240
1244
|
|
|
1241
1245
|
|
|
1242
1246
|
@dataclass(frozen=True)
|
|
@@ -1254,8 +1258,11 @@ class ContractDeviceLoginResult:
|
|
|
1254
1258
|
contract_id: str
|
|
1255
1259
|
runtime_kind: str
|
|
1256
1260
|
verification_uri: str
|
|
1261
|
+
# Empty for Claude Code, whose flow has no device code: the browser shows the person a
|
|
1262
|
+
# code to paste back instead, sealed and relayed against `sign_in_id`.
|
|
1257
1263
|
user_code: str
|
|
1258
1264
|
expires_at: str
|
|
1265
|
+
sign_in_id: str = ""
|
|
1259
1266
|
|
|
1260
1267
|
|
|
1261
1268
|
@dataclass(frozen=True)
|
|
@@ -1279,6 +1286,10 @@ class ContractDeviceLoginStatusResult:
|
|
|
1279
1286
|
runtime_kind: str
|
|
1280
1287
|
token_present: bool
|
|
1281
1288
|
delivered_at: str | None
|
|
1289
|
+
# Claude Code only, from `claude auth status --json` (local-agents 05): which login
|
|
1290
|
+
# the CLI found (`oauth_token`, `claude.ai`) and the plan it reports.
|
|
1291
|
+
auth_method: str | None = None
|
|
1292
|
+
subscription_type: str | None = None
|
|
1282
1293
|
|
|
1283
1294
|
|
|
1284
1295
|
class ContractDeviceLoginActivities(Protocol):
|
|
@@ -1298,7 +1309,7 @@ class ContractDeviceLoginStatusActivities(Protocol):
|
|
|
1298
1309
|
async def check_contract_device_login_status(
|
|
1299
1310
|
self, request: ContractDeviceLoginStatusInput
|
|
1300
1311
|
) -> ContractDeviceLoginStatusResult:
|
|
1301
|
-
"""
|
|
1312
|
+
"""Whether this Contract's harness is signed in; never opens its login."""
|
|
1302
1313
|
|
|
1303
1314
|
|
|
1304
1315
|
@dataclass(frozen=True)
|
|
@@ -43,7 +43,12 @@ from pydantic import (
|
|
|
43
43
|
|
|
44
44
|
from agentic_runner_contracts.channel_messages import TranscriptRequest
|
|
45
45
|
from agentic_runner_contracts.llm_usage import UsageRecord
|
|
46
|
-
from agentic_runner_contracts.sealed_credential import
|
|
46
|
+
from agentic_runner_contracts.sealed_credential import (
|
|
47
|
+
OpenedCredential,
|
|
48
|
+
SealedCredential,
|
|
49
|
+
SealedSignInCode,
|
|
50
|
+
SignInCodeRelay,
|
|
51
|
+
)
|
|
47
52
|
from agentic_runner_contracts.user_sources import SourceStatus, UserSourceAssignment
|
|
48
53
|
|
|
49
54
|
# 23 item 4 (Buildkite's own numbers): heartbeat every 30 s, stale at 3 min. Stale is a
|
|
@@ -544,15 +549,19 @@ class HeartbeatEnvelope(BaseModel):
|
|
|
544
549
|
# Console-v2 issue 29: each Runner-hosted Tool Server's latest start, per slug. A
|
|
545
550
|
# server not started since the process began is absent.
|
|
546
551
|
tool_servers: list[ToolServerHealth] = Field(default_factory=list, max_length=64)
|
|
552
|
+
# Local-agents 05: what became of each sign-in code the last ack relayed.
|
|
553
|
+
sign_in_codes: list[SignInCodeRelay] = Field(default_factory=list, max_length=16)
|
|
547
554
|
|
|
548
555
|
@model_serializer(mode="wrap")
|
|
549
|
-
def
|
|
550
|
-
#
|
|
551
|
-
#
|
|
552
|
-
# the control plane on
|
|
556
|
+
def _omit_empty_optional_lists(self, handler: SerializerFunctionWrapHandler) -> Any:
|
|
557
|
+
# An older control plane forbids extra keys, so a Runner with nothing to say in
|
|
558
|
+
# these sends the body that control plane already parses. One that has something
|
|
559
|
+
# needs the control plane on the contracts minor that added the field first.
|
|
553
560
|
body = handler(self)
|
|
554
|
-
if isinstance(body, dict)
|
|
555
|
-
|
|
561
|
+
if isinstance(body, dict):
|
|
562
|
+
for name in ("tool_servers", "sign_in_codes"):
|
|
563
|
+
if not body.get(name):
|
|
564
|
+
body.pop(name, None)
|
|
556
565
|
return body
|
|
557
566
|
|
|
558
567
|
|
|
@@ -600,6 +609,10 @@ class HeartbeatAck(BaseModel):
|
|
|
600
609
|
# the control plane hands a request over on exactly one beat -- and answered on the
|
|
601
610
|
# signed stream (`TRANSCRIPT_DELIVERIES_PATH`), never on the ack itself.
|
|
602
611
|
transcript_requests: list[TranscriptRequest] = Field(default_factory=list, max_length=16)
|
|
612
|
+
# Local-agents 05: one-time browser sign-in codes, sealed to this installation's
|
|
613
|
+
# Recipient Key. Relayed once each, like `transcript_requests`; the outcome comes back
|
|
614
|
+
# on the next envelope's `sign_in_codes`.
|
|
615
|
+
sign_in_codes: list[SealedSignInCode] = Field(default_factory=list, max_length=16)
|
|
603
616
|
|
|
604
617
|
|
|
605
618
|
class DirectiveTokenRequest(BaseModel):
|
|
@@ -9,9 +9,16 @@ the same reason: both sides resolve this context, through clients of their own.
|
|
|
9
9
|
from __future__ import annotations
|
|
10
10
|
|
|
11
11
|
import re
|
|
12
|
-
from typing import Any, Literal, Protocol
|
|
13
|
-
|
|
14
|
-
from pydantic import
|
|
12
|
+
from typing import Annotated, Any, Literal, Protocol
|
|
13
|
+
|
|
14
|
+
from pydantic import (
|
|
15
|
+
BaseModel,
|
|
16
|
+
ConfigDict,
|
|
17
|
+
Field,
|
|
18
|
+
StringConstraints,
|
|
19
|
+
field_validator,
|
|
20
|
+
model_validator,
|
|
21
|
+
)
|
|
15
22
|
|
|
16
23
|
_FORBIDDEN_SECRET_VALUE_FIELDS = frozenset(
|
|
17
24
|
{
|
|
@@ -98,7 +105,10 @@ class WorkerRuntimeContext(BaseModel):
|
|
|
98
105
|
|
|
99
106
|
work_record_id: str
|
|
100
107
|
profile_slug: str
|
|
101
|
-
|
|
108
|
+
# Any harness a Profile names, the pattern the Runner's registration already reports
|
|
109
|
+
# (local-agents 12 item 6). Which runtime serves it is the Runner's choice; a kind the
|
|
110
|
+
# Runner does not serve fails closed there.
|
|
111
|
+
cli_kind: Annotated[str, StringConstraints(pattern=r"^[a-z][a-z0-9_]{0,31}$")]
|
|
102
112
|
repo: str
|
|
103
113
|
base_branch: str
|
|
104
114
|
reviewer: str | None = None
|
|
@@ -33,6 +33,7 @@ import base64
|
|
|
33
33
|
import binascii
|
|
34
34
|
import hashlib
|
|
35
35
|
from datetime import timedelta
|
|
36
|
+
from enum import StrEnum
|
|
36
37
|
from typing import Annotated, Final
|
|
37
38
|
from uuid import UUID
|
|
38
39
|
|
|
@@ -43,11 +44,16 @@ __all__ = [
|
|
|
43
44
|
"HONEST_STATEMENT",
|
|
44
45
|
"RENEWAL_INTERVAL",
|
|
45
46
|
"SEAL_DOMAIN",
|
|
47
|
+
"SIGN_IN_CODE_DOMAIN",
|
|
46
48
|
"OpenedCredential",
|
|
47
49
|
"SealedCredential",
|
|
50
|
+
"SealedSignInCode",
|
|
51
|
+
"SignInCodeOutcome",
|
|
52
|
+
"SignInCodeRelay",
|
|
48
53
|
"SlotDelivery",
|
|
49
54
|
"delivery_binding",
|
|
50
55
|
"key_fingerprint",
|
|
56
|
+
"sign_in_code_binding",
|
|
51
57
|
]
|
|
52
58
|
|
|
53
59
|
# Domain separation. Version it rather than the wire: a second construction would be a
|
|
@@ -55,6 +61,11 @@ __all__ = [
|
|
|
55
61
|
# something it misread.
|
|
56
62
|
SEAL_DOMAIN: Final[str] = "agentic-os/sealed-credential/v1"
|
|
57
63
|
|
|
64
|
+
# A one-time sign-in code is sealed to the same Recipient Key but is not a credential slot
|
|
65
|
+
# (local-agents 05): its own prefix means a code can never open as a slot value, nor a slot
|
|
66
|
+
# value as a code.
|
|
67
|
+
SIGN_IN_CODE_DOMAIN: Final[str] = "agentic-os/sign-in-code/v1"
|
|
68
|
+
|
|
58
69
|
# 22 A7: the Runner regenerates its keypair on this cadence, re-seals every slot to the
|
|
59
70
|
# new key and destroys the old private half only once every slot reports the new key id.
|
|
60
71
|
RENEWAL_INTERVAL: Final[timedelta] = timedelta(days=30)
|
|
@@ -91,6 +102,21 @@ def delivery_binding(*, contract_id: UUID | str, slot: str, recipient_key_id: st
|
|
|
91
102
|
return "|".join((SEAL_DOMAIN, str(contract_id), slot, recipient_key_id)).encode("utf-8")
|
|
92
103
|
|
|
93
104
|
|
|
105
|
+
def sign_in_code_binding(
|
|
106
|
+
*, contract_id: UUID | str, sign_in_id: str, recipient_key_id: str
|
|
107
|
+
) -> bytes:
|
|
108
|
+
"""The AEAD associated data one sealed sign-in code is bound to (local-agents 05).
|
|
109
|
+
|
|
110
|
+
Bound to the sign-in the Runner started, so a code lifted from one sign-in and
|
|
111
|
+
replayed against another -- or another Contract's -- does not open. The browser builds
|
|
112
|
+
the same bytes in TypeScript; see :func:`delivery_binding` for why this lives here.
|
|
113
|
+
"""
|
|
114
|
+
|
|
115
|
+
return "|".join((SIGN_IN_CODE_DOMAIN, str(contract_id), sign_in_id, recipient_key_id)).encode(
|
|
116
|
+
"utf-8"
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
|
|
94
120
|
def key_fingerprint(public_key: str) -> str:
|
|
95
121
|
"""The Recipient Key's fingerprint, for out-of-band comparison (22 A4).
|
|
96
122
|
|
|
@@ -169,3 +195,46 @@ class SlotDelivery(BaseModel):
|
|
|
169
195
|
delivered: int = Field(ge=0)
|
|
170
196
|
stale: bool = False
|
|
171
197
|
host_party: str = ""
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
SignInId = Annotated[str, StringConstraints(pattern=r"^si_[0-9a-f]{32}$")]
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
class SealedSignInCode(BaseModel):
|
|
204
|
+
"""A one-time browser sign-in code, sealed to the Runner that started the sign-in.
|
|
205
|
+
|
|
206
|
+
Carried on the heartbeat ack once and never in a Temporal payload (local-agents 05):
|
|
207
|
+
the platform relays ciphertext it cannot open and deletes it on delivery or expiry.
|
|
208
|
+
"""
|
|
209
|
+
|
|
210
|
+
model_config = ConfigDict(extra="forbid")
|
|
211
|
+
|
|
212
|
+
contract_id: UUID
|
|
213
|
+
sign_in_id: SignInId
|
|
214
|
+
recipient_key_id: KeyId
|
|
215
|
+
ciphertext: Annotated[str, StringConstraints(min_length=1, max_length=2048)]
|
|
216
|
+
|
|
217
|
+
def binding(self) -> bytes:
|
|
218
|
+
return sign_in_code_binding(
|
|
219
|
+
contract_id=self.contract_id,
|
|
220
|
+
sign_in_id=self.sign_in_id,
|
|
221
|
+
recipient_key_id=self.recipient_key_id,
|
|
222
|
+
)
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
class SignInCodeOutcome(StrEnum):
|
|
226
|
+
"""What the Runner did with one relayed code -- the reason the console shows."""
|
|
227
|
+
|
|
228
|
+
WRITTEN = "written"
|
|
229
|
+
UNKNOWN = "unknown"
|
|
230
|
+
EXPIRED = "expired"
|
|
231
|
+
UNOPENABLE = "unopenable"
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
class SignInCodeRelay(BaseModel):
|
|
235
|
+
"""One relayed code's outcome, reported on the next heartbeat. Never the code."""
|
|
236
|
+
|
|
237
|
+
model_config = ConfigDict(extra="forbid")
|
|
238
|
+
|
|
239
|
+
sign_in_id: SignInId
|
|
240
|
+
outcome: SignInCodeOutcome
|
|
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
|