@decidio/sdk 0.1.12 → 0.2.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/dist/signing.js CHANGED
@@ -111,6 +111,18 @@ export function assertJsonSafe(value, path = "args") {
111
111
  walk(value, path);
112
112
  }
113
113
  export function assertSafeTransport(url, hasCredential) {
114
+ if (!hasCredential) {
115
+ let probe = null;
116
+ try {
117
+ probe = new URL(url);
118
+ }
119
+ catch {
120
+ probe = null;
121
+ }
122
+ if (probe && (probe.username || probe.password)) {
123
+ throw new Error(`[decidio] the gate URL carries credentials in the URL itself (${probe.protocol}//user:***@${probe.host}). Decidio authenticates with a bearer token, so this is either a mistake or a secret you did not mean to put on the wire — and it is sent on every request regardless of whether an api token is configured. Remove the user:password from the URL. Nothing was sent.`);
124
+ }
125
+ }
114
126
  if (!hasCredential)
115
127
  return;
116
128
  let u;
@@ -124,11 +136,17 @@ export function assertSafeTransport(url, hasCredential) {
124
136
  }
125
137
  return;
126
138
  }
139
+ if (u.protocol !== "http:" && u.protocol !== "https:") {
140
+ throw new Error(`[decidio] the gate URL uses the ${u.protocol.replace(":", "")} scheme, which is not a Decidio endpoint — use https (or http://localhost for local dev). Nothing was sent.`);
141
+ }
142
+ if (u.username || u.password) {
143
+ throw new Error(`[decidio] the gate URL carries credentials in the URL itself (${u.protocol}//user:***@${u.host}). Decidio authenticates with a bearer token; remove the user:password from the URL. Nothing was sent.`);
144
+ }
127
145
  if (u.protocol !== "http:")
128
146
  return;
129
147
  const host = u.hostname.toLowerCase();
130
148
  const local = host === "localhost" || host === "127.0.0.1" || host === "[::1]" || host === "::1";
131
- if (local && !u.username && !u.password)
149
+ if (local)
132
150
  return;
133
151
  throw new Error(`[decidio] refusing to send credentials over plaintext http to ${u.origin} — use https, or localhost for local dev. Nothing was sent.`);
134
152
  }
package/openapi.yaml CHANGED
@@ -147,12 +147,76 @@ components:
147
147
  properties:
148
148
  protected: { type: string, description: "base64url JWS protected header (EdDSA, kid=did, typ=decidio-execution-report+jws)" }
149
149
  signature: { type: string, description: base64url detached signature over protected.payload }
