@decidio/sdk 0.11.0 → 0.12.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.
package/openapi.yaml CHANGED
@@ -5,7 +5,7 @@
5
5
  openapi: 3.1.0
6
6
  info:
7
7
  title: Decidio Agent Gate
8
- version: 0.11.0 # tracks the SDK release - checked by packages/conformance/manifests.mjs
8
+ version: 0.12.0 # tracks the SDK release - checked by packages/conformance/manifests.mjs
9
9
  description: >
10
10
  Authorize an AI-agent action (proceed | route | block), confirm the captured outcome, and
11
11
  receive the signed resume webhook when a routed action is decided. Every outcome seals a
@@ -87,6 +87,15 @@ components:
87
87
  same requestCommitment AND the same requester, action, amount and scope (the server binds that
88
88
  tuple itself) - otherwise 409 IDEMPOTENCY_CONFLICT; an earlier decision is never handed back for a
89
89
  changed request.
90
+
91
+
92
+ SDK 0.12.0 sends one BY DEFAULT beside every requestCommitment: a keyed HMAC over a fresh id for the
93
+ call and the commitment (or over the caller's own key, or an engine's own id for the call - an
94
+ OpenAI tool call id, a Temporal workflow run and activity, an Inngest run), and retries a network
95
+ failure, a 5xx or a non-AGENT_RATE 429 with the SAME key: a retry of a request still waiting is
96
+ answered with the one already waiting, and no retry runs an action twice. (A request is not
97
+ guaranteed single: an auto-approval whose answer is lost on every try, or a keyless request a policy
98
+ blocks, can make a second decision.) Without key material there is no commitment and no key.
90
99
  AuthorizeResponse:
91
100
  type: object
92
101
  required: [decision, decisionId, receiptId]
@@ -111,14 +120,27 @@ components:
111
120
  and `receipt` carries only the fields fixed at create. A sealed auto-approval replays as `proceed`
112
121
  only when THIS call could itself auto-approve (a verified, replay-protected identity or a trusted
113
122
  transport); anything else answers `route` or `block` - ask GET /agent/status for the real state.
123
+ openTwin:
124
+ type: boolean
125
+ description: >
126
+ present (true, beside idempotentReplay) when this KEYLESS request joined the same request from the
127
+ same agent that is still waiting for a person: nothing new was created, counted or summoned, and the
128
+ caller waits on that decision. The request may be shared, so a caller that gives up waiting must not
129
+ withdraw it (the SDKs skip their timeout withdraw after either flag; the server refuses a shared
130
+ withdraw with 409 SHARED_REQUEST).
114
131
  StatusResponse:
115
132
  type: object
116
133
  required: [state]
134
+ description: >
135
+ Reading the status claims NOTHING: a dashboard, the CLI or a second watcher can poll it freely. The run
136
+ that acts on a person's approval collects it with POST /agent/claim/{decisionId}.
117
137
  properties:
118
138
  state:
119
139
  type: string
120
140
  enum: [pending, approved, auto_approved, rejected, blocked]
121
- reason: { type: string }
141
+ decisionId: { type: string }
142
+ receiptId: { type: [string, "null"] }
143
+ reason: { type: [string, "null"], description: the approver's reason, tokenized; never a cleartext name }
122
144
  requestCommitment:
123
145
  type: [string, "null"]
124
146
  description: >
@@ -126,6 +148,27 @@ components:
126
148
  sent). The SDK treats an approval as covering the request it is about to run only when this
127
149
  equals its own recompute; a gate that returns no commitment for a request the SDK bound is
128
150
  refused (commitment_missing), never trusted.
151
+ spent:
152
+ type: boolean
153
+ description: >
154
+ SDK 0.8.0: true once an execution is recorded on this approval - one approval covers one action,
155
+ once. The SDKs refuse to run on a spent approval and ask again.
156
+ grantExpiresAt:
157
+ type: [string, "null"]
158
+ format: date-time
159
+ description: >
160
+ SDK 0.8.0: when a granted decision's approval lapses (its seal's time plus the gate's TTL); null
161
+ otherwise. A granted decision with no expiry has a grant of unknown age, which the SDKs treat as
162
+ expired.
163
+ handedBackAt:
164
+ type: [string, "null"]
165
+ format: date-time
166
+ description: when an idempotent replay handed this granted decision back to a retry (null otherwise)
167
+ claimed:
168
+ type: boolean
169
+ description: >
170
+ SDK 0.12.0: whether a run has collected this person's approval (POST /agent/claim) - never by whom.
171
+ Informational: it claims nothing, and a caller decides a claim only by claiming.
129
172
  ConfirmRequest:
130
173
  type: object
131
174
  required: [decisionId, capturedResponse, source]
@@ -328,6 +371,119 @@ paths:
328
371
  check runs BEFORE any lookup, so this response reveals nothing about existence.
329
372
  "401": { description: missing/invalid agent token }
330
373
  "404": { description: unknown decision }
