@kindgi/api 0.1.1 → 0.1.3
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/app.d.ts.map +1 -1
- package/dist/app.js +2 -1
- package/dist/app.js.map +1 -1
- package/dist/cost-binding.d.ts +81 -5
- package/dist/cost-binding.d.ts.map +1 -1
- package/dist/cost-binding.js +6 -0
- package/dist/cost-binding.js.map +1 -1
- package/dist/hitl-binding.d.ts +20 -5
- package/dist/hitl-binding.d.ts.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/middleware/auth.d.ts +3 -2
- package/dist/middleware/auth.d.ts.map +1 -1
- package/dist/middleware/auth.js.map +1 -1
- package/dist/middleware/idempotency.d.ts +5 -1
- package/dist/middleware/idempotency.d.ts.map +1 -1
- package/dist/middleware/idempotency.js +8 -1
- package/dist/middleware/idempotency.js.map +1 -1
- package/dist/openapi/operations.d.ts.map +1 -1
- package/dist/openapi/operations.js +83 -18
- package/dist/openapi/operations.js.map +1 -1
- package/dist/openapi/schemas.d.ts +18 -0
- package/dist/openapi/schemas.d.ts.map +1 -1
- package/dist/openapi/schemas.js +202 -7
- package/dist/openapi/schemas.js.map +1 -1
- package/dist/provenance-binding.d.ts +27 -1
- package/dist/provenance-binding.d.ts.map +1 -1
- package/dist/provenance-binding.js.map +1 -1
- package/dist/reviewer-binding.d.ts +10 -2
- package/dist/reviewer-binding.d.ts.map +1 -1
- package/dist/reviewer-role.d.ts +13 -0
- package/dist/reviewer-role.d.ts.map +1 -0
- package/dist/reviewer-role.js +27 -0
- package/dist/reviewer-role.js.map +1 -0
- package/dist/routes/approvals.d.ts +5 -4
- package/dist/routes/approvals.d.ts.map +1 -1
- package/dist/routes/approvals.js +52 -16
- package/dist/routes/approvals.js.map +1 -1
- package/dist/routes/conversations.d.ts +7 -1
- package/dist/routes/conversations.d.ts.map +1 -1
- package/dist/routes/conversations.js +41 -1
- package/dist/routes/conversations.js.map +1 -1
- package/dist/routes/cost.d.ts.map +1 -1
- package/dist/routes/cost.js +95 -9
- package/dist/routes/cost.js.map +1 -1
- package/dist/routes/identity.d.ts +7 -0
- package/dist/routes/identity.d.ts.map +1 -1
- package/dist/routes/identity.js +3 -2
- package/dist/routes/identity.js.map +1 -1
- package/dist/routes/provenance.d.ts.map +1 -1
- package/dist/routes/provenance.js +32 -1
- package/dist/routes/provenance.js.map +1 -1
- package/dist/routes/runs.d.ts +0 -7
- package/dist/routes/runs.d.ts.map +1 -1
- package/dist/routes/runs.js +44 -20
- package/dist/routes/runs.js.map +1 -1
- package/dist/routes/scope-params.d.ts +16 -1
- package/dist/routes/scope-params.d.ts.map +1 -1
- package/dist/routes/scope-params.js +28 -0
- package/dist/routes/scope-params.js.map +1 -1
- package/dist/routes/sse.d.ts +2 -2
- package/dist/routes/sse.js +1 -1
- package/dist/types.d.ts +4 -3
- package/dist/types.d.ts.map +1 -1
- package/dist/webhook-endpoint-binding.d.ts +11 -0
- package/dist/webhook-endpoint-binding.d.ts.map +1 -1
- package/dist/webhook-endpoint-binding.js.map +1 -1
- package/openapi.json +599 -24
- package/package.json +24 -24
- package/src/app.ts +5 -1
- package/src/cost-binding.ts +96 -5
- package/src/hitl-binding.ts +20 -4
- package/src/index.ts +3 -0
- package/src/middleware/auth.ts +3 -2
- package/src/middleware/idempotency.ts +7 -1
- package/src/openapi/operations.ts +93 -18
- package/src/openapi/schemas.ts +221 -7
- package/src/provenance-binding.ts +42 -1
- package/src/reviewer-binding.ts +10 -2
- package/src/reviewer-role.ts +35 -0
- package/src/routes/approvals.ts +70 -19
- package/src/routes/conversations.ts +57 -1
- package/src/routes/cost.ts +104 -20
- package/src/routes/identity.ts +10 -2
- package/src/routes/provenance.ts +48 -1
- package/src/routes/runs.ts +54 -22
- package/src/routes/scope-params.ts +35 -1
- package/src/routes/sse.ts +2 -2
- package/src/types.ts +4 -3
- package/src/webhook-endpoint-binding.ts +11 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kindgi/api",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"description": "REST + SSE HTTP surface for Kindgi™. createApp assembles a Hono app serving the /v1/* REST API (bearer or session-token auth; OpenAPI 3.1 document at /v1/openapi.json) and an optional S3-compatible /s3/* surface (SigV4), over caller-plugged bindings for the runtime (runs, agents, flows, HITL, supervisor) and for storage. Route conventions: docs/API-ROUTE-CONVENTIONS.md.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"repository": {
|
|
@@ -18,7 +18,8 @@
|
|
|
18
18
|
"exports": {
|
|
19
19
|
".": {
|
|
20
20
|
"types": "./dist/index.d.ts",
|
|
21
|
-
"import": "./dist/index.js"
|
|
21
|
+
"import": "./dist/index.js",
|
|
22
|
+
"default": "./dist/index.js"
|
|
22
23
|
},
|
|
23
24
|
"./openapi.json": "./openapi.json",
|
|
24
25
|
"./package.json": "./package.json"
|
|
@@ -31,31 +32,30 @@
|
|
|
31
32
|
"openapi.json"
|
|
32
33
|
],
|
|
33
34
|
"dependencies": {
|
|
34
|
-
"@kindgi/agents": "0.1.
|
|
35
|
-
"@kindgi/audit-events": "0.1.
|
|
36
|
-
"@kindgi/compliance": "0.1.
|
|
37
|
-
"@kindgi/authz": "0.1.
|
|
38
|
-
"@kindgi/blob-binding": "0.1.
|
|
39
|
-
"@kindgi/capabilities": "0.1.
|
|
40
|
-
"@kindgi/crypto": "0.1.
|
|
41
|
-
"@kindgi/flow": "0.1.
|
|
42
|
-
"@kindgi/guardrails": "0.1.
|
|
43
|
-
"@kindgi/memory": "0.1.
|
|
44
|
-
"@kindgi/platform": "0.1.
|
|
45
|
-
"@kindgi/provenance": "0.1.
|
|
46
|
-
"@kindgi/runtime": "0.1.
|
|
47
|
-
"@kindgi/policy-contract": "0.1.
|
|
48
|
-
"@kindgi/schema": "0.1.
|
|
49
|
-
"@kindgi/tools": "0.1.
|
|
50
|
-
"@kindgi/types": "0.1.
|
|
35
|
+
"@kindgi/agents": "0.1.3",
|
|
36
|
+
"@kindgi/audit-events": "0.1.3",
|
|
37
|
+
"@kindgi/compliance": "0.1.3",
|
|
38
|
+
"@kindgi/authz": "0.1.3",
|
|
39
|
+
"@kindgi/blob-binding": "0.1.3",
|
|
40
|
+
"@kindgi/capabilities": "0.1.3",
|
|
41
|
+
"@kindgi/crypto": "0.1.3",
|
|
42
|
+
"@kindgi/flow": "0.1.3",
|
|
43
|
+
"@kindgi/guardrails": "0.1.3",
|
|
44
|
+
"@kindgi/memory": "0.1.3",
|
|
45
|
+
"@kindgi/platform": "0.1.3",
|
|
46
|
+
"@kindgi/provenance": "0.1.3",
|
|
47
|
+
"@kindgi/runtime": "0.1.3",
|
|
48
|
+
"@kindgi/policy-contract": "0.1.3",
|
|
49
|
+
"@kindgi/schema": "0.1.3",
|
|
50
|
+
"@kindgi/tools": "0.1.3",
|
|
51
|
+
"@kindgi/types": "0.1.3",
|
|
51
52
|
"@scalar/hono-api-reference": "^0.12.2",
|
|
52
|
-
"drizzle-orm": "^0.36.4",
|
|
53
53
|
"hono": "^4.6.14"
|
|
54
54
|
},
|
|
55
55
|
"devDependencies": {
|
|
56
|
-
"@kindgi/audit-events-inmemory": "0.1.
|
|
57
|
-
"@kindgi/specs": "0.1.
|
|
58
|
-
"@kindgi/testing": "0.1.
|
|
56
|
+
"@kindgi/audit-events-inmemory": "0.1.3",
|
|
57
|
+
"@kindgi/specs": "0.1.3",
|
|
58
|
+
"@kindgi/testing": "0.1.3",
|
|
59
59
|
"@types/aws4": "^1.11.6",
|
|
60
60
|
"@types/node": "^22.10.5",
|
|
61
61
|
"aws4": "^1.13.2",
|
|
@@ -63,7 +63,7 @@
|
|
|
63
63
|
"vitest": "^2.1.8"
|
|
64
64
|
},
|
|
65
65
|
"engines": {
|
|
66
|
-
"node": ">=22.
|
|
66
|
+
"node": ">=22.12.0"
|
|
67
67
|
},
|
|
68
68
|
"publishConfig": {
|
|
69
69
|
"access": "public",
|
package/src/app.ts
CHANGED
|
@@ -955,7 +955,10 @@ export function createApp(input: CreateAppInput): Hono<AppEnv> {
|
|
|
955
955
|
authorizer,
|
|
956
956
|
),
|
|
957
957
|
);
|
|
958
|
-
v1.route(
|
|
958
|
+
v1.route(
|
|
959
|
+
'/conversations',
|
|
960
|
+
conversationsRouter(input.conversationBinding, runBinding, input.projectBinding),
|
|
961
|
+
);
|
|
959
962
|
if (publicRunTokenLimits !== undefined) {
|
|
960
963
|
v1.route(
|
|
961
964
|
'/tokens/public',
|
|
@@ -1070,6 +1073,7 @@ export function createApp(input: CreateAppInput): Hono<AppEnv> {
|
|
|
1070
1073
|
identityRouter({
|
|
1071
1074
|
...(input.identityDirectory !== undefined && { directory: input.identityDirectory }),
|
|
1072
1075
|
...(input.sessionStore !== undefined && { sessionStore: input.sessionStore }),
|
|
1076
|
+
...(input.reviewerBinding !== undefined && { reviewerBinding: input.reviewerBinding }),
|
|
1073
1077
|
}),
|
|
1074
1078
|
);
|
|
1075
1079
|
if (input.cost !== undefined) {
|
package/src/cost-binding.ts
CHANGED
|
@@ -42,13 +42,16 @@ export interface CostBinding {
|
|
|
42
42
|
getRecord(input: CostGetRecordInput): Promise<CostRecord | null>;
|
|
43
43
|
/**
|
|
44
44
|
* Multi-dimensional aggregate rollup. `groupBy` may combine any
|
|
45
|
-
* subset of `
|
|
46
|
-
*
|
|
47
|
-
*
|
|
45
|
+
* subset of `COST_GROUP_DIMENSIONS`; the binding returns one group per
|
|
46
|
+
* distinct key tuple within the required `from`..`to` window, with its
|
|
47
|
+
* cost and token sums.
|
|
48
48
|
*/
|
|
49
49
|
aggregate(input: CostAggregateInput): Promise<CostAggregateResult>;
|
|
50
50
|
}
|
|
51
51
|
|
|
52
|
+
// A binding throws when its store fails: the route answers 500. It never
|
|
53
|
+
// answers a failed read with an empty page or zero sums.
|
|
54
|
+
|
|
52
55
|
// -------- listRecords --------
|
|
53
56
|
|
|
54
57
|
export interface CostListRecordsInput {
|
|
@@ -80,6 +83,8 @@ export interface CostListRecordsInput {
|
|
|
80
83
|
* uniformity: scope-aware bindings share one filter shape.
|
|
81
84
|
*/
|
|
82
85
|
readonly inherit?: boolean;
|
|
86
|
+
/** Add each record's `rawUsage`: the vendor's own usage object. */
|
|
87
|
+
readonly includeRawUsage?: boolean;
|
|
83
88
|
}
|
|
84
89
|
|
|
85
90
|
export interface CostRecordFilter {
|
|
@@ -94,7 +99,20 @@ export interface CostRecordFilter {
|
|
|
94
99
|
*/
|
|
95
100
|
readonly category?: string;
|
|
96
101
|
readonly providerId?: string;
|
|
102
|
+
/** The model actually called. */
|
|
103
|
+
readonly model?: string;
|
|
104
|
+
/** The exact model version the vendor reported. */
|
|
105
|
+
readonly servedModel?: string;
|
|
106
|
+
/** Every record of the run tree whose root is this run. */
|
|
107
|
+
readonly rootRunId?: string;
|
|
108
|
+
/**
|
|
109
|
+
* With `runId`: that run's records and those of every run it started,
|
|
110
|
+
* at any depth.
|
|
111
|
+
*/
|
|
112
|
+
readonly includeDescendants?: boolean;
|
|
113
|
+
/** Inclusive. */
|
|
97
114
|
readonly from?: Date;
|
|
115
|
+
/** Exclusive. */
|
|
98
116
|
readonly to?: Date;
|
|
99
117
|
}
|
|
100
118
|
|
|
@@ -108,6 +126,8 @@ export interface CostRecordPage {
|
|
|
108
126
|
export interface CostGetRecordInput {
|
|
109
127
|
readonly tenantId: TenantId;
|
|
110
128
|
readonly recordId: string;
|
|
129
|
+
/** Add the record's `rawUsage`: the vendor's own usage object. */
|
|
130
|
+
readonly includeRawUsage?: boolean;
|
|
111
131
|
}
|
|
112
132
|
|
|
113
133
|
// -------- aggregate --------
|
|
@@ -116,7 +136,9 @@ export interface CostGetRecordInput {
|
|
|
116
136
|
* Group dimensions the binding may aggregate over. `day` / `month` are
|
|
117
137
|
* time bucketing on `occurredAt` (UTC calendar day / month). Every call
|
|
118
138
|
* is tenant-scoped, so `tenant` always resolves to the caller's tenant
|
|
119
|
-
* id.
|
|
139
|
+
* id. `model` is the model actually called; `servedModel` the exact
|
|
140
|
+
* version the vendor reported. `orgId` is the org of the record's
|
|
141
|
+
* project (`null` for a project in no org).
|
|
120
142
|
*/
|
|
121
143
|
export type CostGroupDimension =
|
|
122
144
|
| 'agentId'
|
|
@@ -126,7 +148,13 @@ export type CostGroupDimension =
|
|
|
126
148
|
| 'day'
|
|
127
149
|
| 'month'
|
|
128
150
|
| 'tenant'
|
|
129
|
-
| 'conversationId'
|
|
151
|
+
| 'conversationId'
|
|
152
|
+
| 'model'
|
|
153
|
+
| 'servedModel'
|
|
154
|
+
| 'projectId'
|
|
155
|
+
| 'orgId'
|
|
156
|
+
| 'rootRunId'
|
|
157
|
+
| 'flowId';
|
|
130
158
|
|
|
131
159
|
export const COST_GROUP_DIMENSIONS: readonly CostGroupDimension[] = [
|
|
132
160
|
'agentId',
|
|
@@ -137,6 +165,12 @@ export const COST_GROUP_DIMENSIONS: readonly CostGroupDimension[] = [
|
|
|
137
165
|
'month',
|
|
138
166
|
'tenant',
|
|
139
167
|
'conversationId',
|
|
168
|
+
'model',
|
|
169
|
+
'servedModel',
|
|
170
|
+
'projectId',
|
|
171
|
+
'orgId',
|
|
172
|
+
'rootRunId',
|
|
173
|
+
'flowId',
|
|
140
174
|
];
|
|
141
175
|
|
|
142
176
|
export interface CostAggregateInput {
|
|
@@ -174,12 +208,27 @@ export interface CostAggregateGroup {
|
|
|
174
208
|
readonly key: Readonly<Record<string, string | null>>;
|
|
175
209
|
readonly count: number;
|
|
176
210
|
readonly totalUsd: number;
|
|
211
|
+
readonly tokens: CostTokenTotals;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* Token sums. `prompt` and `completion` are the totals; `cacheRead` /
|
|
216
|
+
* `cacheWrite` are parts of `prompt`, `reasoning` of `completion`
|
|
217
|
+
* (`0` where providers didn't report them).
|
|
218
|
+
*/
|
|
219
|
+
export interface CostTokenTotals {
|
|
220
|
+
readonly prompt: number;
|
|
221
|
+
readonly completion: number;
|
|
222
|
+
readonly cacheRead: number;
|
|
223
|
+
readonly cacheWrite: number;
|
|
224
|
+
readonly reasoning: number;
|
|
177
225
|
}
|
|
178
226
|
|
|
179
227
|
export interface CostAggregateResult {
|
|
180
228
|
readonly groups: readonly CostAggregateGroup[];
|
|
181
229
|
readonly totalUsd: number;
|
|
182
230
|
readonly totalRecords: number;
|
|
231
|
+
readonly tokens: CostTokenTotals;
|
|
183
232
|
readonly timeRange: {
|
|
184
233
|
readonly from: Timestamp;
|
|
185
234
|
readonly to: Timestamp;
|
|
@@ -210,4 +259,46 @@ export interface CostRecord {
|
|
|
210
259
|
readonly occurredAt: Timestamp;
|
|
211
260
|
readonly metrics?: Readonly<Record<string, unknown>>;
|
|
212
261
|
readonly attributes?: Readonly<Record<string, unknown>>;
|
|
262
|
+
// A model call (`category` `llm.inference`):
|
|
263
|
+
/** The call's id; its provenance node carries it too. */
|
|
264
|
+
readonly callId?: string;
|
|
265
|
+
readonly projectId?: string;
|
|
266
|
+
/** The root of the record's run tree. */
|
|
267
|
+
readonly rootRunId?: string;
|
|
268
|
+
readonly parentRunId?: string;
|
|
269
|
+
readonly agentVersion?: string;
|
|
270
|
+
/** The flow of the run tree's root. */
|
|
271
|
+
readonly flowId?: string;
|
|
272
|
+
/** The step that made the call, and the turn's step number. */
|
|
273
|
+
readonly nodeId?: string;
|
|
274
|
+
readonly step?: number;
|
|
275
|
+
/** What the call was for, beyond the turn's own model step (`guardrail-judge:<id>`). */
|
|
276
|
+
readonly purpose?: string;
|
|
277
|
+
/** The model actually called. */
|
|
278
|
+
readonly model?: string;
|
|
279
|
+
/** The exact model version the vendor reported. */
|
|
280
|
+
readonly servedModel?: string;
|
|
281
|
+
/** The router picked a fallback provider. */
|
|
282
|
+
readonly fallback?: boolean;
|
|
283
|
+
/** `ok`: the provider answered. `failed`: the call threw. */
|
|
284
|
+
readonly status?: 'ok' | 'failed';
|
|
285
|
+
readonly usage?: {
|
|
286
|
+
readonly promptTokens: number;
|
|
287
|
+
readonly completionTokens: number;
|
|
288
|
+
readonly cacheReadTokens?: number;
|
|
289
|
+
readonly cacheWriteTokens?: number;
|
|
290
|
+
readonly reasoningTokens?: number;
|
|
291
|
+
};
|
|
292
|
+
readonly durationMs?: number;
|
|
293
|
+
readonly finishReason?: string;
|
|
294
|
+
readonly providerRequestId?: string;
|
|
295
|
+
/** HTTP attempts, the client's retries included. */
|
|
296
|
+
readonly attempts?: number;
|
|
297
|
+
readonly error?: { readonly message: string };
|
|
298
|
+
/** The vendor's own usage object (only when asked for: `include=rawUsage`). */
|
|
299
|
+
readonly rawUsage?: {
|
|
300
|
+
readonly provider: string;
|
|
301
|
+
readonly model: string;
|
|
302
|
+
readonly usage: Readonly<Record<string, unknown>>;
|
|
303
|
+
};
|
|
213
304
|
}
|
package/src/hitl-binding.ts
CHANGED
|
@@ -12,6 +12,7 @@ import type { ReviewerRole } from '@kindgi/authz';
|
|
|
12
12
|
import type {
|
|
13
13
|
ApprovalId,
|
|
14
14
|
Cursor,
|
|
15
|
+
ListScope,
|
|
15
16
|
ProjectId,
|
|
16
17
|
ProvenanceId,
|
|
17
18
|
Result,
|
|
@@ -57,6 +58,12 @@ export interface Approval {
|
|
|
57
58
|
readonly updatedAt: Timestamp;
|
|
58
59
|
readonly decidedAt?: Timestamp;
|
|
59
60
|
readonly expiresAt?: Timestamp;
|
|
61
|
+
/**
|
|
62
|
+
* The reviewer's decision, once one is recorded. Absent while the
|
|
63
|
+
* approval is open, and when it ended without one (it expired, or a
|
|
64
|
+
* timeout escalated it).
|
|
65
|
+
*/
|
|
66
|
+
readonly decision?: ReviewDecisionRecord;
|
|
60
67
|
}
|
|
61
68
|
|
|
62
69
|
export interface ReviewDecision {
|
|
@@ -76,6 +83,8 @@ export interface ReviewDecision {
|
|
|
76
83
|
export interface ListApprovalsBindingInput {
|
|
77
84
|
readonly tenantId: TenantId;
|
|
78
85
|
readonly limit: number;
|
|
86
|
+
/** Only one project's approvals, or every project's in an org. Absent: the tenant's. */
|
|
87
|
+
readonly scope?: ListScope;
|
|
79
88
|
readonly status?: ApprovalStatus;
|
|
80
89
|
readonly requiredRole?: ReviewerRole;
|
|
81
90
|
readonly since?: Timestamp;
|
|
@@ -110,12 +119,18 @@ export type SubmitReviewBindingResult =
|
|
|
110
119
|
};
|
|
111
120
|
|
|
112
121
|
/**
|
|
113
|
-
*
|
|
114
|
-
* `
|
|
115
|
-
*
|
|
122
|
+
* An approval's recorded decision: on the approval it decided
|
|
123
|
+
* (`Approval.decision`), and for the audit-bundle route. `null` from
|
|
124
|
+
* `loadReviewDecision` = the approval terminated without a recorded
|
|
125
|
+
* decision (e.g. status `expired`).
|
|
116
126
|
*/
|
|
117
127
|
export interface ReviewDecisionRecord {
|
|
118
128
|
readonly decision: ReviewDecisionKind;
|
|
129
|
+
/**
|
|
130
|
+
* Who decided, as an actor: `user:<userId>`, the reviewer's user.
|
|
131
|
+
* Absent from a binding that doesn't record it (the Kindgi runtime does).
|
|
132
|
+
*/
|
|
133
|
+
readonly decidedBy?: string;
|
|
119
134
|
readonly reviewerId: ReviewerId;
|
|
120
135
|
readonly reviewerRoleAtDecision: ReviewerRole;
|
|
121
136
|
readonly decidedAt: Timestamp;
|
|
@@ -163,7 +178,8 @@ export interface HitlBinding {
|
|
|
163
178
|
/**
|
|
164
179
|
* Hydrate the recorded decision row for an approval — used by the
|
|
165
180
|
* audit-bundle route. Returns `null` when the approval terminated
|
|
166
|
-
* without a recorded decision (status `expired`).
|
|
181
|
+
* without a recorded decision (status `expired`). `getApproval` and
|
|
182
|
+
* `listApprovals` return the same decision on each approval.
|
|
167
183
|
*/
|
|
168
184
|
loadReviewDecision(
|
|
169
185
|
tenantId: TenantId,
|
package/src/index.ts
CHANGED
|
@@ -391,6 +391,7 @@ export type {
|
|
|
391
391
|
CostRecord,
|
|
392
392
|
CostRecordFilter,
|
|
393
393
|
CostRecordPage,
|
|
394
|
+
CostTokenTotals,
|
|
394
395
|
} from './cost-binding.js';
|
|
395
396
|
export type {
|
|
396
397
|
ToolGetInput,
|
|
@@ -550,6 +551,8 @@ export type {
|
|
|
550
551
|
ListProvenanceRecordsInput,
|
|
551
552
|
ListProvenanceRecordsResult,
|
|
552
553
|
ProvenanceBinding,
|
|
554
|
+
CallUsage,
|
|
555
|
+
CallUsageByCallId,
|
|
553
556
|
ProvenanceBindingError,
|
|
554
557
|
ProvenanceListCursor,
|
|
555
558
|
ProvenanceRecordSummary,
|
package/src/middleware/auth.ts
CHANGED
|
@@ -37,8 +37,9 @@ export interface TokenResolution {
|
|
|
37
37
|
* middleware surfaces it as `c.get('reviewerRole')` and approvals
|
|
38
38
|
* routes gate visibility / decisions on the `standard < senior < admin`
|
|
39
39
|
* hierarchy. Absent for tokens that were never provisioned with a
|
|
40
|
-
* reviewer role
|
|
41
|
-
*
|
|
40
|
+
* reviewer role: the approvals routes then take the role the reviewer
|
|
41
|
+
* roster gives the token's user (`ReviewerBinding.resolveReviewerRole`),
|
|
42
|
+
* and return `403 permission-denied` when it gives none.
|
|
42
43
|
*/
|
|
43
44
|
readonly reviewerRole?: ReviewerRole;
|
|
44
45
|
/**
|
|
@@ -63,7 +63,11 @@ const DEFAULT_TTL_MS = 24 * 60 * 60 * 1000;
|
|
|
63
63
|
* byte-identical (status, content-type, body).
|
|
64
64
|
* - Cached hit with different body hash → 409
|
|
65
65
|
* `idempotency-key-body-mismatch`.
|
|
66
|
-
* - Cache miss → run the handler, then store the response
|
|
66
|
+
* - Cache miss → run the handler, then store the response when the
|
|
67
|
+
* request took effect (a status below 400). A refusal (4xx) changed
|
|
68
|
+
* nothing, and a failure (5xx) may be transient: neither is stored, so
|
|
69
|
+
* a retry with the same key after fixing the cause runs again instead
|
|
70
|
+
* of replaying the old error.
|
|
67
71
|
*
|
|
68
72
|
* The middleware sits AFTER auth so `tenantId` is available; the
|
|
69
73
|
* cache key is namespaced by tenant so callers can't collide across
|
|
@@ -122,6 +126,8 @@ export function idempotencyMiddleware(
|
|
|
122
126
|
await next();
|
|
123
127
|
|
|
124
128
|
const res = c.res;
|
|
129
|
+
// Only what took effect is replayed; see the doc above.
|
|
130
|
+
if (res.status >= 400) return;
|
|
125
131
|
const savedBody = await res.clone().text();
|
|
126
132
|
const contentType = res.headers.get('Content-Type') ?? 'application/octet-stream';
|
|
127
133
|
await store.set(cacheKey, {
|