@pushary/server 1.4.0 → 2.0.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.
@@ -1,6 +1,8 @@
1
1
  interface PusharyConfig {
2
2
  readonly apiKey: string;
3
3
  readonly baseUrl?: string;
4
+ /** HTTP request timeout, including response-body reading. Default 65 seconds. */
5
+ readonly requestTimeoutMs?: number;
4
6
  }
5
7
  interface ListParams {
6
8
  readonly limit?: number;
@@ -206,7 +208,128 @@ interface FlowListParams extends ListParams {
206
208
  }
207
209
  type DecisionType = 'confirm' | 'select' | 'input';
208
210
  type DecisionStatus = 'pending' | 'answered' | 'expired' | 'cancelled';
209
- interface CreateDecision {
211
+ /**
212
+ * Typed arguments of an action. Scalars only: a rule reads these, and a nested
213
+ * object has no comparable value to read.
214
+ */
215
+ type DecisionParameters = Readonly<Record<string, string | number | boolean>>;
216
+ /**
217
+ * How to render one parameter to the person deciding.
218
+ *
219
+ * Declared here rather than imported, for the same reason AuthorizationVerdict
220
+ * is: this package is MIT and must not depend on Pushary's private core, so the
221
+ * wire values are the contract between them.
222
+ *
223
+ * A currency parameter holds MINOR units — 4800 is EUR 48.00. A float cannot
224
+ * hold money exactly and a pre-formatted string cannot be compared by a rule.
225
+ */
226
+ type PresentedFormat = {
227
+ readonly kind: 'text';
228
+ } | {
229
+ readonly kind: 'currency';
230
+ readonly currency: string;
231
+ } | {
232
+ readonly kind: 'quantity';
233
+ readonly unit?: string;
234
+ } | {
235
+ readonly kind: 'timestamp';
236
+ } | {
237
+ readonly kind: 'boolean';
238
+ };
239
+ interface PresentedChange {
240
+ /**
241
+ * A key of `parameters`. The value is read from there and never carried here,
242
+ * so what the person sees and what a rule reads cannot be different numbers.
243
+ */
244
+ readonly parameter: string;
245
+ /** What to call it to a person: "Refund amount", not "amount_minor". */
246
+ readonly label: string;
247
+ readonly format: PresentedFormat;
248
+ }
249
+ /**
250
+ * The action in business language, for the surfaces a person actually reads.
251
+ *
252
+ * Optional. A caller that sends none keeps the free-text `question` it has
253
+ * always sent; only a caller that claims a presentation is held to it, and an
254
+ * invalid one is refused rather than half-rendered.
255
+ */
256
+ interface ActionPresentation {
257
+ /** The action as a person would name it. "Refund order #4471". */
258
+ readonly label: string;
259
+ /** What actually happens. "Returns EUR 48.00 to the customer's card." */
260
+ readonly effect: string;
261
+ readonly changes?: readonly PresentedChange[];
262
+ /** Optional. Why this one is worth stopping for. */
263
+ readonly risk?: string;
264
+ }
265
+ interface DecisionSubject {
266
+ readonly toolName?: string;
267
+ readonly toolTarget?: string;
268
+ /**
269
+ * Who the action runs on behalf of, e.g. `user:u_44` or `team:billing`. This is
270
+ * the principal, which is not necessarily the person who approves: `externalId`
271
+ * names the recipient of the question, `actor` names whose authority the action
272
+ * claims. Max 120 characters.
273
+ */
274
+ readonly actor?: string;
275
+ /** Which deployment the action runs against, e.g. `production`. Max 40 characters. */
276
+ readonly environment?: string;
277
+ /**
278
+ * Typed arguments of the action, e.g. `{ amount: 4800, currency: 'EUR' }`.
279
+ * Scalars only, at most 32 keys. Recorded on the audit row so an approval can be
280
+ * read back against what it approved.
281
+ *
282
+ * Rejected as a whole (400) if any value is not a string, a finite number or a
283
+ * boolean, rather than being partly dropped: a rule that reads a missing
284
+ * parameter can only ever decide more permissively than one that reads it.
285
+ */
286
+ readonly parameters?: DecisionParameters;
287
+ /**
288
+ * What the person deciding sees, in business language. Its `changes` name
289
+ * parameters rather than carrying values, so display and policy read the same
290
+ * numbers. Refused (400) if it names a parameter you did not send, names one
291
+ * whose name marks it secret, or gives a value a format cannot render.
292
+ */
293
+ readonly presentation?: ActionPresentation;
294
+ /**
295
+ * Return a short-lived `approvalUrl` you can deliver yourself, over the email,
296
+ * SMS or in-app inbox you already have for this end-user.
297
+ *
298
+ * Off by default: a capability URL returned on every create is a capability URL
299
+ * in every log, for the majority of decisions that never needed one. The link
300
+ * is bound to this decision, this site and this recipient, and expires.
301
+ */
302
+ readonly approvalUrl?: boolean;
303
+ }
304
+ /** Whether a decision can reach this person, and by whose hands. */
305
+ type DecisionDeliverability = 'push' | 'fallback' | 'unreachable';
306
+ interface DecisionReach {
307
+ readonly externalId: string;
308
+ /**
309
+ * `push` — Pushary delivers it. `fallback` — nobody is enrolled, but you know
310
+ * this end-user: open the decision with `approvalUrl: true` and deliver the
311
+ * link yourself. `unreachable` — no end-user was named, so there is nobody to
312
+ * reach and no link to mint.
313
+ */
314
+ readonly deliverability: DecisionDeliverability;
315
+ readonly reachable: boolean;
316
+ readonly reachableChannels: number;
317
+ readonly deviceCount: number;
318
+ readonly webEndpoints: number;
319
+ readonly hint: string;
320
+ }
321
+ /**
322
+ * What the sandbox's simulated approver does.
323
+ *
324
+ * `approve` is the default. `no_answer` leaves the decision open, which is how
325
+ * you exercise your own fail-closed path without waiting on a real phone.
326
+ *
327
+ * Read only by a key issued on your workspace's sandbox site, where nothing is
328
+ * delivered and nobody is asked. Ignored in production, where the approver is a
329
+ * person.
330
+ */
331
+ type SandboxOutcome = 'approve' | 'deny' | 'no_answer';
332
+ interface CreateDecision extends DecisionSubject {
210
333
  readonly question: string;
211
334
  readonly type?: DecisionType;
212
335
  readonly options?: readonly string[];
@@ -222,6 +345,108 @@ interface CreateDecision {
222
345
  readonly idempotencyKey?: string;
223
346
  readonly poweredBy?: boolean;
224
347
  readonly requireReachable?: boolean;
348
+ readonly sandboxOutcome?: SandboxOutcome;
349
+ }
350
+ type AuthorizationVerdict = 'allow' | 'deny' | 'requires_human';
351
+ interface AuthorizationQuery extends DecisionSubject {
352
+ /** The action being authorized. Required: a rule has to name it. */
353
+ readonly toolName: string;
354
+ /** Your own id for the end-user this action belongs to. */
355
+ readonly externalId?: string;
356
+ /**
357
+ * Shown to the person, if policy defers to one. Defaults to the action and its
358
+ * target, so a caller that only names the action still asks a readable question.
359
+ */
360
+ readonly question?: string;
361
+ readonly agentName?: string;
362
+ }
363
+ interface AuthorizeRequest extends AuthorizationQuery {
364
+ /** Forwarded to the ask, and used only when policy defers to a person. */
365
+ readonly timeoutMs?: number;
366
+ readonly expiresInSeconds?: number;
367
+ readonly idempotencyKey?: string;
368
+ readonly requireReachable?: boolean;
369
+ }
370
+ interface AuthorizeResult {
371
+ /** Fail-closed. False for a denial, and false when nobody answered. */
372
+ readonly approved: boolean;
373
+ /** What settled it: a rule, or a person. */
374
+ readonly resolvedBy: 'policy' | 'human';
375
+ /** The rule that decided, or null when no rule named the action. */
376
+ readonly policy: string | null;
377
+ readonly reason: string;
378
+ /** The decision opened when policy deferred to a person, else null. */
379
+ readonly decisionId: string | null;
380
+ /** The audit row a policy-settled authorization produced, else null. */
381
+ readonly authorizationId: string | null;
382
+ }
383
+ interface AuthorizationEvaluation {
384
+ readonly verdict: AuthorizationVerdict;
385
+ readonly policy: string | null;
386
+ readonly reason: string;
387
+ readonly authorizationId: string | null;
388
+ }
389
+ /**
390
+ * The exact subject an authorization was recorded against.
391
+ *
392
+ * Carried from the gate to the consume rather than rebuilt, because the two have
393
+ * to name the same action and rebuilding it is a second chance for them not to.
394
+ * The server digests this and what it recorded, and refuses unless they match.
395
+ */
396
+ interface AuthorizationBinding {
397
+ /** The settled authorization: a policy verdict's id, or an approved decision's id. */
398
+ readonly authorizationId: string;
399
+ readonly toolName: string;
400
+ readonly toolTarget?: string;
401
+ readonly actor?: string;
402
+ readonly environment?: string;
403
+ readonly externalId?: string;
404
+ readonly parameters?: DecisionParameters;
405
+ }
406
+ /**
407
+ * Who allowed it, frozen when it was allowed.
408
+ *
409
+ * A rule can be edited and a member can lose their seat. An authorization that
410
+ * can only say "something allowed this" is not an audit trail, so the deciding
411
+ * authority is copied onto the permit and never updated.
412
+ */
413
+ type AuthorizationAuthority = {
414
+ readonly kind: 'policy';
415
+ readonly rulePattern?: string;
416
+ readonly authorizationRuleId?: string;
417
+ readonly policyId?: string;
418
+ } | {
419
+ readonly kind: 'human';
420
+ readonly answeredAt: string;
421
+ readonly answerSource?: string;
422
+ readonly approverUserId?: string;
423
+ readonly approverExternalId?: string;
424
+ };
425
+ /** Why a consume was refused. Every branch is a refusal; none of them means allowed. */
426
+ type PermitRefusal = 'not_authorized' | 'action_mismatch' | 'expired' | 'already_consumed';
427
+ type PermitExecutionState = 'running' | 'succeeded' | 'failed';
428
+ interface ConsumedAuthorization {
429
+ readonly permitId: string;
430
+ readonly authority: AuthorizationAuthority;
431
+ /** ISO-8601. The permit is spent already; this is when it would have stopped being spendable. */
432
+ readonly expiresAt: string;
433
+ }
434
+ type ConsumeAuthorizationResult = {
435
+ readonly consumed: true;
436
+ readonly permit: ConsumedAuthorization;
437
+ } | {
438
+ readonly consumed: false;
439
+ readonly refusal: PermitRefusal;
440
+ readonly reason: string;
441
+ /** On `already_consumed`, what the winning execution did. */
442
+ readonly executionState?: PermitExecutionState;
443
+ };
444
+ type ExecutionOutcome = 'succeeded' | 'failed';
445
+ interface ExecutionReceipt {
446
+ readonly permitId: string;
447
+ readonly outcome: ExecutionOutcome;
448
+ /** Bounded and redacted server-side. What the action did, or why it did not. */
449
+ readonly summary?: string;
225
450
  }
226
451
  interface DecisionReachability {
227
452
  readonly reachable?: boolean;
@@ -283,7 +508,9 @@ interface EnrollResult {
283
508
  readonly universalLink: string;
284
509
  readonly expiresInSeconds: number;
285
510
  }
286
- interface AskDecision {
511
+ interface AskDecision extends DecisionSubject {
512
+ /** Cancel the local wait; the remote decision remains recoverable. */
513
+ readonly signal?: AbortSignal;
287
514
  readonly question: string;
288
515
  readonly type?: DecisionType;
289
516
  readonly options?: readonly string[];
@@ -297,6 +524,9 @@ interface AskDecision {
297
524
  readonly poweredBy?: boolean;
298
525
  readonly callbackUrl?: string;
299
526
  readonly requireReachable?: boolean;
527
+ readonly sandboxOutcome?: SandboxOutcome;
528
+ readonly presentation?: ActionPresentation;
529
+ readonly approvalUrl?: boolean;
300
530
  readonly timeoutMs?: number;
301
531
  }
302
532
  interface AskResult extends DecisionReachability {
@@ -401,7 +631,14 @@ interface PusharyServer {
401
631
  readonly flows: FlowsResource;
402
632
  readonly decisions: DecisionsResource;
403
633
  readonly keys: KeysResource;
634
+ readonly authorize: (input: AuthorizeRequest) => Promise<AuthorizeResult>;
635
+ readonly evaluateAuthorization: (input: AuthorizationQuery) => Promise<AuthorizationEvaluation>;
636
+ readonly consumeAuthorization: (binding: AuthorizationBinding) => Promise<ConsumeAuthorizationResult>;
637
+ readonly recordExecution: (receipt: ExecutionReceipt) => Promise<{
638
+ readonly executionState: PermitExecutionState;
639
+ }>;
404
640
  readonly enroll: (externalId: string) => Promise<EnrollResult>;
641
+ readonly reachability: (externalId: string) => Promise<DecisionReach>;
405
642
  }
406
643
  declare const createPusharyServer: (config: PusharyConfig) => PusharyServer;
407
644
 
@@ -416,4 +653,4 @@ declare const isApproved: (decision: {
416
653
  readonly value: string | null;
417
654
  }) => boolean;
418
655
 
419
- export { parseDecisionCallback as $, type DecisionAnswerResult as A, type CancelDecisionResult as B, type Campaign as C, type DecisionType as D, type DecisionCallback as E, type Flow as F, type EnrollResult as G, type AskDecision as H, type AskResult as I, type IssueBoundKey as J, type BoundKeyResult as K, type ListParams as L, type BoundKeySummary as M, type SubscribersResource as N, type CampaignsResource as O, type PusharyServer as P, type TemplatesResource as Q, type RevokeKeyResult as R, type Subscriber as S, type Template as T, type UpdateSubscriber as U, type NotificationsResource as V, type WebhookSecret as W, type FlowsResource as X, type DecisionsResource as Y, type KeysResource as Z, verifyWebhookSignature as _, type PusharyConfig as a, SIGNATURE_HEADER as a0, deterministicKey as a1, isApproved as a2, type SubscriberListParams as b, createPusharyServer as c, type SubscriberCount as d, type CreateCampaign as e, type UpdateCampaign as f, type CampaignStats as g, type CreateTemplate as h, type UpdateTemplate as i, type SendNotification as j, type SendResult as k, type CreateFlow as l, type UpdateFlow as m, type FlowListParams as n, type FlowTriggerType as o, type FlowStepType as p, type FlowStatus as q, type FlowStep as r, type FlowStepConfig as s, type FlowStepInput as t, type PaginatedResponse as u, type DecisionStatus as v, type CreateDecision as w, type DecisionReachability as x, type DecisionResult as y, type Decision as z };
656
+ export { type DecisionResult as $, type ActionPresentation as A, type PresentedChange as B, type Campaign as C, type DecisionType as D, type PresentedFormat as E, type Flow as F, type SandboxOutcome as G, type AuthorizationVerdict as H, type AuthorizationQuery as I, type AuthorizeRequest as J, type AuthorizeResult as K, type ListParams as L, type AuthorizationEvaluation as M, type AuthorizationBinding as N, type AuthorizationAuthority as O, type PusharyServer as P, type ConsumedAuthorization as Q, type ConsumeAuthorizationResult as R, type Subscriber as S, type Template as T, type UpdateSubscriber as U, type ExecutionOutcome as V, type ExecutionReceipt as W, type PermitRefusal as X, type PermitExecutionState as Y, type CreateDecision as Z, type DecisionReachability as _, type PusharyConfig as a, type Decision as a0, type DecisionAnswerResult as a1, type CancelDecisionResult as a2, type DecisionCallback as a3, type WebhookSecret as a4, type EnrollResult as a5, type AskDecision as a6, type AskResult as a7, type IssueBoundKey as a8, type BoundKeyResult as a9, type BoundKeySummary as aa, type RevokeKeyResult as ab, type SubscribersResource as ac, type CampaignsResource as ad, type TemplatesResource as ae, type NotificationsResource as af, type FlowsResource as ag, type DecisionsResource as ah, type KeysResource as ai, verifyWebhookSignature as aj, parseDecisionCallback as ak, SIGNATURE_HEADER as al, deterministicKey as am, isApproved as an, type SubscriberListParams as b, createPusharyServer as c, type SubscriberCount as d, type CreateCampaign as e, type UpdateCampaign as f, type CampaignStats as g, type CreateTemplate as h, type UpdateTemplate as i, type SendNotification as j, type SendResult as k, type CreateFlow as l, type UpdateFlow as m, type FlowListParams as n, type FlowTriggerType as o, type FlowStepType as p, type FlowStatus as q, type FlowStep as r, type FlowStepConfig as s, type FlowStepInput as t, type PaginatedResponse as u, type DecisionStatus as v, type DecisionSubject as w, type DecisionDeliverability as x, type DecisionReach as y, type DecisionParameters as z };
@@ -1,6 +1,8 @@
1
1
  interface PusharyConfig {
2
2
  readonly apiKey: string;
3
3
  readonly baseUrl?: string;
4
+ /** HTTP request timeout, including response-body reading. Default 65 seconds. */
5
+ readonly requestTimeoutMs?: number;
4
6
  }
5
7
  interface ListParams {
6
8
  readonly limit?: number;
@@ -206,7 +208,128 @@ interface FlowListParams extends ListParams {
206
208
  }
207
209
  type DecisionType = 'confirm' | 'select' | 'input';
208
210
  type DecisionStatus = 'pending' | 'answered' | 'expired' | 'cancelled';
209
- interface CreateDecision {
211
+ /**
212
+ * Typed arguments of an action. Scalars only: a rule reads these, and a nested
213
+ * object has no comparable value to read.
214
+ */
215
+ type DecisionParameters = Readonly<Record<string, string | number | boolean>>;
216
+ /**
217
+ * How to render one parameter to the person deciding.
218
+ *
219
+ * Declared here rather than imported, for the same reason AuthorizationVerdict
220
+ * is: this package is MIT and must not depend on Pushary's private core, so the
221
+ * wire values are the contract between them.
222
+ *
223
+ * A currency parameter holds MINOR units — 4800 is EUR 48.00. A float cannot
224
+ * hold money exactly and a pre-formatted string cannot be compared by a rule.
225
+ */
226
+ type PresentedFormat = {
227
+ readonly kind: 'text';
228
+ } | {
229
+ readonly kind: 'currency';
230
+ readonly currency: string;
231
+ } | {
232
+ readonly kind: 'quantity';
233
+ readonly unit?: string;
234
+ } | {
235
+ readonly kind: 'timestamp';
236
+ } | {
237
+ readonly kind: 'boolean';
238
+ };
239
+ interface PresentedChange {
240
+ /**
241
+ * A key of `parameters`. The value is read from there and never carried here,
242
+ * so what the person sees and what a rule reads cannot be different numbers.
243
+ */
244
+ readonly parameter: string;
245
+ /** What to call it to a person: "Refund amount", not "amount_minor". */
246
+ readonly label: string;
247
+ readonly format: PresentedFormat;
248
+ }
249
+ /**
250
+ * The action in business language, for the surfaces a person actually reads.
251
+ *
252
+ * Optional. A caller that sends none keeps the free-text `question` it has
253
+ * always sent; only a caller that claims a presentation is held to it, and an
254
+ * invalid one is refused rather than half-rendered.
255
+ */
256
+ interface ActionPresentation {
257
+ /** The action as a person would name it. "Refund order #4471". */
258
+ readonly label: string;
259
+ /** What actually happens. "Returns EUR 48.00 to the customer's card." */
260
+ readonly effect: string;
261
+ readonly changes?: readonly PresentedChange[];
262
+ /** Optional. Why this one is worth stopping for. */
263
+ readonly risk?: string;
264
+ }
265
+ interface DecisionSubject {
266
+ readonly toolName?: string;
267
+ readonly toolTarget?: string;
268
+ /**
269
+ * Who the action runs on behalf of, e.g. `user:u_44` or `team:billing`. This is
270
+ * the principal, which is not necessarily the person who approves: `externalId`
271
+ * names the recipient of the question, `actor` names whose authority the action
272
+ * claims. Max 120 characters.
273
+ */
274
+ readonly actor?: string;
275
+ /** Which deployment the action runs against, e.g. `production`. Max 40 characters. */
276
+ readonly environment?: string;
277
+ /**
278
+ * Typed arguments of the action, e.g. `{ amount: 4800, currency: 'EUR' }`.
279
+ * Scalars only, at most 32 keys. Recorded on the audit row so an approval can be
280
+ * read back against what it approved.
281
+ *
282
+ * Rejected as a whole (400) if any value is not a string, a finite number or a
283
+ * boolean, rather than being partly dropped: a rule that reads a missing
284
+ * parameter can only ever decide more permissively than one that reads it.
285
+ */
286
+ readonly parameters?: DecisionParameters;
287
+ /**
288
+ * What the person deciding sees, in business language. Its `changes` name
289
+ * parameters rather than carrying values, so display and policy read the same
290
+ * numbers. Refused (400) if it names a parameter you did not send, names one
291
+ * whose name marks it secret, or gives a value a format cannot render.
292
+ */
293
+ readonly presentation?: ActionPresentation;
294
+ /**
295
+ * Return a short-lived `approvalUrl` you can deliver yourself, over the email,
296
+ * SMS or in-app inbox you already have for this end-user.
297
+ *
298
+ * Off by default: a capability URL returned on every create is a capability URL
299
+ * in every log, for the majority of decisions that never needed one. The link
300
+ * is bound to this decision, this site and this recipient, and expires.
301
+ */
302
+ readonly approvalUrl?: boolean;
303
+ }
304
+ /** Whether a decision can reach this person, and by whose hands. */
305
+ type DecisionDeliverability = 'push' | 'fallback' | 'unreachable';
306
+ interface DecisionReach {
307
+ readonly externalId: string;
308
+ /**
309
+ * `push` — Pushary delivers it. `fallback` — nobody is enrolled, but you know
310
+ * this end-user: open the decision with `approvalUrl: true` and deliver the
311
+ * link yourself. `unreachable` — no end-user was named, so there is nobody to
312
+ * reach and no link to mint.
313
+ */
314
+ readonly deliverability: DecisionDeliverability;
315
+ readonly reachable: boolean;
316
+ readonly reachableChannels: number;
317
+ readonly deviceCount: number;
318
+ readonly webEndpoints: number;
319
+ readonly hint: string;
320
+ }
321
+ /**
322
+ * What the sandbox's simulated approver does.
323
+ *
324
+ * `approve` is the default. `no_answer` leaves the decision open, which is how
325
+ * you exercise your own fail-closed path without waiting on a real phone.
326
+ *
327
+ * Read only by a key issued on your workspace's sandbox site, where nothing is
328
+ * delivered and nobody is asked. Ignored in production, where the approver is a
329
+ * person.
330
+ */
331
+ type SandboxOutcome = 'approve' | 'deny' | 'no_answer';
332
+ interface CreateDecision extends DecisionSubject {
210
333
  readonly question: string;
211
334
  readonly type?: DecisionType;
212
335
  readonly options?: readonly string[];
@@ -222,6 +345,108 @@ interface CreateDecision {
222
345
  readonly idempotencyKey?: string;
223
346
  readonly poweredBy?: boolean;
224
347
  readonly requireReachable?: boolean;
348
+ readonly sandboxOutcome?: SandboxOutcome;
349
+ }
350
+ type AuthorizationVerdict = 'allow' | 'deny' | 'requires_human';
351
+ interface AuthorizationQuery extends DecisionSubject {
352
+ /** The action being authorized. Required: a rule has to name it. */
353
+ readonly toolName: string;
354
+ /** Your own id for the end-user this action belongs to. */
355
+ readonly externalId?: string;
356
+ /**
357
+ * Shown to the person, if policy defers to one. Defaults to the action and its
358
+ * target, so a caller that only names the action still asks a readable question.
359
+ */
360
+ readonly question?: string;
361
+ readonly agentName?: string;
362
+ }
363
+ interface AuthorizeRequest extends AuthorizationQuery {
364
+ /** Forwarded to the ask, and used only when policy defers to a person. */
365
+ readonly timeoutMs?: number;
366
+ readonly expiresInSeconds?: number;
367
+ readonly idempotencyKey?: string;
368
+ readonly requireReachable?: boolean;
369
+ }
370
+ interface AuthorizeResult {
371
+ /** Fail-closed. False for a denial, and false when nobody answered. */
372
+ readonly approved: boolean;
373
+ /** What settled it: a rule, or a person. */
374
+ readonly resolvedBy: 'policy' | 'human';
375
+ /** The rule that decided, or null when no rule named the action. */
376
+ readonly policy: string | null;
377
+ readonly reason: string;
378
+ /** The decision opened when policy deferred to a person, else null. */
379
+ readonly decisionId: string | null;
380
+ /** The audit row a policy-settled authorization produced, else null. */
381
+ readonly authorizationId: string | null;
382
+ }
383
+ interface AuthorizationEvaluation {
384
+ readonly verdict: AuthorizationVerdict;
385
+ readonly policy: string | null;
386
+ readonly reason: string;
387
+ readonly authorizationId: string | null;
388
+ }
389
+ /**
390
+ * The exact subject an authorization was recorded against.
391
+ *
392
+ * Carried from the gate to the consume rather than rebuilt, because the two have
393
+ * to name the same action and rebuilding it is a second chance for them not to.
394
+ * The server digests this and what it recorded, and refuses unless they match.
395
+ */
396
+ interface AuthorizationBinding {
397
+ /** The settled authorization: a policy verdict's id, or an approved decision's id. */
398
+ readonly authorizationId: string;
399
+ readonly toolName: string;
400
+ readonly toolTarget?: string;
401
+ readonly actor?: string;
402
+ readonly environment?: string;
403
+ readonly externalId?: string;
404
+ readonly parameters?: DecisionParameters;
405
+ }
406
+ /**
407
+ * Who allowed it, frozen when it was allowed.
408
+ *
409
+ * A rule can be edited and a member can lose their seat. An authorization that
410
+ * can only say "something allowed this" is not an audit trail, so the deciding
411
+ * authority is copied onto the permit and never updated.
412
+ */
413
+ type AuthorizationAuthority = {
414
+ readonly kind: 'policy';
415
+ readonly rulePattern?: string;
416
+ readonly authorizationRuleId?: string;
417
+ readonly policyId?: string;
418
+ } | {
419
+ readonly kind: 'human';
420
+ readonly answeredAt: string;
421
+ readonly answerSource?: string;
422
+ readonly approverUserId?: string;
423
+ readonly approverExternalId?: string;
424
+ };
425
+ /** Why a consume was refused. Every branch is a refusal; none of them means allowed. */
426
+ type PermitRefusal = 'not_authorized' | 'action_mismatch' | 'expired' | 'already_consumed';
427
+ type PermitExecutionState = 'running' | 'succeeded' | 'failed';
428
+ interface ConsumedAuthorization {
429
+ readonly permitId: string;
430
+ readonly authority: AuthorizationAuthority;
431
+ /** ISO-8601. The permit is spent already; this is when it would have stopped being spendable. */
432
+ readonly expiresAt: string;
433
+ }
434
+ type ConsumeAuthorizationResult = {
435
+ readonly consumed: true;
436
+ readonly permit: ConsumedAuthorization;
437
+ } | {
438
+ readonly consumed: false;
439
+ readonly refusal: PermitRefusal;
440
+ readonly reason: string;
441
+ /** On `already_consumed`, what the winning execution did. */
442
+ readonly executionState?: PermitExecutionState;
443
+ };
444
+ type ExecutionOutcome = 'succeeded' | 'failed';
445
+ interface ExecutionReceipt {
446
+ readonly permitId: string;
447
+ readonly outcome: ExecutionOutcome;
448
+ /** Bounded and redacted server-side. What the action did, or why it did not. */
449
+ readonly summary?: string;
225
450
  }
226
451
  interface DecisionReachability {
227
452
  readonly reachable?: boolean;
@@ -283,7 +508,9 @@ interface EnrollResult {
283
508
  readonly universalLink: string;
284
509
  readonly expiresInSeconds: number;
285
510
  }
286
- interface AskDecision {
511
+ interface AskDecision extends DecisionSubject {
512
+ /** Cancel the local wait; the remote decision remains recoverable. */
513
+ readonly signal?: AbortSignal;
287
514
  readonly question: string;
288
515
  readonly type?: DecisionType;
289
516
  readonly options?: readonly string[];
@@ -297,6 +524,9 @@ interface AskDecision {
297
524
  readonly poweredBy?: boolean;
298
525
  readonly callbackUrl?: string;
299
526
  readonly requireReachable?: boolean;
527
+ readonly sandboxOutcome?: SandboxOutcome;
528
+ readonly presentation?: ActionPresentation;
529
+ readonly approvalUrl?: boolean;
300
530
  readonly timeoutMs?: number;
301
531
  }
302
532
  interface AskResult extends DecisionReachability {
@@ -401,7 +631,14 @@ interface PusharyServer {
401
631
  readonly flows: FlowsResource;
402
632
  readonly decisions: DecisionsResource;
403
633
  readonly keys: KeysResource;
634
+ readonly authorize: (input: AuthorizeRequest) => Promise<AuthorizeResult>;
635
+ readonly evaluateAuthorization: (input: AuthorizationQuery) => Promise<AuthorizationEvaluation>;
636
+ readonly consumeAuthorization: (binding: AuthorizationBinding) => Promise<ConsumeAuthorizationResult>;
637
+ readonly recordExecution: (receipt: ExecutionReceipt) => Promise<{
638
+ readonly executionState: PermitExecutionState;
639
+ }>;
404
640
  readonly enroll: (externalId: string) => Promise<EnrollResult>;
641
+ readonly reachability: (externalId: string) => Promise<DecisionReach>;
405
642
  }
406
643
  declare const createPusharyServer: (config: PusharyConfig) => PusharyServer;
407
644
 
@@ -416,4 +653,4 @@ declare const isApproved: (decision: {
416
653
  readonly value: string | null;
417
654
  }) => boolean;
418
655
 
419
- export { parseDecisionCallback as $, type DecisionAnswerResult as A, type CancelDecisionResult as B, type Campaign as C, type DecisionType as D, type DecisionCallback as E, type Flow as F, type EnrollResult as G, type AskDecision as H, type AskResult as I, type IssueBoundKey as J, type BoundKeyResult as K, type ListParams as L, type BoundKeySummary as M, type SubscribersResource as N, type CampaignsResource as O, type PusharyServer as P, type TemplatesResource as Q, type RevokeKeyResult as R, type Subscriber as S, type Template as T, type UpdateSubscriber as U, type NotificationsResource as V, type WebhookSecret as W, type FlowsResource as X, type DecisionsResource as Y, type KeysResource as Z, verifyWebhookSignature as _, type PusharyConfig as a, SIGNATURE_HEADER as a0, deterministicKey as a1, isApproved as a2, type SubscriberListParams as b, createPusharyServer as c, type SubscriberCount as d, type CreateCampaign as e, type UpdateCampaign as f, type CampaignStats as g, type CreateTemplate as h, type UpdateTemplate as i, type SendNotification as j, type SendResult as k, type CreateFlow as l, type UpdateFlow as m, type FlowListParams as n, type FlowTriggerType as o, type FlowStepType as p, type FlowStatus as q, type FlowStep as r, type FlowStepConfig as s, type FlowStepInput as t, type PaginatedResponse as u, type DecisionStatus as v, type CreateDecision as w, type DecisionReachability as x, type DecisionResult as y, type Decision as z };
656
+ export { type DecisionResult as $, type ActionPresentation as A, type PresentedChange as B, type Campaign as C, type DecisionType as D, type PresentedFormat as E, type Flow as F, type SandboxOutcome as G, type AuthorizationVerdict as H, type AuthorizationQuery as I, type AuthorizeRequest as J, type AuthorizeResult as K, type ListParams as L, type AuthorizationEvaluation as M, type AuthorizationBinding as N, type AuthorizationAuthority as O, type PusharyServer as P, type ConsumedAuthorization as Q, type ConsumeAuthorizationResult as R, type Subscriber as S, type Template as T, type UpdateSubscriber as U, type ExecutionOutcome as V, type ExecutionReceipt as W, type PermitRefusal as X, type PermitExecutionState as Y, type CreateDecision as Z, type DecisionReachability as _, type PusharyConfig as a, type Decision as a0, type DecisionAnswerResult as a1, type CancelDecisionResult as a2, type DecisionCallback as a3, type WebhookSecret as a4, type EnrollResult as a5, type AskDecision as a6, type AskResult as a7, type IssueBoundKey as a8, type BoundKeyResult as a9, type BoundKeySummary as aa, type RevokeKeyResult as ab, type SubscribersResource as ac, type CampaignsResource as ad, type TemplatesResource as ae, type NotificationsResource as af, type FlowsResource as ag, type DecisionsResource as ah, type KeysResource as ai, verifyWebhookSignature as aj, parseDecisionCallback as ak, SIGNATURE_HEADER as al, deterministicKey as am, isApproved as an, type SubscriberListParams as b, createPusharyServer as c, type SubscriberCount as d, type CreateCampaign as e, type UpdateCampaign as f, type CampaignStats as g, type CreateTemplate as h, type UpdateTemplate as i, type SendNotification as j, type SendResult as k, type CreateFlow as l, type UpdateFlow as m, type FlowListParams as n, type FlowTriggerType as o, type FlowStepType as p, type FlowStatus as q, type FlowStep as r, type FlowStepConfig as s, type FlowStepInput as t, type PaginatedResponse as u, type DecisionStatus as v, type DecisionSubject as w, type DecisionDeliverability as x, type DecisionReach as y, type DecisionParameters as z };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pushary/server",
3
- "version": "1.4.0",
3
+ "version": "2.0.0",
4
4
  "description": "Pushary Server SDK: send push notifications and add human-in-the-loop approvals to your AI agent (pause on a decision until a specific human answers).",
5
5
  "author": "Pushary <business@pushary.com>",
6
6
  "homepage": "https://pushary.com",