374
+ /agent/claim/{decisionId}:
375
+ post:
376
+ summary: Collect a person's approval for ONE run, once, right before acting on it (SDK 0.12.0)
377
+ description: >
378
+ Several programs can wait on one approval (callers that asked the same request, a second poller); only
379
+ the run that collects it runs it. The first claim wins. The same claimKey claiming again is the same run
380
+ retrying (a lost answer) and is answered the same, with `again: true`. Any other key is told the
381
+ approval was already used, and asks again. Only a PERSON's approval (state `approved`) is claimed: an
382
+ auto-approval is never shared, so it is NOT_CLAIMABLE, and so are a refusal, a waiting request and a
383
+ GitHub deploy approval (its deploy job uses it). A spent or expired approval is refused before anything
384
+ is written. A person's sign-in cannot claim (people approve, runs claim): it gets the 404. A bound agent
385
+ token claims only its own signed decisions; an unsigned decision is claimable by any agent credential
386
+ of the workspace (founder call 29). The SDKs make the claimKey at random for each run (two workers with
387
+ one key would be one run), retry a network failure or a 5xx with the SAME key, and run only on a 200.
388
+ parameters:
389
+ - name: decisionId
390
+ in: path
391
+ required: true
392
+ schema: { type: string }
393
+ requestBody:
394
+ required: true
395
+ content:
396
+ application/json:
397
+ schema:
398
+ type: object
399
+ required: [claimKey]
400
+ properties:
401
+ claimKey:
402
+ type: string
403
+ pattern: "^[A-Za-z0-9._:-]{16,128}$"
404
+ description: the run's own key; stored only as a keyed digest scoped to this decision
405
+ responses:
406
+ "200":
407
+ description: this run collected the approval - run the action
408
+ content:
409
+ application/json:
410
+ schema:
411
+ type: object
412
+ required: [ok, claimed, decisionId, again]
413
+ properties:
414
+ ok: { type: boolean, enum: [true] }
415
+ claimed: { type: boolean, enum: [true] }
416
+ decisionId: { type: string }
417
+ again: { type: boolean, description: true when the same key claimed again (the same run retrying) }
418
+ "400": { description: "a malformed decision id (INVALID_DECISION_ID) or claimKey (VALIDATION)" }
419
+ "401": { description: missing/invalid agent token }
420
+ "404": { description: unknown decision, one this credential may not claim, or a person's sign-in }
421
+ "409":
422
+ description: >
423
+ Do NOT run. `ALREADY_USED` - another run collected this approval, or it was already used: ask again
424
+ for a new one. `EXPIRED` - the approval lapsed: ask again. `NOT_CLAIMABLE` - not a person's approval
425
+ (still waiting, refused, an auto-approval, or a deploy approval).
426
+ content:
427
+ application/json:
428
+ schema:
429
+ type: object
430
+ properties:
431
+ code: { type: string, enum: [ALREADY_USED, EXPIRED, NOT_CLAIMABLE] }
432
+ decisionId: { type: string }
433
+ state: { type: string, enum: [pending, approved, auto_approved, rejected, blocked] }
434
+ message: { type: string }
435
+ /agent/withdraw/{decisionId}:
436
+ post:
437
+ summary: The requester stopped waiting - close its OPEN routed request as withdrawn (SDK 0.6.4)
438
+ description: >
439
+ The blocking SDK wait calls this when it hits its ceiling, so the queue stops carrying a request nobody
440
+ will act on and a later tap cannot seal "approved" for an action that will never run. A withdrawal only
441
+ ever reduces authority. Only the requester may withdraw: a bound agent token whose agent signed the
442
+ request; everything else is the 404. A withdrawn request reads `rejected` on GET /agent/status. A SHARED
443
+ request is never withdrawn: once another caller joined it (openTwin), the answer is 409 SHARED_REQUEST and
444
+ nothing changes. The SDKs skip the withdraw after an `idempotentReplay` or `openTwin` answer.
445
+ parameters:
446
+ - name: decisionId
447
+ in: path
448
+ required: true
449
+ schema: { type: string }
450
+ requestBody:
451
+ content:
452
+ application/json:
453
+ schema:
454
+ type: object
455
+ properties:
456
+ reason: { type: string, enum: [timeout, cancelled], description: "default timeout" }
457
+ responses:
458
+ "200":
459
+ description: withdrawn
460
+ content:
461
+ application/json:
462
+ schema:
463
+ type: object
464
+ properties:
465
+ ok: { type: boolean }
466
+ withdrawn: { type: boolean, enum: [true] }
467
+ state: { type: string, enum: [rejected] }
468
+ decisionId: { type: string }
469
+ reason: { type: string }
470
+ "400": { description: "a malformed decision id (INVALID_DECISION_ID) or an unknown reason (INVALID_REASON)" }
471
+ "401": { description: missing/invalid agent token }
472
+ "404": { description: unknown decision, or not this requester's own signed request }
473
+ "409":
474
+ description: >
475
+ `NOT_OPEN` - a decision already stands (or it was already withdrawn); `state` is the derived state, so
476
+ an approval that landed in the last moment is honoured. `SHARED_REQUEST` - another caller is waiting on
477
+ this same request; nothing changed (no `state`), and a person still decides it.
478
+ content:
479
+ application/json:
480
+ schema:
481
+ type: object
482
+ properties:
483
+ code: { type: string, enum: [NOT_OPEN, SHARED_REQUEST] }
484
+ state: { type: string, enum: [pending, approved, auto_approved, rejected, blocked] }
485
+ decisionId: { type: string }
486
+ message: { type: string }
331
487
  /agent/confirm:
332
488
  post:
333
489
  summary: >
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@decidio/sdk",
3
- "version": "0.11.0",
3
+ "version": "0.12.0",
4
4
  "description": "One-line approval gate for AI-agent actions. Wrap a risky tool call with guard.protect(); Decidio's policy engine decides proceed | route | block, a human approves routed actions, and every outcome is sealed as a verifiable Authority Receipt.",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",
@@ -84,7 +84,7 @@
84
84
  "prepublishOnly": "npm run typecheck && npm run build && node ../publish-boundary/check.mjs --only @decidio/sdk"
85
85
  },
86
86
  "dependencies": {
87
- "@decidio/verify": "0.11.0"
87
+ "@decidio/verify": "0.12.0"
88
88
  },
89
89
  "author": "OmniTwin Technologies Inc.",
90
90
  "bugs": {