@company-semantics/contracts 1.15.0 → 1.17.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/package.json +1 -1
- package/src/admin/authz-simulate.ts +40 -0
- package/src/admin/direct-grants.ts +31 -0
- package/src/api/generated-spec-hash.ts +2 -2
- package/src/api/generated.ts +166 -66
- package/src/dispatch/index.ts +99 -0
- package/src/generated/openapi-routes.ts +5 -3
- package/src/index.ts +16 -0
package/package.json
CHANGED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Admin What-If Simulator — request/response schemas.
|
|
3
|
+
*
|
|
4
|
+
* Mirrors the Fastify schema for `POST /api/admin/authz/simulate`. The endpoint
|
|
5
|
+
* is read-only and returns the full structured AuthDecision verbatim, so that
|
|
6
|
+
* admins can debug denials by reading evaluator name + reason rather than
|
|
7
|
+
* guessing. See PRD-00544 and feedback_decision_logic_must_be_visible.md.
|
|
8
|
+
*/
|
|
9
|
+
import { z } from 'zod';
|
|
10
|
+
|
|
11
|
+
export const SimulateRequest = z.object({
|
|
12
|
+
actor: z.object({
|
|
13
|
+
type: z.enum(['user', 'agent']),
|
|
14
|
+
id: z.string().uuid(),
|
|
15
|
+
}),
|
|
16
|
+
scope: z.string(),
|
|
17
|
+
resource_kind: z.enum(['system', 'org', 'team', 'member', 'doc']),
|
|
18
|
+
resource_id: z.string().uuid().optional(),
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
export type SimulateRequest = z.infer<typeof SimulateRequest>;
|
|
22
|
+
|
|
23
|
+
export const SimulateResponse = z.discriminatedUnion('allow', [
|
|
24
|
+
z.object({
|
|
25
|
+
allow: z.literal(true),
|
|
26
|
+
matched_grant: z.unknown(),
|
|
27
|
+
evaluator_name: z.string().optional(),
|
|
28
|
+
evaluator_reason: z.string().optional(),
|
|
29
|
+
}),
|
|
30
|
+
z.object({
|
|
31
|
+
allow: z.literal(false),
|
|
32
|
+
deny_reasons: z.array(z.unknown()),
|
|
33
|
+
missing: z.object({
|
|
34
|
+
scope: z.string().optional(),
|
|
35
|
+
suggested_role: z.string().optional(),
|
|
36
|
+
}),
|
|
37
|
+
}),
|
|
38
|
+
]);
|
|
39
|
+
|
|
40
|
+
export type SimulateResponse = z.infer<typeof SimulateResponse>;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Admin Direct Grants — request/response schemas.
|
|
3
|
+
*
|
|
4
|
+
* Mirrors the Fastify schemas for `POST /api/admin/grants/direct` and the
|
|
5
|
+
* persisted grant row. `granted_by` is intentionally absent from the create
|
|
6
|
+
* body — it is set by the server from the authenticated actor (see PRD-00544
|
|
7
|
+
* direct-grant-audit-trail and the `direct-grant-audit` CI guard).
|
|
8
|
+
*/
|
|
9
|
+
import { z } from 'zod';
|
|
10
|
+
|
|
11
|
+
export const DirectGrantCreate = z.object({
|
|
12
|
+
subject_id: z.string().uuid(),
|
|
13
|
+
scope_pattern: z.string(),
|
|
14
|
+
resource_filter: z.record(z.string(), z.unknown()).optional(),
|
|
15
|
+
expires_at: z.string().datetime().optional(),
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
export type DirectGrantCreate = z.infer<typeof DirectGrantCreate>;
|
|
19
|
+
|
|
20
|
+
export const DirectGrant = z.object({
|
|
21
|
+
id: z.string().uuid(),
|
|
22
|
+
org_id: z.string().uuid(),
|
|
23
|
+
subject_id: z.string().uuid(),
|
|
24
|
+
scope_pattern: z.string(),
|
|
25
|
+
source: z.literal('direct'),
|
|
26
|
+
granted_by: z.string().uuid(),
|
|
27
|
+
granted_at: z.string().datetime(),
|
|
28
|
+
expires_at: z.string().datetime().nullable(),
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
export type DirectGrant = z.infer<typeof DirectGrant>;
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
// AUTO-GENERATED — do not edit. Run pnpm generate:spec-hash to regenerate.
|
|
2
|
-
export const SPEC_HASH = '
|
|
3
|
-
export const SPEC_HASH_FULL = '
|
|
2
|
+
export const SPEC_HASH = '6edfe6e7117c' as const;
|
|
3
|
+
export const SPEC_HASH_FULL = '6edfe6e7117c49e81cd7cad51ac9cd5c6f4c9e18cf7a37ff82a5630855404a00' as const;
|
package/src/api/generated.ts
CHANGED
|
@@ -72,6 +72,26 @@ export interface paths {
|
|
|
72
72
|
patch?: never;
|
|
73
73
|
trace?: never;
|
|
74
74
|
};
|
|
75
|
+
"/api/auth/consent/grant": {
|
|
76
|
+
parameters: {
|
|
77
|
+
query?: never;
|
|
78
|
+
header?: never;
|
|
79
|
+
path?: never;
|
|
80
|
+
cookie?: never;
|
|
81
|
+
};
|
|
82
|
+
get?: never;
|
|
83
|
+
put?: never;
|
|
84
|
+
/**
|
|
85
|
+
* Grant incremental consent for a write action
|
|
86
|
+
* @description Records per-(user, org, action) consent for a registered write action. Idempotent: regranting an already-active consent re-emits the audit event but does not create a duplicate row.
|
|
87
|
+
*/
|
|
88
|
+
post: operations["grantIncrementalConsent"];
|
|
89
|
+
delete?: never;
|
|
90
|
+
options?: never;
|
|
91
|
+
head?: never;
|
|
92
|
+
patch?: never;
|
|
93
|
+
trace?: never;
|
|
94
|
+
};
|
|
75
95
|
"/api/me": {
|
|
76
96
|
parameters: {
|
|
77
97
|
query?: never;
|
|
@@ -647,38 +667,38 @@ export interface paths {
|
|
|
647
667
|
patch?: never;
|
|
648
668
|
trace?: never;
|
|
649
669
|
};
|
|
650
|
-
"/api/
|
|
670
|
+
"/api/workspace/members/{id}/role": {
|
|
651
671
|
parameters: {
|
|
652
672
|
query?: never;
|
|
653
673
|
header?: never;
|
|
654
674
|
path?: never;
|
|
655
675
|
cookie?: never;
|
|
656
676
|
};
|
|
657
|
-
|
|
658
|
-
get: operations["getRbacRoles"];
|
|
677
|
+
get?: never;
|
|
659
678
|
put?: never;
|
|
660
679
|
post?: never;
|
|
661
680
|
delete?: never;
|
|
662
681
|
options?: never;
|
|
663
682
|
head?: never;
|
|
664
|
-
|
|
683
|
+
/** Change member role */
|
|
684
|
+
patch: operations["changeMemberRole"];
|
|
665
685
|
trace?: never;
|
|
666
686
|
};
|
|
667
|
-
"/api/
|
|
687
|
+
"/api/rbac/roles": {
|
|
668
688
|
parameters: {
|
|
669
689
|
query?: never;
|
|
670
690
|
header?: never;
|
|
671
691
|
path?: never;
|
|
672
692
|
cookie?: never;
|
|
673
693
|
};
|
|
674
|
-
|
|
694
|
+
/** List RBAC roles catalog (system + custom) with scopes and member counts */
|
|
695
|
+
get: operations["getRbacRoles"];
|
|
675
696
|
put?: never;
|
|
676
697
|
post?: never;
|
|
677
698
|
delete?: never;
|
|
678
699
|
options?: never;
|
|
679
700
|
head?: never;
|
|
680
|
-
|
|
681
|
-
patch: operations["changeMemberRole"];
|
|
701
|
+
patch?: never;
|
|
682
702
|
trace?: never;
|
|
683
703
|
};
|
|
684
704
|
"/api/user/orgs": {
|
|
@@ -2036,6 +2056,15 @@ export interface components {
|
|
|
2036
2056
|
/** @constant */
|
|
2037
2057
|
ok: true;
|
|
2038
2058
|
};
|
|
2059
|
+
ConsentGrantResponse: {
|
|
2060
|
+
/** @constant */
|
|
2061
|
+
ok: true;
|
|
2062
|
+
/** Format: date-time */
|
|
2063
|
+
grantedAt: string;
|
|
2064
|
+
};
|
|
2065
|
+
ConsentGrantRequest: {
|
|
2066
|
+
actionId: string;
|
|
2067
|
+
};
|
|
2039
2068
|
MeResponse: {
|
|
2040
2069
|
/** Format: uuid */
|
|
2041
2070
|
userId: string;
|
|
@@ -2355,49 +2384,26 @@ export interface components {
|
|
|
2355
2384
|
WorkspaceHandleRequest: {
|
|
2356
2385
|
handle: string;
|
|
2357
2386
|
};
|
|
2358
|
-
WorkspaceMemberUnitSummary: {
|
|
2359
|
-
unitId: string;
|
|
2360
|
-
unitName: string;
|
|
2361
|
-
unitPath: string;
|
|
2362
|
-
/** @enum {string} */
|
|
2363
|
-
role: "owner" | "manager" | "member";
|
|
2364
|
-
};
|
|
2365
|
-
WorkspaceMember: {
|
|
2366
|
-
id: string;
|
|
2367
|
-
name: string;
|
|
2368
|
-
email: string;
|
|
2369
|
-
/** @enum {string} */
|
|
2370
|
-
role: "owner" | "admin" | "member" | "auditor";
|
|
2371
|
-
roleNames: string[];
|
|
2372
|
-
joinedAt: string;
|
|
2373
|
-
lastActiveAt: string | null;
|
|
2374
|
-
unitMemberships: components["schemas"]["WorkspaceMemberUnitSummary"][];
|
|
2375
|
-
unitMembershipsTruncated: boolean;
|
|
2376
|
-
inviteStatus: ("active" | "pending" | "expired") | null;
|
|
2377
|
-
};
|
|
2378
|
-
MemberRecentAction: {
|
|
2379
|
-
id: string;
|
|
2380
|
-
timestamp: string;
|
|
2381
|
-
action: string;
|
|
2382
|
-
summary: string;
|
|
2383
|
-
};
|
|
2384
|
-
WorkspaceMemberDetail: components["schemas"]["WorkspaceMember"] & {
|
|
2385
|
-
effectiveScopes: string[];
|
|
2386
|
-
recentActions: components["schemas"]["MemberRecentAction"][];
|
|
2387
|
-
};
|
|
2388
|
-
RoleCatalogEntry: {
|
|
2389
|
-
name: string;
|
|
2390
|
-
/** @enum {string} */
|
|
2391
|
-
type: "system" | "custom";
|
|
2392
|
-
description: string;
|
|
2393
|
-
scopes: string[];
|
|
2394
|
-
memberCount: number;
|
|
2395
|
-
};
|
|
2396
|
-
RoleCatalogResponse: {
|
|
2397
|
-
roles: components["schemas"]["RoleCatalogEntry"][];
|
|
2398
|
-
};
|
|
2399
2387
|
WorkspaceMembersResponse: {
|
|
2400
|
-
items:
|
|
2388
|
+
items: {
|
|
2389
|
+
id: string;
|
|
2390
|
+
name: string;
|
|
2391
|
+
email: string;
|
|
2392
|
+
/** @enum {string} */
|
|
2393
|
+
role: "owner" | "admin" | "member" | "auditor";
|
|
2394
|
+
roleNames: string[];
|
|
2395
|
+
joinedAt: string;
|
|
2396
|
+
lastActiveAt: string | null;
|
|
2397
|
+
unitMemberships: {
|
|
2398
|
+
unitId: string;
|
|
2399
|
+
unitName: string;
|
|
2400
|
+
unitPath: string;
|
|
2401
|
+
/** @enum {string} */
|
|
2402
|
+
role: "owner" | "manager" | "member";
|
|
2403
|
+
}[];
|
|
2404
|
+
unitMembershipsTruncated: boolean;
|
|
2405
|
+
inviteStatus: ("active" | "pending" | "expired") | null;
|
|
2406
|
+
}[];
|
|
2401
2407
|
nextCursor: string | null;
|
|
2402
2408
|
hasMore: boolean;
|
|
2403
2409
|
};
|
|
@@ -2566,6 +2572,32 @@ export interface components {
|
|
|
2566
2572
|
/** @enum {string} */
|
|
2567
2573
|
errorCode?: "IDENTITY_CONFLICT" | "DOMAIN_MISMATCH" | "ISSUER_MISMATCH" | "CALLBACK_ERROR";
|
|
2568
2574
|
};
|
|
2575
|
+
WorkspaceMemberDetail: {
|
|
2576
|
+
id: string;
|
|
2577
|
+
name: string;
|
|
2578
|
+
email: string;
|
|
2579
|
+
/** @enum {string} */
|
|
2580
|
+
role: "owner" | "admin" | "member" | "auditor";
|
|
2581
|
+
roleNames: string[];
|
|
2582
|
+
joinedAt: string;
|
|
2583
|
+
lastActiveAt: string | null;
|
|
2584
|
+
unitMemberships: {
|
|
2585
|
+
unitId: string;
|
|
2586
|
+
unitName: string;
|
|
2587
|
+
unitPath: string;
|
|
2588
|
+
/** @enum {string} */
|
|
2589
|
+
role: "owner" | "manager" | "member";
|
|
2590
|
+
}[];
|
|
2591
|
+
unitMembershipsTruncated: boolean;
|
|
2592
|
+
inviteStatus: ("active" | "pending" | "expired") | null;
|
|
2593
|
+
effectiveScopes: string[];
|
|
2594
|
+
recentActions: {
|
|
2595
|
+
id: string;
|
|
2596
|
+
timestamp: string;
|
|
2597
|
+
action: string;
|
|
2598
|
+
summary: string;
|
|
2599
|
+
}[];
|
|
2600
|
+
};
|
|
2569
2601
|
RemoveMemberResponse: {
|
|
2570
2602
|
success: boolean;
|
|
2571
2603
|
memberId: string;
|
|
@@ -2584,6 +2616,16 @@ export interface components {
|
|
|
2584
2616
|
/** @enum {string} */
|
|
2585
2617
|
newRole: "admin" | "member";
|
|
2586
2618
|
};
|
|
2619
|
+
RoleCatalogResponse: {
|
|
2620
|
+
roles: {
|
|
2621
|
+
name: string;
|
|
2622
|
+
/** @enum {string} */
|
|
2623
|
+
type: "system" | "custom";
|
|
2624
|
+
description: string;
|
|
2625
|
+
scopes: string[];
|
|
2626
|
+
memberCount: number;
|
|
2627
|
+
}[];
|
|
2628
|
+
};
|
|
2587
2629
|
UserOrgsResponse: {
|
|
2588
2630
|
orgs: {
|
|
2589
2631
|
userId: string;
|
|
@@ -2802,6 +2844,7 @@ export interface components {
|
|
|
2802
2844
|
target: {
|
|
2803
2845
|
type: string;
|
|
2804
2846
|
workspaceId?: string;
|
|
2847
|
+
service?: string;
|
|
2805
2848
|
};
|
|
2806
2849
|
connectionId?: string;
|
|
2807
2850
|
returnUrl?: string;
|
|
@@ -3538,6 +3581,63 @@ export interface operations {
|
|
|
3538
3581
|
};
|
|
3539
3582
|
};
|
|
3540
3583
|
};
|
|
3584
|
+
grantIncrementalConsent: {
|
|
3585
|
+
parameters: {
|
|
3586
|
+
query?: never;
|
|
3587
|
+
header?: never;
|
|
3588
|
+
path?: never;
|
|
3589
|
+
cookie?: never;
|
|
3590
|
+
};
|
|
3591
|
+
requestBody: {
|
|
3592
|
+
content: {
|
|
3593
|
+
"application/json": components["schemas"]["ConsentGrantRequest"];
|
|
3594
|
+
};
|
|
3595
|
+
};
|
|
3596
|
+
responses: {
|
|
3597
|
+
/** @description Consent granted */
|
|
3598
|
+
200: {
|
|
3599
|
+
headers: {
|
|
3600
|
+
[name: string]: unknown;
|
|
3601
|
+
};
|
|
3602
|
+
content: {
|
|
3603
|
+
"application/json": components["schemas"]["ConsentGrantResponse"];
|
|
3604
|
+
};
|
|
3605
|
+
};
|
|
3606
|
+
/** @description Invalid or unregistered actionId */
|
|
3607
|
+
400: {
|
|
3608
|
+
headers: {
|
|
3609
|
+
[name: string]: unknown;
|
|
3610
|
+
};
|
|
3611
|
+
content: {
|
|
3612
|
+
"application/json": {
|
|
3613
|
+
error: string;
|
|
3614
|
+
message: string;
|
|
3615
|
+
};
|
|
3616
|
+
};
|
|
3617
|
+
};
|
|
3618
|
+
/** @description Not authenticated */
|
|
3619
|
+
401: {
|
|
3620
|
+
headers: {
|
|
3621
|
+
[name: string]: unknown;
|
|
3622
|
+
};
|
|
3623
|
+
content?: never;
|
|
3624
|
+
};
|
|
3625
|
+
/** @description CSRF token missing or invalid */
|
|
3626
|
+
403: {
|
|
3627
|
+
headers: {
|
|
3628
|
+
[name: string]: unknown;
|
|
3629
|
+
};
|
|
3630
|
+
content?: never;
|
|
3631
|
+
};
|
|
3632
|
+
/** @description Rate limit exceeded */
|
|
3633
|
+
429: {
|
|
3634
|
+
headers: {
|
|
3635
|
+
[name: string]: unknown;
|
|
3636
|
+
};
|
|
3637
|
+
content?: never;
|
|
3638
|
+
};
|
|
3639
|
+
};
|
|
3640
|
+
};
|
|
3541
3641
|
getCurrentUser: {
|
|
3542
3642
|
parameters: {
|
|
3543
3643
|
query?: never;
|
|
@@ -4467,48 +4567,48 @@ export interface operations {
|
|
|
4467
4567
|
};
|
|
4468
4568
|
};
|
|
4469
4569
|
};
|
|
4470
|
-
|
|
4570
|
+
changeMemberRole: {
|
|
4471
4571
|
parameters: {
|
|
4472
4572
|
query?: never;
|
|
4473
4573
|
header?: never;
|
|
4474
|
-
path
|
|
4574
|
+
path: {
|
|
4575
|
+
id: string;
|
|
4576
|
+
};
|
|
4475
4577
|
cookie?: never;
|
|
4476
4578
|
};
|
|
4477
|
-
requestBody
|
|
4579
|
+
requestBody: {
|
|
4580
|
+
content: {
|
|
4581
|
+
"application/json": components["schemas"]["ChangeMemberRoleRequest"];
|
|
4582
|
+
};
|
|
4583
|
+
};
|
|
4478
4584
|
responses: {
|
|
4479
|
-
/** @description Role
|
|
4585
|
+
/** @description Role changed successfully */
|
|
4480
4586
|
200: {
|
|
4481
4587
|
headers: {
|
|
4482
4588
|
[name: string]: unknown;
|
|
4483
4589
|
};
|
|
4484
4590
|
content: {
|
|
4485
|
-
"application/json": components["schemas"]["
|
|
4591
|
+
"application/json": components["schemas"]["ChangeMemberRoleResponse"];
|
|
4486
4592
|
};
|
|
4487
4593
|
};
|
|
4488
4594
|
};
|
|
4489
4595
|
};
|
|
4490
|
-
|
|
4596
|
+
getRbacRoles: {
|
|
4491
4597
|
parameters: {
|
|
4492
4598
|
query?: never;
|
|
4493
4599
|
header?: never;
|
|
4494
|
-
path
|
|
4495
|
-
id: string;
|
|
4496
|
-
};
|
|
4600
|
+
path?: never;
|
|
4497
4601
|
cookie?: never;
|
|
4498
4602
|
};
|
|
4499
|
-
requestBody
|
|
4500
|
-
content: {
|
|
4501
|
-
"application/json": components["schemas"]["ChangeMemberRoleRequest"];
|
|
4502
|
-
};
|
|
4503
|
-
};
|
|
4603
|
+
requestBody?: never;
|
|
4504
4604
|
responses: {
|
|
4505
|
-
/** @description Role
|
|
4605
|
+
/** @description Role catalog */
|
|
4506
4606
|
200: {
|
|
4507
4607
|
headers: {
|
|
4508
4608
|
[name: string]: unknown;
|
|
4509
4609
|
};
|
|
4510
4610
|
content: {
|
|
4511
|
-
"application/json": components["schemas"]["
|
|
4611
|
+
"application/json": components["schemas"]["RoleCatalogResponse"];
|
|
4512
4612
|
};
|
|
4513
4613
|
};
|
|
4514
4614
|
};
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dispatch-deny vocabulary.
|
|
3
|
+
*
|
|
4
|
+
* The four deny paths on the agent-action dispatch flow — consent missing,
|
|
5
|
+
* write-tier disabled, per-provider kill switch, and execution-budget
|
|
6
|
+
* exhausted — all return the canonical `ErrorResponse` envelope with one of
|
|
7
|
+
* the codes defined below plus a per-code `meta` payload. The shapes are
|
|
8
|
+
* shared between the backend (which emits) and the app (which renders a
|
|
9
|
+
* recovery affordance), so they live in contracts per the promotion rule.
|
|
10
|
+
*
|
|
11
|
+
* HTTP status is carried by the envelope, not duplicated here; conventional
|
|
12
|
+
* mapping in the backend:
|
|
13
|
+
* - `incremental_consent_required` → 402 (grant required to proceed)
|
|
14
|
+
* - `write_tier_disabled` → 403
|
|
15
|
+
* - `provider_access_denied` → 403
|
|
16
|
+
* - `execution_budget_exceeded` → 429
|
|
17
|
+
*
|
|
18
|
+
* @see ../errors/index.ts for the `ErrorResponse` envelope.
|
|
19
|
+
* @see PRD-00519/00521/00522 — the PRDs that introduced these denies.
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
export const DISPATCH_DENY_CODES = [
|
|
23
|
+
'incremental_consent_required',
|
|
24
|
+
'write_tier_disabled',
|
|
25
|
+
'provider_access_denied',
|
|
26
|
+
'execution_budget_exceeded',
|
|
27
|
+
] as const;
|
|
28
|
+
|
|
29
|
+
export type DispatchDenyCode = (typeof DISPATCH_DENY_CODES)[number];
|
|
30
|
+
|
|
31
|
+
export type ActionTier = 'SAFE' | 'EXTERNAL' | 'DESTRUCTIVE';
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* The user has no active grant row in `incremental_consents` for the
|
|
35
|
+
* `(user_id, org_id, action_id)` triple. Recoverable by the user: they
|
|
36
|
+
* POST `grantPath` to create the grant, then retry the original action.
|
|
37
|
+
*/
|
|
38
|
+
export interface IncrementalConsentRequiredMeta {
|
|
39
|
+
actionId: string;
|
|
40
|
+
tier: ActionTier;
|
|
41
|
+
requiredScopes: string[];
|
|
42
|
+
/** HTTP path the app can POST to with `{ actionId }` to create the grant. */
|
|
43
|
+
grantPath: string;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* The global `WRITE_TIER_ENABLED[tier]` kill switch is off. Recoverable
|
|
48
|
+
* only by an operator flipping the config — not user-actionable.
|
|
49
|
+
*/
|
|
50
|
+
export interface WriteTierDisabledMeta {
|
|
51
|
+
actionId: string;
|
|
52
|
+
tier: ActionTier;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Distinct reasons `ProviderAccessGate.canDispatchAction` can deny.
|
|
57
|
+
* Kept separate from the global `WRITE_TIER_ENABLED` codes so the app
|
|
58
|
+
* can phrase the message by cause (provider paused vs. org override vs.
|
|
59
|
+
* tier paused for this provider).
|
|
60
|
+
*/
|
|
61
|
+
export type ProviderAccessDenyReason =
|
|
62
|
+
| 'provider-disabled'
|
|
63
|
+
| 'org-override'
|
|
64
|
+
| 'tier-SAFE-disabled'
|
|
65
|
+
| 'tier-EXTERNAL-disabled'
|
|
66
|
+
| 'tier-DESTRUCTIVE-disabled'
|
|
67
|
+
| 'write-tier-SAFE-globally-disabled'
|
|
68
|
+
| 'write-tier-EXTERNAL-globally-disabled'
|
|
69
|
+
| 'write-tier-DESTRUCTIVE-globally-disabled';
|
|
70
|
+
|
|
71
|
+
export interface ProviderAccessDeniedMeta {
|
|
72
|
+
reason: ProviderAccessDenyReason;
|
|
73
|
+
provider: string;
|
|
74
|
+
tier: ActionTier;
|
|
75
|
+
actionType: string;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* The signed execution has hit one of its atomic per-run caps. `kind` tells
|
|
80
|
+
* the app which cap tripped so it can phrase the message correctly; the
|
|
81
|
+
* recovery is always "start a new session / execution".
|
|
82
|
+
*/
|
|
83
|
+
export interface ExecutionBudgetExceededMeta {
|
|
84
|
+
kind: 'token_uses' | 'time_window' | 'provider_calls' | 'rows_returned';
|
|
85
|
+
executionId: string;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/** Type-level map: deny code → meta shape. Drives the app's deny renderer. */
|
|
89
|
+
export interface DispatchDenyMetaByCode {
|
|
90
|
+
incremental_consent_required: IncrementalConsentRequiredMeta;
|
|
91
|
+
write_tier_disabled: WriteTierDisabledMeta;
|
|
92
|
+
provider_access_denied: ProviderAccessDeniedMeta;
|
|
93
|
+
execution_budget_exceeded: ExecutionBudgetExceededMeta;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/** Narrow `unknown` to a `DispatchDenyCode` for error-code dispatch switches. */
|
|
97
|
+
export function isDispatchDenyCode(code: unknown): code is DispatchDenyCode {
|
|
98
|
+
return typeof code === 'string' && (DISPATCH_DENY_CODES as readonly string[]).includes(code);
|
|
99
|
+
}
|
|
@@ -9,6 +9,7 @@ export const openApiRoutes = {
|
|
|
9
9
|
'/api/account/deletion-eligibility': ['GET'],
|
|
10
10
|
'/api/account/sessions': ['GET'],
|
|
11
11
|
'/api/account/sessions/{sessionId}': ['DELETE'],
|
|
12
|
+
'/api/auth/consent/grant': ['POST'],
|
|
12
13
|
'/api/chats': ['GET', 'POST'],
|
|
13
14
|
'/api/chats/by-interaction/{interactionId}': ['GET'],
|
|
14
15
|
'/api/chats/events': ['GET'],
|
|
@@ -47,7 +48,8 @@ export const openApiRoutes = {
|
|
|
47
48
|
'/api/internal-admin/impersonate/start': ['POST'],
|
|
48
49
|
'/api/me': ['GET'],
|
|
49
50
|
'/api/org-units': ['POST'],
|
|
50
|
-
'/api/org-units/
|
|
51
|
+
'/api/org-units/tree': ['GET'],
|
|
52
|
+
'/api/org-units/{unitId}': ['GET', 'PATCH'],
|
|
51
53
|
'/api/org-units/{unitId}/ancestors': ['GET'],
|
|
52
54
|
'/api/org-units/{unitId}/archive': ['POST'],
|
|
53
55
|
'/api/org-units/{unitId}/children': ['GET'],
|
|
@@ -73,7 +75,7 @@ export const openApiRoutes = {
|
|
|
73
75
|
'/api/orgs/{orgId}/billing': ['GET'],
|
|
74
76
|
'/api/orgs/{orgId}/budget-config': ['GET', 'PUT'],
|
|
75
77
|
'/api/orgs/{orgId}/level-config': ['GET', 'PUT'],
|
|
76
|
-
'/api/
|
|
78
|
+
'/api/rbac/roles': ['GET'],
|
|
77
79
|
'/api/scope/check': ['GET'],
|
|
78
80
|
'/api/scope/check-batch': ['POST'],
|
|
79
81
|
'/api/shared/{token}': ['GET'],
|
|
@@ -103,7 +105,7 @@ export const openApiRoutes = {
|
|
|
103
105
|
'/api/workspace/invites/validate': ['GET'],
|
|
104
106
|
'/api/workspace/invites/{id}': ['DELETE'],
|
|
105
107
|
'/api/workspace/members': ['GET'],
|
|
106
|
-
'/api/workspace/members/{id}': ['DELETE'],
|
|
108
|
+
'/api/workspace/members/{id}': ['DELETE', 'GET'],
|
|
107
109
|
'/api/workspace/members/{id}/role': ['PATCH'],
|
|
108
110
|
'/api/workspace/name': ['PATCH'],
|
|
109
111
|
'/api/workspace/resolve-path': ['POST'],
|
package/src/index.ts
CHANGED
|
@@ -166,6 +166,22 @@ export type {
|
|
|
166
166
|
export type { AuthStartMode, AuthStartResponse } from './auth/index'
|
|
167
167
|
export { OTPErrorCode } from './auth/index'
|
|
168
168
|
|
|
169
|
+
// Dispatch deny vocabulary (PRD-00519/00521/00522)
|
|
170
|
+
export {
|
|
171
|
+
DISPATCH_DENY_CODES,
|
|
172
|
+
isDispatchDenyCode,
|
|
173
|
+
} from './dispatch/index'
|
|
174
|
+
export type {
|
|
175
|
+
ActionTier,
|
|
176
|
+
DispatchDenyCode,
|
|
177
|
+
DispatchDenyMetaByCode,
|
|
178
|
+
ExecutionBudgetExceededMeta,
|
|
179
|
+
IncrementalConsentRequiredMeta,
|
|
180
|
+
ProviderAccessDenyReason,
|
|
181
|
+
ProviderAccessDeniedMeta,
|
|
182
|
+
WriteTierDisabledMeta,
|
|
183
|
+
} from './dispatch/index'
|
|
184
|
+
|
|
169
185
|
// Email domain types
|
|
170
186
|
// @see ADR-CONT-034 for design rationale
|
|
171
187
|
export type {
|