150
+ ResumeWebhookResponse:
151
+ type: object
152
+ description: >
153
+ What the agent's resume endpoint answers. Through 0.1.x this was `{ok, outcome:{status,
154
+ decisionId}}` — the operator learned the SDK had finished, not whether the execution was
155
+ recorded, which is the only part Decidio can act on. Every field below was already
156
+ computed and discarded at the last line of the handler.
157
+ required: [ok, outcome]
158
+ properties:
159
+ ok: { type: boolean }
160
+ outcome:
161
+ type: object
162
+ required: [status, decisionId]
163
+ properties:
164
+ status:
165
+ type: string
166
+ enum: [executed, indeterminate, rejected, blocked, no_handler, unknown]
167
+ description: >
168
+ the LOCAL run. `indeterminate` means the handler was invoked and its outcome is
169
+ unknown — it will NOT be retried automatically and needs reconcile().
170
+ decisionId: { type: string }
171
+ reportRecorded: { type: boolean, description: "Decidio accepted the execution report." }
172
+ executionConfirmed: { type: boolean, description: "the recorded tier attests a COMMITTED execution — a different question from reportRecorded." }
173
+ evidenceTier:
174
+ type: string
175
+ enum: [pending, execution_unconfirmed, agent_asserted, sensor_observed, application_confirmed, independently_reconciled]
176
+ executionReportVerified: { type: boolean }
177
+ executionReportReason: { type: string, description: "why the attestation did not verify — a field, so a caller can branch instead of parsing prose." }
178
+ confirmationRetryable: { type: boolean, description: "whether re-sending this report could succeed." }
179
+ reportJournalled: { type: boolean, description: "whether the reported value is durable, which decides whether reconfirm() can help." }
180
+ evidenceError: { type: string, description: "the evidence mapper rejected the return value, so an empty captured response was reported." }
181
+ diagnostic: { type: string }
182
+ ConfirmResponse:
183
+ type: object
184
+ description: >
185
+ What the gate actually recorded. The SDK treats an unrecognised `confirmation` as NOT
186
+ evidence (closed enum, fail-closed), so adding a tier here is a breaking change for older
187
+ clients by design — they refuse to interpret what they do not understand rather than
188
+ guess.
189
+ required: [ok]
190
+ properties:
191
+ ok:
192
+ type: boolean
193
+ description: false means nothing was recorded; the SDK does not treat the report as delivered.
194
+ confirmation:
195
+ type: string
196
+ enum: [pending, execution_unconfirmed, agent_asserted, sensor_observed, application_confirmed, independently_reconciled]
197
+ description: the tier the report EARNED. Absent on a 2xx means no tier was assigned — also not evidence.
198
+ recordId:
199
+ type: string
200
+ description: the record the evidence accrued to.
201
+ executionReport:
202
+ type: object
203
+ description: >
204
+ The server's verdict on the signed report, when one was sent. `verified: false` with a
205
+ reason is surfaced to the caller verbatim — an execution whose proof did not check out
206
+ must not read as confirmed.
207
+ properties:
208
+ verified: { type: boolean }
209
+ reason:
210
+ type: string
211
+ description: why verification failed (unknown did, expired proof, burnt nonce, payload mismatch).
150
212
  ResumeWebhook:
151
213
  type: object
152
214
  description: >
153
215
  OUTBOUND — Decidio → your resumeUrl when a routed decision is decided. The raw body is
154
216
  HMAC-signed; verify `x-decidio-signature: sha256=<hex>` with your DECIDIO_WEBHOOK_SECRET
155
- BEFORE acting (the SDK resume controllers fail closed). Re-execution is single-use.
217
+ BEFORE acting (the SDK resume controllers fail closed). A duplicate delivery cannot cause a
218
+ second execution: the SDK writes a durable invoking marker before calling the handler,
219
+ so once invocation may have begun it is never invoked again automatically.
156
220
  OPERATOR DEPLOYMENTS ONLY: signing uses a shared secret configured on both the Decidio
157
221
  server and the agent, and self-hosted production allow-lists resume hosts (default-deny).
158
222
  Self-serve agents against the hosted sandbox use the SDK's poll worker (guard.worker())
@@ -201,16 +265,27 @@ paths:
201
265
  /agent/confirm:
202
266
  post:
203
267
  summary: >
204
- Report the captured execution outcome — recorded as the RECORD's evidence tier
205
- (application_confirmed). The credential sealed at decision time is immutable; evidence
206
- accrues on the record, never inside the sealed bytes.
268
+ Report the captured execution outcome — recorded as the RECORD's evidence tier. The tier
269
+ is EARNED, not requested: `application_confirmed` when the report carries a valid signed
270
+ executionReport from a registered agent, `agent_asserted` when it is an unsigned
271
+ self-report, `execution_unconfirmed` when the captured response carries no usable
272
+ evidence. The caller does not choose it and this endpoint does not always grant the top
273
+ one. The credential sealed at decision time is immutable; evidence accrues on the record,
274
+ never inside the sealed bytes.
207
275
  requestBody:
208
276
  required: true
209
277
  content:
210
278
  application/json:
211
279
  schema: { $ref: "#/components/schemas/ConfirmRequest" }
212
280
  responses:
213
- "200": { description: recorded }
281
+ "200":
282
+ description: >
283
+ The report was accepted. `ok` and the tier are what the SDK branches on — a 200 alone
284
+ does NOT mean the execution was confirmed, and a client that assumes it does will
285
+ record an assurance the server never granted.
286
+ content:
287
+ application/json:
288
+ schema: { $ref: "#/components/schemas/ConfirmResponse" }
214
289
  "401": { description: missing/invalid agent token }
215
290
  x-webhooks:
216
291
  resume:
@@ -221,5 +296,13 @@ paths:
221
296
  application/json:
222
297
  schema: { $ref: "#/components/schemas/ResumeWebhook" }
223
298
  responses:
224
- "200": { description: agent accepted and (single-use) re-executed }
299
+ "200":
300
+ description: >
301
+ Agent accepted; executed at most once — a duplicate delivery is refused by the
302
+ durable execution state, not re-run. The body reports what was RECORDED, not merely
303
+ that the SDK finished: a 200 whose `outcome.reportRecorded` is false means the
304
+ action ran and Decidio does NOT have its report.
305
+ content:
306
+ application/json:
307
+ schema: { $ref: "#/components/schemas/ResumeWebhookResponse" }
225
308
  "401": { description: signature missing/invalid — the controller fails closed }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@decidio/sdk",
3
- "version": "0.1.12",
3
+ "version": "0.2.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",
@@ -54,24 +54,29 @@
54
54
  "verifiable-credentials"
55
55
  ],
56
56
  "homepage": "https://decidioai.com/developers/",
57
+ "repository": {
58
+ "type": "git",
59
+ "url": "git+https://github.com/OmniTwin/Decidio.git",
60
+ "directory": "packages/decidio-guard"
61
+ },
57
62
  "publishConfig": {
58
63
  "access": "public"
59
64
  },
65
+ "peerDependencies": {
66
+ "@langchain/langgraph": ">=0.2 <2",
67
+ "@temporalio/workflow": ">=1.9 <2"
68
+ },
60
69
  "peerDependenciesMeta": {
61
- "inngest": {
62
- "optional": true
63
- },
64
70
  "@langchain/langgraph": {
65
71
  "optional": true
66
72
  },
67
73
  "@temporalio/workflow": {
68
74
  "optional": true
69
- },
70
- "@openai/agents": {
71
- "optional": true
72
75
  }
73
76
  },
74
77
  "devDependencies": {
78
+ "@langchain/langgraph": "^1.4.13",
79
+ "@temporalio/workflow": "^1.23.0",
75
80
  "@types/node": "^22.20.1",
76
81
  "typescript": "^5.9.3"
77
82
  },
@@ -81,7 +86,7 @@
81
86
  "prepublishOnly": "npm run typecheck && npm run build"
82
87
  },
83
88
  "dependencies": {
84
- "@decidio/verify": "0.1.10"
89
+ "@decidio/verify": "0.1.12"
85
90
  },
86
91
  "author": "OmniTwin Technologies Inc.",
87
92
  "bugs": {