agentadmit 1.13.0 → 1.14.0
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.
- checksums.yaml +4 -4
- data/README.md +23 -0
- data/lib/agentadmit/introspection_client.rb +82 -2
- data/lib/agentadmit/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 4614ce271d54fa958f580f2180349bbdad92764566efe17d3a459c0fca62815c
|
|
4
|
+
data.tar.gz: 5c46517cd626d99eda4482cf47fdb14ae98b13f9a451216bcb117f6fd236f9c8
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 716a550dd977efc852984419c4f79e8fba5e28fd282a3c5b8630251fc48dba14548ac6e1950d2c548057acf84862d9c023525cdfe1ba5e8c2ef60e5ca0d021a7
|
|
7
|
+
data.tar.gz: c568d905a1930f6376109751c2fe3c464a181049f359b50f280872f9b18042d82d75a73e628119ed0d8f51270bfa843b39ca4585f7867182c4ed3242d6d8000f
|
data/README.md
CHANGED
|
@@ -398,6 +398,29 @@ result.user_intent # => "Book my flights to the Austin offsite in October" or ni
|
|
|
398
398
|
|
|
399
399
|
`user_intent` is nullable -- connections issued without one (or by servers that predate the field) read as `nil`. Months later, a review screen can answer "is this still appropriate?" with the user's own stated boundary, not just the app's. Like purpose, it is a review-time record and never an enforcement input; authorization decisions ride scopes, connection status, and consent.
|
|
400
400
|
|
|
401
|
+
## Agent Designation
|
|
402
|
+
|
|
403
|
+
On AgentAdmit's consent page the user says which agent app a connection is for and, optionally, which AI model family it mainly uses. That is the user's statement, recorded with the grant; it is not a verified identity of the agent. Separately, an agent may report its own platform and model when it redeems the connection token; that is unverified too.
|
|
404
|
+
|
|
405
|
+
The verify response reports both on every introspection, and the SDK surfaces them on the result as `designated_agent` and `agent_reported` (`nil` when the connection carries none):
|
|
406
|
+
|
|
407
|
+
```ruby
|
|
408
|
+
result = AgentAdmit::IntrospectionClient.new.verify(token)
|
|
409
|
+
|
|
410
|
+
# {"agent_id" => ..., "name" => ..., "source" => "user_declared",
|
|
411
|
+
# "model" => {"model_id" => ..., "name" => ...} or nil}
|
|
412
|
+
named = result.designated_agent
|
|
413
|
+
# {"platform" => ..., "model" => {"id" => ..., "version" => ...} or nil}
|
|
414
|
+
reported = result.agent_reported
|
|
415
|
+
|
|
416
|
+
puts "Named by the user: #{named ? named['name'] : '(not named)'}"
|
|
417
|
+
puts "Reported by the agent: #{reported&.dig('model', 'id') || '(nothing)'}"
|
|
418
|
+
```
|
|
419
|
+
|
|
420
|
+
Use `designated_agent["name"]` when you tell a user which of their connections did something: it is the name they chose. `agent_id` and `model["model_id"]` are the ids of your list entries, or `nil` when the user typed a name that is not on your list.
|
|
421
|
+
|
|
422
|
+
Do not branch authorization on either field. They are records for people reviewing a connection, not a gate. Scope checks, connection status, and consent verdicts remain the only decision inputs. To act on a designation, use the hosted revoke API (`designated_agent_id`, `designated_model_id`) or your list's block setting in the dashboard.
|
|
423
|
+
|
|
401
424
|
## App-Attested Presence
|
|
402
425
|
|
|
403
426
|
If your app gates token minting behind its own embedded passkey/WebAuthn ceremony, AgentAdmit never witnesses that ceremony (it is origin-bound), so by default the hosted service reports `presence.verified: false` for those connections. Attest the ceremony fact at issuance to close that gap -- AFTER verifying and consuming your own fresh, purpose-bound attestation:
|
|
@@ -34,7 +34,8 @@ module AgentAdmit
|
|
|
34
34
|
:sub, :role, :app_id, :jti, :exp, :consent,
|
|
35
35
|
:presence, :purpose, :user_intent,
|
|
36
36
|
:action_confirmation, :audit_row_id,
|
|
37
|
-
:consumed_receipt,
|
|
37
|
+
:consumed_receipt, :designated_agent,
|
|
38
|
+
:agent_reported, keyword_init: true) do
|
|
38
39
|
def has_scope?(scope)
|
|
39
40
|
scopes.include?(scope)
|
|
40
41
|
end
|
|
@@ -94,6 +95,24 @@ module AgentAdmit
|
|
|
94
95
|
# `consumed_receipt` (Hash or nil) is a replay diagnostic for an
|
|
95
96
|
# already-consumed confirm-each-time attestation. It is not a fresh
|
|
96
97
|
# authorization and must not be treated as permission to run.
|
|
98
|
+
|
|
99
|
+
# `designated_agent` (Hash or nil) is what the USER said on AgentAdmit's
|
|
100
|
+
# consent page: which agent app the connection is for and, optionally,
|
|
101
|
+
# which AI model family it mainly uses:
|
|
102
|
+
# {"agent_id" => String|nil, "name" => String,
|
|
103
|
+
# "source" => "user_declared",
|
|
104
|
+
# "model" => {"model_id" => String|nil, "name" => String} | nil}.
|
|
105
|
+
# It is the user's statement, recorded with the grant; it is not a
|
|
106
|
+
# verified identity of the agent. `agent_id` / `model_id` are nil when
|
|
107
|
+
# the user typed a name that is not on the app's list.
|
|
108
|
+
|
|
109
|
+
# `agent_reported` (Hash or nil) is what the AGENT reported about itself
|
|
110
|
+
# when it redeemed the connection token:
|
|
111
|
+
# {"platform" => String|nil,
|
|
112
|
+
# "model" => {"id" => String, "version" => String|nil} | nil}.
|
|
113
|
+
# Unverified. Both fields are review-time records only, never an
|
|
114
|
+
# enforcement input; authorization decisions ride scopes, connection
|
|
115
|
+
# status, and consent.
|
|
97
116
|
end
|
|
98
117
|
|
|
99
118
|
class << self
|
|
@@ -192,6 +211,58 @@ module AgentAdmit
|
|
|
192
211
|
"row_hash" => raw["row_hash"].is_a?(String) ? raw["row_hash"] : nil }
|
|
193
212
|
end
|
|
194
213
|
|
|
214
|
+
##
|
|
215
|
+
# The user's agent designation from a verify response, or nil when the
|
|
216
|
+
# block is absent, null, or not a user-declared designation. Strict: a
|
|
217
|
+
# non-empty String name and source "user_declared", or the whole block
|
|
218
|
+
# is dropped. A malformed model is dropped and the app is kept. This is
|
|
219
|
+
# what the user said on the consent page, not a verified identity.
|
|
220
|
+
#
|
|
221
|
+
# @param raw [Object] the `designated_agent` value from the verify body
|
|
222
|
+
# @return [Hash, nil]
|
|
223
|
+
#
|
|
224
|
+
def parse_designated_agent(raw)
|
|
225
|
+
return nil unless raw.is_a?(Hash)
|
|
226
|
+
name = raw["name"]
|
|
227
|
+
return nil unless name.is_a?(String) && !name.empty?
|
|
228
|
+
return nil unless raw["source"] == "user_declared"
|
|
229
|
+
|
|
230
|
+
m = raw["model"]
|
|
231
|
+
model =
|
|
232
|
+
if m.is_a?(Hash) && m["name"].is_a?(String) && !m["name"].empty?
|
|
233
|
+
{ "model_id" => m["model_id"].is_a?(String) ? m["model_id"] : nil,
|
|
234
|
+
"name" => m["name"] }
|
|
235
|
+
end
|
|
236
|
+
|
|
237
|
+
{ "agent_id" => raw["agent_id"].is_a?(String) ? raw["agent_id"] : nil,
|
|
238
|
+
"name" => name,
|
|
239
|
+
"source" => "user_declared",
|
|
240
|
+
"model" => model }
|
|
241
|
+
end
|
|
242
|
+
|
|
243
|
+
##
|
|
244
|
+
# What the agent reported about itself at token exchange, or nil when
|
|
245
|
+
# the block carries neither a platform nor a model id. Unverified.
|
|
246
|
+
#
|
|
247
|
+
# @param raw [Object] the `agent_reported` value from the verify body
|
|
248
|
+
# @return [Hash, nil]
|
|
249
|
+
#
|
|
250
|
+
def parse_agent_reported(raw)
|
|
251
|
+
return nil unless raw.is_a?(Hash)
|
|
252
|
+
|
|
253
|
+
platform = raw["platform"]
|
|
254
|
+
platform = nil unless platform.is_a?(String) && !platform.empty?
|
|
255
|
+
m = raw["model"]
|
|
256
|
+
model =
|
|
257
|
+
if m.is_a?(Hash) && m["id"].is_a?(String) && !m["id"].empty?
|
|
258
|
+
{ "id" => m["id"],
|
|
259
|
+
"version" => m["version"].is_a?(String) ? m["version"] : nil }
|
|
260
|
+
end
|
|
261
|
+
return nil if platform.nil? && model.nil?
|
|
262
|
+
|
|
263
|
+
{ "platform" => platform, "model" => model }
|
|
264
|
+
end
|
|
265
|
+
|
|
195
266
|
##
|
|
196
267
|
# The agent's X-AgentAdmit-Action-Attestation header from a Rack env:
|
|
197
268
|
# first value only, trimmed, capped at 120 characters. nil when absent
|
|
@@ -408,6 +479,13 @@ module AgentAdmit
|
|
|
408
479
|
audit_row_id = nil unless audit_row_id.is_a?(String)
|
|
409
480
|
consumed_receipt = self.class.parse_consumed_receipt(data["consumed_receipt"])
|
|
410
481
|
|
|
482
|
+
# Agent designation (1.14.0): what the user said the connection is
|
|
483
|
+
# for, and what the agent reported about itself. Review-time records,
|
|
484
|
+
# never an enforcement input, so a malformed block is dropped rather
|
|
485
|
+
# than failing the verify.
|
|
486
|
+
designated_agent = self.class.parse_designated_agent(data["designated_agent"])
|
|
487
|
+
agent_reported = self.class.parse_agent_reported(data["agent_reported"])
|
|
488
|
+
|
|
411
489
|
return IntrospectionResult.new(
|
|
412
490
|
user_id: data["user_id"],
|
|
413
491
|
connection_id: data["connection_id"],
|
|
@@ -424,7 +502,9 @@ module AgentAdmit
|
|
|
424
502
|
user_intent: user_intent,
|
|
425
503
|
action_confirmation: action_confirmation,
|
|
426
504
|
audit_row_id: audit_row_id,
|
|
427
|
-
consumed_receipt: consumed_receipt
|
|
505
|
+
consumed_receipt: consumed_receipt,
|
|
506
|
+
designated_agent: designated_agent,
|
|
507
|
+
agent_reported: agent_reported
|
|
428
508
|
)
|
|
429
509
|
end
|
|
430
510
|
|
data/lib/agentadmit/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: agentadmit
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.
|
|
4
|
+
version: 1.14.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Christopher Emerson
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-
|
|
11
|
+
date: 2026-11-06 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: json
|