@statewavedev/sdk 0.9.0 → 0.10.1
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/README.md +104 -4
- package/dist/client.d.ts +96 -1
- package/dist/client.js +167 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/types.d.ts +246 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -12,6 +12,12 @@ Official TypeScript SDK for [Statewave](https://github.com/smaramwbc/statewave)
|
|
|
12
12
|
|
|
13
13
|
> ⚠️ **v0.9.0 is a breaking change.** The entire SDK surface — request params *and* response fields — is now idiomatic **camelCase** (`subjectId`, `maxTokens`, `createdAt`, `receiptId`, …). The wire protocol is unchanged; the client maps to/from the server's snake_case transparently. `payload`, `metadata`, and `provenance` are passed through verbatim — their inner keys are never rewritten. See [CHANGELOG](CHANGELOG.md#090) for the full rename table and migration steps.
|
|
14
14
|
|
|
15
|
+
> **New to Statewave?** This SDK is a thin client for a running **Statewave
|
|
16
|
+
> server**. If you don't have one yet, the
|
|
17
|
+
> [Getting Started guide](https://github.com/smaramwbc/statewave-docs/blob/main/getting-started.md)
|
|
18
|
+
> brings one up with Docker Compose in about 5 minutes. Every example below
|
|
19
|
+
> assumes a server reachable at `http://localhost:8100`.
|
|
20
|
+
|
|
15
21
|
## Install
|
|
16
22
|
|
|
17
23
|
```bash
|
|
@@ -91,9 +97,9 @@ console.log(`${timeline.episodes.length} episodes, ${timeline.memories.length} m
|
|
|
91
97
|
await sw.deleteSubject("user-42");
|
|
92
98
|
```
|
|
93
99
|
|
|
94
|
-
## Governance & audit (v0.8)
|
|
100
|
+
## Governance & audit (v0.8+)
|
|
95
101
|
|
|
96
|
-
The SDK surfaces the [state-assembly receipts](https://github.com/smaramwbc/statewave-docs/blob/main/receipts.md) and [sensitivity-labels / policy](https://github.com/smaramwbc/statewave-docs/blob/main/sensitivity-labels.md) layer added in server v0.8.
|
|
102
|
+
The SDK surfaces the [state-assembly receipts](https://github.com/smaramwbc/statewave-docs/blob/main/receipts.md) and [sensitivity-labels / policy](https://github.com/smaramwbc/statewave-docs/blob/main/sensitivity-labels.md) layer added in server v0.8, plus the v0.9 [HMAC signing](https://github.com/smaramwbc/statewave/blob/main/docs/state-assembly-receipts.md) and [as-of replay](https://github.com/smaramwbc/statewave/blob/main/docs/replay.md) surfaces.
|
|
97
103
|
|
|
98
104
|
```typescript
|
|
99
105
|
import { StatewaveClient } from "@statewavedev/sdk";
|
|
@@ -134,6 +140,39 @@ for (const r of receipts) {
|
|
|
134
140
|
console.log(r.receiptId, r.task);
|
|
135
141
|
}
|
|
136
142
|
|
|
143
|
+
// Verify the HMAC signature on a stored receipt (v0.9+).
|
|
144
|
+
// `valid` is true | false | null — see ReceiptVerifyResult for the
|
|
145
|
+
// full reason vocabulary (no_signature / key_unavailable / etc.).
|
|
146
|
+
if (bundle.receiptId) {
|
|
147
|
+
const verdict = await sw.verifyReceipt(bundle.receiptId);
|
|
148
|
+
if (verdict.valid === true) {
|
|
149
|
+
console.log(`signature OK — signed by ${verdict.keyId}`);
|
|
150
|
+
} else if (verdict.valid === false) {
|
|
151
|
+
console.log("signature mismatch — body may have been tampered with");
|
|
152
|
+
} else {
|
|
153
|
+
console.log(`verdict undetermined: ${verdict.reason}`);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// Replay the receipt against current memories using the original
|
|
158
|
+
// policy bundle captured on the receipt (v0.9+). Returns a diff
|
|
159
|
+
// envelope showing what changed since emission. Pre-v0.9 receipts
|
|
160
|
+
// throw StatewaveUnreplayableError with reason="missing_policy_snapshot".
|
|
161
|
+
import { StatewaveUnreplayableError } from "@statewavedev/sdk";
|
|
162
|
+
try {
|
|
163
|
+
const replay = await sw.replayReceipt(bundle.receiptId!);
|
|
164
|
+
if (replay.diff.contextHash.changed) {
|
|
165
|
+
console.log(`replay differs from original: new id ${replay.replayReceiptId}`);
|
|
166
|
+
}
|
|
167
|
+
} catch (err) {
|
|
168
|
+
if (err instanceof StatewaveUnreplayableError) {
|
|
169
|
+
// err.reason ∈ {"missing_policy_snapshot", "nested_replay", "invalid_snapshot"}
|
|
170
|
+
console.log(`replay refused: ${err.reason}`);
|
|
171
|
+
} else {
|
|
172
|
+
throw err;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
137
176
|
// Set per-memory sensitivity labels (server normalizes — dedup, lowercase, trim).
|
|
138
177
|
// Memories with labels become subject to any active policy bundle for the tenant.
|
|
139
178
|
const updated = await sw.setMemoryLabels({
|
|
@@ -145,6 +184,59 @@ console.log(updated.sensitivityLabels); // → ["financial", "pii"]
|
|
|
145
184
|
|
|
146
185
|
Receipts and the policy engine cooperate: every assembly call records its policy decisions into `receipt.policy.filtersApplied` (one entry per memory the policy fired on) and `receipt.policy.filtersSkipped` (per-rule summary of what didn't fire). In `log_only` mode (the tenant default) the receipt is the full audit trail without filtering; under `enforce` denied memories are dropped before they reach the assembly and the deny is still recorded. See [`receipts.md`](https://github.com/smaramwbc/statewave-docs/blob/main/receipts.md) and [`sensitivity-labels.md`](https://github.com/smaramwbc/statewave-docs/blob/main/sensitivity-labels.md) for the full schemas and policy YAML format.
|
|
147
186
|
|
|
187
|
+
## Support-agent endpoints
|
|
188
|
+
|
|
189
|
+
Statewave's support wedge — customer health scoring, SLA tracking, resolution state, and structured escalation briefs — is exposed through ergonomic SDK methods (server v0.6+).
|
|
190
|
+
|
|
191
|
+
```typescript
|
|
192
|
+
import { StatewaveClient } from "@statewavedev/sdk";
|
|
193
|
+
|
|
194
|
+
const sw = new StatewaveClient("http://localhost:8100");
|
|
195
|
+
|
|
196
|
+
// Customer health score (0–100) with the explainable factors behind it.
|
|
197
|
+
const health = await sw.getHealth("customer:globex");
|
|
198
|
+
console.log(`${health.score}/100 — ${health.state}`);
|
|
199
|
+
for (const f of health.factors) {
|
|
200
|
+
console.log(` ${f.signal}: ${f.impact >= 0 ? "+" : ""}${f.impact} (${f.detail})`);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// SLA metrics — first-response / resolution times and breach counts.
|
|
204
|
+
// Thresholds are optional; they default server-side to 5 min / 24 h.
|
|
205
|
+
const sla = await sw.getSLA({
|
|
206
|
+
subjectId: "customer:globex",
|
|
207
|
+
firstResponseThresholdMinutes: 10,
|
|
208
|
+
resolutionThresholdHours: 48,
|
|
209
|
+
});
|
|
210
|
+
console.log(`${sla.resolvedSessions}/${sla.totalSessions} resolved, ${sla.resolutionBreachCount} SLA breaches`);
|
|
211
|
+
|
|
212
|
+
// Track resolution state for a session (upserts by subject + session).
|
|
213
|
+
await sw.createResolution({
|
|
214
|
+
subjectId: "customer:globex",
|
|
215
|
+
sessionId: "ticket-8842",
|
|
216
|
+
status: "resolved",
|
|
217
|
+
resolutionSummary: "Issued refund for the duplicate charge",
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
// List resolutions, optionally filtered by status.
|
|
221
|
+
const openItems = await sw.listResolutions({
|
|
222
|
+
subjectId: "customer:globex",
|
|
223
|
+
status: "open",
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
// Generate a handoff context pack for escalation or shift change.
|
|
227
|
+
// `handoffNotes` is a pre-rendered markdown brief for human or LLM use.
|
|
228
|
+
const handoff = await sw.createHandoff({
|
|
229
|
+
subjectId: "customer:globex",
|
|
230
|
+
sessionId: "ticket-8842",
|
|
231
|
+
reason: "escalation",
|
|
232
|
+
callerId: "agent-7",
|
|
233
|
+
callerType: "support_agent",
|
|
234
|
+
});
|
|
235
|
+
console.log(handoff.handoffNotes);
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
`getHealth`, `getSLA`, `createResolution`, `listResolutions`, and `createHandoff` respect the same auth, tenant-scoping, and retry behaviour as the rest of the client. `createHandoff` shares `getContext`'s caller-identity gate — when the tenant config sets `require_caller_identity: true`, both `callerId` and `callerType` are mandatory.
|
|
239
|
+
|
|
148
240
|
## Error handling
|
|
149
241
|
|
|
150
242
|
```typescript
|
|
@@ -187,10 +279,18 @@ All response types are fully typed:
|
|
|
187
279
|
- `BatchCreateResult` — batch ingestion response
|
|
188
280
|
- `SubjectSummary` — subject with episode/memory counts
|
|
189
281
|
- `ListSubjectsResult` — paginated subject listing
|
|
190
|
-
- `Receipt` + `ReceiptSelectedEntry` + `ReceiptPolicy` + `ReceiptOutput` — state-assembly audit artifact (v0.8) and its nested shapes
|
|
282
|
+
- `Receipt` + `ReceiptSelectedEntry` + `ReceiptPolicy` + `ReceiptOutput` — state-assembly audit artifact (v0.8+) and its nested shapes; v0.9 added HMAC signature fields (`receiptSignatureKeyId`, `receiptSignatureAlgorithm`), `policySnapshot` for replay, and `region` for residency
|
|
283
|
+
- `ReceiptVerifyResult` — `valid` (true | false | null) + `keyId` + `algorithm` + `reason` for the v0.9 HMAC verify endpoint
|
|
284
|
+
- `ReceiptReplayResult` / `ReceiptReplayDiff` — original + replay receipt ids plus the structural diff envelope from `POST /v1/receipts/{id}/replay` (v0.9)
|
|
285
|
+
- `StatewaveUnreplayableError` — thrown by `replayReceipt(...)` on HTTP 422; `.reason` is a discriminated union of `"missing_policy_snapshot" | "nested_replay" | "invalid_snapshot"`
|
|
191
286
|
- `ReceiptList` — cursor-paginated receipt listing
|
|
287
|
+
- `Health` + `HealthFactor` — customer health score and its explainable factors
|
|
288
|
+
- `SLASummary` + `SessionSLA` — SLA metrics, aggregate and per-session
|
|
289
|
+
- `Handoff` + `ResolutionSummaryItem` — handoff context pack and its prior-resolution items
|
|
290
|
+
- `Resolution` — resolution tracking record
|
|
291
|
+
- `HealthState` / `ResolutionStatus` — string-literal status unions
|
|
192
292
|
|
|
193
|
-
Param types: `CreateEpisodeParams`, `SearchMemoriesParams`, `GetContextParams`, `ListReceiptsParams`, `SetMemoryLabelsParams`
|
|
293
|
+
Param types: `CreateEpisodeParams`, `SearchMemoriesParams`, `GetContextParams`, `ListReceiptsParams`, `SetMemoryLabelsParams`, `GetSLAParams`, `CreateHandoffParams`, `CreateResolutionParams`, `ListResolutionsParams`
|
|
194
294
|
|
|
195
295
|
## Running tests
|
|
196
296
|
|
package/dist/client.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { BatchCreateResult, ClientOptions, CompileJob, CompileResult, ContextBundle, CreateEpisodeParams, DeleteResult, Episode, GetContextParams, ListReceiptsParams, ListSubjectsResult, Memory, Receipt, ReceiptList, SearchMemoriesParams, SearchResult, SetMemoryLabelsParams, Timeline } from "./types.js";
|
|
1
|
+
import type { BatchCreateResult, ClientOptions, CompileJob, CompileResult, ContextBundle, CreateEpisodeParams, CreateHandoffParams, CreateResolutionParams, DeleteResult, Episode, GetContextParams, GetSLAParams, Handoff, Health, ListReceiptsParams, ListResolutionsParams, ListSubjectsResult, Memory, Receipt, ReceiptList, ReceiptReplayResult, ReceiptVerifyResult, Resolution, SLASummary, SearchMemoriesParams, SearchResult, SetMemoryLabelsParams, Timeline, UnreplayableReason } from "./types.js";
|
|
2
2
|
/** Structured error from the Statewave API. */
|
|
3
3
|
export declare class StatewaveAPIError extends Error {
|
|
4
4
|
readonly statusCode: number;
|
|
@@ -11,6 +11,26 @@ export declare class StatewaveAPIError extends Error {
|
|
|
11
11
|
export declare class StatewaveConnectionError extends Error {
|
|
12
12
|
constructor(message?: string);
|
|
13
13
|
}
|
|
14
|
+
/**
|
|
15
|
+
* Raised by `replayReceipt(...)` when the server refuses with HTTP 422.
|
|
16
|
+
* Subclass of `StatewaveAPIError` so generic handlers still catch it;
|
|
17
|
+
* adds a typed `reason` field so callers can branch on the structured
|
|
18
|
+
* refusal vocabulary without parsing the error code.
|
|
19
|
+
*
|
|
20
|
+
* `reason` is a discriminated union of:
|
|
21
|
+
* - `"missing_policy_snapshot"` — pre-v0.9 receipt. No
|
|
22
|
+
* `policySnapshot` was captured at emission and the replay engine
|
|
23
|
+
* cannot synthesise one retroactively.
|
|
24
|
+
* - `"nested_replay"` — the receipt is itself a replay
|
|
25
|
+
* (`mode === "as_of_replay"`). v0.9 ships one level only; replay
|
|
26
|
+
* the source receipt referenced by `parentReceiptId` instead.
|
|
27
|
+
* - `"invalid_snapshot"` — the snapshot's YAML failed to parse.
|
|
28
|
+
* Tampering or corruption at the column level.
|
|
29
|
+
*/
|
|
30
|
+
export declare class StatewaveUnreplayableError extends StatewaveAPIError {
|
|
31
|
+
readonly reason: UnreplayableReason;
|
|
32
|
+
constructor(reason: UnreplayableReason, statusCode: number, code: string, message: string, details?: unknown, requestId?: string);
|
|
33
|
+
}
|
|
14
34
|
export declare class StatewaveClient {
|
|
15
35
|
private baseUrl;
|
|
16
36
|
private defaultHeaders;
|
|
@@ -47,6 +67,81 @@ export declare class StatewaveClient {
|
|
|
47
67
|
* to fetch the next page.
|
|
48
68
|
*/
|
|
49
69
|
listReceipts(params: ListReceiptsParams): Promise<ReceiptList>;
|
|
70
|
+
/**
|
|
71
|
+
* Verify the HMAC signature on a stored receipt (v0.9+ #157).
|
|
72
|
+
*
|
|
73
|
+
* Returns a `ReceiptVerifyResult` with `valid` ∈ `{true, false, null}`:
|
|
74
|
+
* - `true` — signature matches the canonical body (`reason === "ok"`).
|
|
75
|
+
* - `false` — signature does not cover the body
|
|
76
|
+
* (`reason === "signature_mismatch"`).
|
|
77
|
+
* - `null` — verdict could not be determined; `reason` is one of
|
|
78
|
+
* `"no_signature"` (unsigned receipt — pre-v0.9 or tenant didn't
|
|
79
|
+
* opt in), `"key_unavailable"` (the keyId rotated out of operator
|
|
80
|
+
* config), or `"unsupported_algorithm"` (forward-compat).
|
|
81
|
+
*
|
|
82
|
+
* Comparison is constant-time on the server side. The signing key
|
|
83
|
+
* bytes never appear on the response — only the public `keyId` is
|
|
84
|
+
* echoed.
|
|
85
|
+
*
|
|
86
|
+
* Throws `StatewaveAPIError` on 404 (receipt not found or belongs to
|
|
87
|
+
* a different tenant — indistinguishable on the wire) and other
|
|
88
|
+
* non-2xx responses.
|
|
89
|
+
*/
|
|
90
|
+
verifyReceipt(receiptId: string): Promise<ReceiptVerifyResult>;
|
|
91
|
+
/**
|
|
92
|
+
* Re-run the original retrieval against current memories using the
|
|
93
|
+
* original policy bundle captured in the receipt's `policySnapshot`
|
|
94
|
+
* (v0.9+ #159).
|
|
95
|
+
*
|
|
96
|
+
* Emits a new `mode="as_of_replay"` receipt with `parentReceiptId`
|
|
97
|
+
* pointing at the source; the original receipt is **never**
|
|
98
|
+
* modified. Returns the new `replayReceiptId` plus a structural
|
|
99
|
+
* diff envelope (added/removed selected entries, filter changes,
|
|
100
|
+
* context-hash diff).
|
|
101
|
+
*
|
|
102
|
+
* Semantic: current code + original policy. Replay is *not*
|
|
103
|
+
* byte-for-byte reproduction; memories that were added, tombstoned,
|
|
104
|
+
* or supersession-resolved between the original emission and now
|
|
105
|
+
* will appear in the diff. See `docs/replay.md` in the server repo
|
|
106
|
+
* for the design rationale.
|
|
107
|
+
*
|
|
108
|
+
* Throws `StatewaveUnreplayableError` (HTTP 422) when:
|
|
109
|
+
* - `reason === "missing_policy_snapshot"` — pre-v0.9 receipt.
|
|
110
|
+
* - `reason === "nested_replay"` — the receipt is itself a replay.
|
|
111
|
+
* - `reason === "invalid_snapshot"` — snapshot YAML failed to parse.
|
|
112
|
+
*
|
|
113
|
+
* Throws `StatewaveAPIError` on 404 and other non-2xx responses.
|
|
114
|
+
*/
|
|
115
|
+
replayReceipt(receiptId: string): Promise<ReceiptReplayResult>;
|
|
116
|
+
/**
|
|
117
|
+
* Compute the customer health score (0–100) for a subject, with the
|
|
118
|
+
* explainable factors that drove it. Backs proactive risk triage.
|
|
119
|
+
*/
|
|
120
|
+
getHealth(subjectId: string): Promise<Health>;
|
|
121
|
+
/**
|
|
122
|
+
* Compute SLA metrics for a subject — first-response and resolution
|
|
123
|
+
* times plus breach flags, aggregated across the subject's sessions.
|
|
124
|
+
* Both thresholds fall back to the server defaults (5 minutes /
|
|
125
|
+
* 24 hours) when omitted.
|
|
126
|
+
*/
|
|
127
|
+
getSLA(params: GetSLAParams): Promise<SLASummary>;
|
|
128
|
+
/**
|
|
129
|
+
* Generate a handoff context pack — a structured escalation brief for
|
|
130
|
+
* shift change or agent transfer. Same caller-identity gate as
|
|
131
|
+
* `getContext`: when the tenant sets `require_caller_identity: true`,
|
|
132
|
+
* both `callerId` and `callerType` are mandatory.
|
|
133
|
+
*/
|
|
134
|
+
createHandoff(params: CreateHandoffParams): Promise<Handoff>;
|
|
135
|
+
/**
|
|
136
|
+
* Create or update a resolution record for a support session.
|
|
137
|
+
* Upserts by `subjectId` + `sessionId`.
|
|
138
|
+
*/
|
|
139
|
+
createResolution(params: CreateResolutionParams): Promise<Resolution>;
|
|
140
|
+
/**
|
|
141
|
+
* List resolution records for a subject, optionally filtered to a
|
|
142
|
+
* single status.
|
|
143
|
+
*/
|
|
144
|
+
listResolutions(params: ListResolutionsParams): Promise<Resolution[]>;
|
|
50
145
|
getTimeline(subjectId: string): Promise<Timeline>;
|
|
51
146
|
deleteSubject(subjectId: string): Promise<DeleteResult>;
|
|
52
147
|
listSubjects(params?: {
|
package/dist/client.js
CHANGED
|
@@ -59,6 +59,40 @@ export class StatewaveConnectionError extends Error {
|
|
|
59
59
|
this.name = "StatewaveConnectionError";
|
|
60
60
|
}
|
|
61
61
|
}
|
|
62
|
+
/** The documented refusal vocabulary for the v0.9 replay endpoint.
|
|
63
|
+
* Mirrors the `UnreplayableReason` type alias in `./types.ts`.
|
|
64
|
+
* Auto-promotion to `StatewaveUnreplayableError` only happens when
|
|
65
|
+
* the server returns a code with one of these reasons — a future
|
|
66
|
+
* unknown reason stays on the generic `StatewaveAPIError` path. */
|
|
67
|
+
const UNREPLAYABLE_REASONS = new Set([
|
|
68
|
+
"missing_policy_snapshot",
|
|
69
|
+
"nested_replay",
|
|
70
|
+
"invalid_snapshot",
|
|
71
|
+
]);
|
|
72
|
+
/**
|
|
73
|
+
* Raised by `replayReceipt(...)` when the server refuses with HTTP 422.
|
|
74
|
+
* Subclass of `StatewaveAPIError` so generic handlers still catch it;
|
|
75
|
+
* adds a typed `reason` field so callers can branch on the structured
|
|
76
|
+
* refusal vocabulary without parsing the error code.
|
|
77
|
+
*
|
|
78
|
+
* `reason` is a discriminated union of:
|
|
79
|
+
* - `"missing_policy_snapshot"` — pre-v0.9 receipt. No
|
|
80
|
+
* `policySnapshot` was captured at emission and the replay engine
|
|
81
|
+
* cannot synthesise one retroactively.
|
|
82
|
+
* - `"nested_replay"` — the receipt is itself a replay
|
|
83
|
+
* (`mode === "as_of_replay"`). v0.9 ships one level only; replay
|
|
84
|
+
* the source receipt referenced by `parentReceiptId` instead.
|
|
85
|
+
* - `"invalid_snapshot"` — the snapshot's YAML failed to parse.
|
|
86
|
+
* Tampering or corruption at the column level.
|
|
87
|
+
*/
|
|
88
|
+
export class StatewaveUnreplayableError extends StatewaveAPIError {
|
|
89
|
+
reason;
|
|
90
|
+
constructor(reason, statusCode, code, message, details, requestId) {
|
|
91
|
+
super(statusCode, code, message, details, requestId);
|
|
92
|
+
this.name = "StatewaveUnreplayableError";
|
|
93
|
+
this.reason = reason;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
62
96
|
export class StatewaveClient {
|
|
63
97
|
baseUrl;
|
|
64
98
|
defaultHeaders;
|
|
@@ -195,6 +229,128 @@ export class StatewaveClient {
|
|
|
195
229
|
qs.set("limit", String(params.limit));
|
|
196
230
|
return this.get(`/v1/receipts?${qs}`);
|
|
197
231
|
}
|
|
232
|
+
/**
|
|
233
|
+
* Verify the HMAC signature on a stored receipt (v0.9+ #157).
|
|
234
|
+
*
|
|
235
|
+
* Returns a `ReceiptVerifyResult` with `valid` ∈ `{true, false, null}`:
|
|
236
|
+
* - `true` — signature matches the canonical body (`reason === "ok"`).
|
|
237
|
+
* - `false` — signature does not cover the body
|
|
238
|
+
* (`reason === "signature_mismatch"`).
|
|
239
|
+
* - `null` — verdict could not be determined; `reason` is one of
|
|
240
|
+
* `"no_signature"` (unsigned receipt — pre-v0.9 or tenant didn't
|
|
241
|
+
* opt in), `"key_unavailable"` (the keyId rotated out of operator
|
|
242
|
+
* config), or `"unsupported_algorithm"` (forward-compat).
|
|
243
|
+
*
|
|
244
|
+
* Comparison is constant-time on the server side. The signing key
|
|
245
|
+
* bytes never appear on the response — only the public `keyId` is
|
|
246
|
+
* echoed.
|
|
247
|
+
*
|
|
248
|
+
* Throws `StatewaveAPIError` on 404 (receipt not found or belongs to
|
|
249
|
+
* a different tenant — indistinguishable on the wire) and other
|
|
250
|
+
* non-2xx responses.
|
|
251
|
+
*/
|
|
252
|
+
async verifyReceipt(receiptId) {
|
|
253
|
+
return this.get(`/v1/receipts/${encodeURIComponent(receiptId)}/verify`);
|
|
254
|
+
}
|
|
255
|
+
/**
|
|
256
|
+
* Re-run the original retrieval against current memories using the
|
|
257
|
+
* original policy bundle captured in the receipt's `policySnapshot`
|
|
258
|
+
* (v0.9+ #159).
|
|
259
|
+
*
|
|
260
|
+
* Emits a new `mode="as_of_replay"` receipt with `parentReceiptId`
|
|
261
|
+
* pointing at the source; the original receipt is **never**
|
|
262
|
+
* modified. Returns the new `replayReceiptId` plus a structural
|
|
263
|
+
* diff envelope (added/removed selected entries, filter changes,
|
|
264
|
+
* context-hash diff).
|
|
265
|
+
*
|
|
266
|
+
* Semantic: current code + original policy. Replay is *not*
|
|
267
|
+
* byte-for-byte reproduction; memories that were added, tombstoned,
|
|
268
|
+
* or supersession-resolved between the original emission and now
|
|
269
|
+
* will appear in the diff. See `docs/replay.md` in the server repo
|
|
270
|
+
* for the design rationale.
|
|
271
|
+
*
|
|
272
|
+
* Throws `StatewaveUnreplayableError` (HTTP 422) when:
|
|
273
|
+
* - `reason === "missing_policy_snapshot"` — pre-v0.9 receipt.
|
|
274
|
+
* - `reason === "nested_replay"` — the receipt is itself a replay.
|
|
275
|
+
* - `reason === "invalid_snapshot"` — snapshot YAML failed to parse.
|
|
276
|
+
*
|
|
277
|
+
* Throws `StatewaveAPIError` on 404 and other non-2xx responses.
|
|
278
|
+
*/
|
|
279
|
+
async replayReceipt(receiptId) {
|
|
280
|
+
return this.post(`/v1/receipts/${encodeURIComponent(receiptId)}/replay`, undefined);
|
|
281
|
+
}
|
|
282
|
+
// -- Support: health, SLA, handoff, resolutions ----------------------
|
|
283
|
+
/**
|
|
284
|
+
* Compute the customer health score (0–100) for a subject, with the
|
|
285
|
+
* explainable factors that drove it. Backs proactive risk triage.
|
|
286
|
+
*/
|
|
287
|
+
async getHealth(subjectId) {
|
|
288
|
+
return this.get(`/v1/subjects/${encodeURIComponent(subjectId)}/health`);
|
|
289
|
+
}
|
|
290
|
+
/**
|
|
291
|
+
* Compute SLA metrics for a subject — first-response and resolution
|
|
292
|
+
* times plus breach flags, aggregated across the subject's sessions.
|
|
293
|
+
* Both thresholds fall back to the server defaults (5 minutes /
|
|
294
|
+
* 24 hours) when omitted.
|
|
295
|
+
*/
|
|
296
|
+
async getSLA(params) {
|
|
297
|
+
const qs = new URLSearchParams();
|
|
298
|
+
if (params.firstResponseThresholdMinutes !== undefined) {
|
|
299
|
+
qs.set("first_response_threshold_minutes", String(params.firstResponseThresholdMinutes));
|
|
300
|
+
}
|
|
301
|
+
if (params.resolutionThresholdHours !== undefined) {
|
|
302
|
+
qs.set("resolution_threshold_hours", String(params.resolutionThresholdHours));
|
|
303
|
+
}
|
|
304
|
+
const query = qs.toString();
|
|
305
|
+
return this.get(`/v1/subjects/${encodeURIComponent(params.subjectId)}/sla${query ? `?${query}` : ""}`);
|
|
306
|
+
}
|
|
307
|
+
/**
|
|
308
|
+
* Generate a handoff context pack — a structured escalation brief for
|
|
309
|
+
* shift change or agent transfer. Same caller-identity gate as
|
|
310
|
+
* `getContext`: when the tenant sets `require_caller_identity: true`,
|
|
311
|
+
* both `callerId` and `callerType` are mandatory.
|
|
312
|
+
*/
|
|
313
|
+
async createHandoff(params) {
|
|
314
|
+
return this.post("/v1/handoff", {
|
|
315
|
+
subjectId: params.subjectId,
|
|
316
|
+
sessionId: params.sessionId,
|
|
317
|
+
...(params.reason !== undefined && { reason: params.reason }),
|
|
318
|
+
...(params.maxTokens !== undefined && { maxTokens: params.maxTokens }),
|
|
319
|
+
...(params.emitReceipt !== undefined && { emitReceipt: params.emitReceipt }),
|
|
320
|
+
...(params.queryId !== undefined && { queryId: params.queryId }),
|
|
321
|
+
...(params.taskId !== undefined && { taskId: params.taskId }),
|
|
322
|
+
...(params.parentReceiptId !== undefined && {
|
|
323
|
+
parentReceiptId: params.parentReceiptId,
|
|
324
|
+
}),
|
|
325
|
+
...(params.callerId !== undefined && { callerId: params.callerId }),
|
|
326
|
+
...(params.callerType !== undefined && { callerType: params.callerType }),
|
|
327
|
+
});
|
|
328
|
+
}
|
|
329
|
+
/**
|
|
330
|
+
* Create or update a resolution record for a support session.
|
|
331
|
+
* Upserts by `subjectId` + `sessionId`.
|
|
332
|
+
*/
|
|
333
|
+
async createResolution(params) {
|
|
334
|
+
return this.post("/v1/resolutions", {
|
|
335
|
+
subjectId: params.subjectId,
|
|
336
|
+
sessionId: params.sessionId,
|
|
337
|
+
...(params.status !== undefined && { status: params.status }),
|
|
338
|
+
...(params.resolutionSummary !== undefined && {
|
|
339
|
+
resolutionSummary: params.resolutionSummary,
|
|
340
|
+
}),
|
|
341
|
+
...(params.metadata !== undefined && { metadata: params.metadata }),
|
|
342
|
+
});
|
|
343
|
+
}
|
|
344
|
+
/**
|
|
345
|
+
* List resolution records for a subject, optionally filtered to a
|
|
346
|
+
* single status.
|
|
347
|
+
*/
|
|
348
|
+
async listResolutions(params) {
|
|
349
|
+
const qs = new URLSearchParams({ subject_id: params.subjectId });
|
|
350
|
+
if (params.status !== undefined)
|
|
351
|
+
qs.set("status", params.status);
|
|
352
|
+
return this.get(`/v1/resolutions?${qs}`);
|
|
353
|
+
}
|
|
198
354
|
async getTimeline(subjectId) {
|
|
199
355
|
return this.get(`/v1/timeline?subject_id=${encodeURIComponent(subjectId)}`);
|
|
200
356
|
}
|
|
@@ -281,6 +437,17 @@ export class StatewaveClient {
|
|
|
281
437
|
const body = await resp.json();
|
|
282
438
|
const err = body?.error;
|
|
283
439
|
if (err && typeof err.code === "string") {
|
|
440
|
+
// Promote unreplayable.<reason> refusals into a typed
|
|
441
|
+
// exception so callers can `catch (e) { if (e instanceof
|
|
442
|
+
// StatewaveUnreplayableError) ... e.reason }` without
|
|
443
|
+
// string-matching the error code. Forward-compat: an
|
|
444
|
+
// unrecognised future reason stays on the generic path.
|
|
445
|
+
if (resp.status === 422 && err.code.startsWith("unreplayable.")) {
|
|
446
|
+
const reason = err.code.slice("unreplayable.".length);
|
|
447
|
+
if (UNREPLAYABLE_REASONS.has(reason)) {
|
|
448
|
+
throw new StatewaveUnreplayableError(reason, resp.status, err.code, err.message ?? resp.statusText, err.details, err.request_id);
|
|
449
|
+
}
|
|
450
|
+
}
|
|
284
451
|
throw new StatewaveAPIError(resp.status, err.code, err.message ?? resp.statusText, err.details, err.request_id);
|
|
285
452
|
}
|
|
286
453
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export { StatewaveClient, StatewaveAPIError, StatewaveConnectionError } from "./client.js";
|
|
1
|
+
export { StatewaveClient, StatewaveAPIError, StatewaveConnectionError, StatewaveUnreplayableError, } from "./client.js";
|
|
2
2
|
export type * from "./types.js";
|
package/dist/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export { StatewaveClient, StatewaveAPIError, StatewaveConnectionError } from "./client.js";
|
|
1
|
+
export { StatewaveClient, StatewaveAPIError, StatewaveConnectionError, StatewaveUnreplayableError, } from "./client.js";
|
package/dist/types.d.ts
CHANGED
|
@@ -101,14 +101,38 @@ export interface ReceiptOutput {
|
|
|
101
101
|
canonicalizationVersion: number;
|
|
102
102
|
tokenEstimate: number;
|
|
103
103
|
}
|
|
104
|
+
/**
|
|
105
|
+
* v0.9 (#159) — self-contained policy bundle envelope embedded on
|
|
106
|
+
* every v0.9+ receipt. Self-sufficient: the replay engine evaluates
|
|
107
|
+
* against this bundle even if the live `policy_bundles` row has
|
|
108
|
+
* since been deleted or overwritten.
|
|
109
|
+
*
|
|
110
|
+
* - A null inner pair (`bundleHash` AND `bundleYaml` both null)
|
|
111
|
+
* records "no policy bundle was active at emission" — a valid,
|
|
112
|
+
* replayable state.
|
|
113
|
+
* - The whole envelope being absent (`Receipt.policySnapshot ===
|
|
114
|
+
* undefined`) marks "pre-v0.9 receipt, no snapshot was ever
|
|
115
|
+
* captured" — the replay endpoint refuses those.
|
|
116
|
+
*/
|
|
117
|
+
export interface PolicySnapshot {
|
|
118
|
+
bundleHash: string | null;
|
|
119
|
+
bundleYaml: string | null;
|
|
120
|
+
/** ISO-8601 UTC timestamp captured at receipt emission. */
|
|
121
|
+
capturedAt: string;
|
|
122
|
+
}
|
|
104
123
|
/**
|
|
105
124
|
* Immutable per-retrieval audit artifact for a single context assembly.
|
|
106
125
|
* See `docs/state-assembly-receipts.md` in the server repository.
|
|
126
|
+
*
|
|
127
|
+
* The `mode` discriminator distinguishes:
|
|
128
|
+
* - `"retrieval"` — receipts emitted by `/v1/context` + `/v1/handoff`.
|
|
129
|
+
* - `"as_of_replay"` — receipts emitted by `POST /v1/receipts/{id}/replay`
|
|
130
|
+
* (v0.9+); the `parentReceiptId` points at the source receipt.
|
|
107
131
|
*/
|
|
108
132
|
export interface Receipt {
|
|
109
133
|
receiptId: string;
|
|
110
134
|
parentReceiptId: string | null;
|
|
111
|
-
mode: "retrieval" | string;
|
|
135
|
+
mode: "retrieval" | "as_of_replay" | string;
|
|
112
136
|
queryId: string | null;
|
|
113
137
|
taskId: string | null;
|
|
114
138
|
tenantId: string | null;
|
|
@@ -119,8 +143,24 @@ export interface Receipt {
|
|
|
119
143
|
selectedEntries: ReceiptSelectedEntry[];
|
|
120
144
|
policy: ReceiptPolicy;
|
|
121
145
|
output: ReceiptOutput;
|
|
146
|
+
/** Server region the receipt was emitted from (v0.9+ residency).
|
|
147
|
+
* `null` in single-region deployments. */
|
|
122
148
|
region: string | null;
|
|
149
|
+
/** HMAC-SHA256 hex digest over the canonical body (v0.9+ #157).
|
|
150
|
+
* `null` for pre-v0.9 receipts or tenants without signing
|
|
151
|
+
* configured — those verify cleanly as
|
|
152
|
+
* `{valid: null, reason: "no_signature"}`. */
|
|
123
153
|
receiptSignature: string | null;
|
|
154
|
+
/** Operator key id used to sign (v0.9+). `null`/`undefined` when unsigned. */
|
|
155
|
+
receiptSignatureKeyId?: string | null;
|
|
156
|
+
/** Algorithm + canonical-form version (e.g. "hmac-sha256-canonical-v1")
|
|
157
|
+
* (v0.9+). `null`/`undefined` when unsigned. */
|
|
158
|
+
receiptSignatureAlgorithm?: string | null;
|
|
159
|
+
/** Embedded policy bundle YAML + hash + capture timestamp (v0.9+ #159).
|
|
160
|
+
* See `PolicySnapshot`. `undefined` for pre-v0.9 receipts (the
|
|
161
|
+
* replay endpoint refuses those with
|
|
162
|
+
* `unreplayable.missing_policy_snapshot`). */
|
|
163
|
+
policySnapshot?: PolicySnapshot | null;
|
|
124
164
|
}
|
|
125
165
|
export interface ReceiptList {
|
|
126
166
|
receipts: Receipt[];
|
|
@@ -134,6 +174,75 @@ export interface ListReceiptsParams {
|
|
|
134
174
|
cursor?: string;
|
|
135
175
|
limit?: number;
|
|
136
176
|
}
|
|
177
|
+
/**
|
|
178
|
+
* Result of `GET /v1/receipts/{id}/verify` (v0.9+ #157).
|
|
179
|
+
*
|
|
180
|
+
* `valid` is the verdict:
|
|
181
|
+
* - `true` — HMAC matches the canonical body. `reason === "ok"`.
|
|
182
|
+
* - `false` — math checked, signature does not cover the body.
|
|
183
|
+
* `reason === "signature_mismatch"`.
|
|
184
|
+
* - `null` — verdict could not be determined. `reason` is one of:
|
|
185
|
+
* - `"no_signature"` — receipt is unsigned (pre-v0.9 or tenant
|
|
186
|
+
* didn't opt in).
|
|
187
|
+
* - `"key_unavailable"` — the `keyId` rotated out of operator
|
|
188
|
+
* config; receipt is no longer verifiable on this binary.
|
|
189
|
+
* - `"unsupported_algorithm"` — receipt signed under a canonical
|
|
190
|
+
* form / algorithm variant this binary doesn't implement.
|
|
191
|
+
*
|
|
192
|
+
* Comparison is constant-time on the server side; the signing key
|
|
193
|
+
* bytes never appear on the response.
|
|
194
|
+
*/
|
|
195
|
+
export interface ReceiptVerifyResult {
|
|
196
|
+
valid: boolean | null;
|
|
197
|
+
keyId: string | null;
|
|
198
|
+
algorithm: string | null;
|
|
199
|
+
reason: "ok" | "signature_mismatch" | "no_signature" | "key_unavailable" | "unsupported_algorithm" | string;
|
|
200
|
+
}
|
|
201
|
+
/**
|
|
202
|
+
* Structural diff envelope returned by `POST /v1/receipts/{id}/replay`.
|
|
203
|
+
* Entries are matched by their `memoryId` / `episodeId` so re-ranking
|
|
204
|
+
* the same entry is reported under `common`, not as add+remove.
|
|
205
|
+
*/
|
|
206
|
+
export interface ReceiptReplayDiff {
|
|
207
|
+
contextHash: {
|
|
208
|
+
original: string | null;
|
|
209
|
+
replay: string | null;
|
|
210
|
+
changed: boolean;
|
|
211
|
+
};
|
|
212
|
+
selectedEntries: {
|
|
213
|
+
added: ReceiptSelectedEntry[];
|
|
214
|
+
removed: ReceiptSelectedEntry[];
|
|
215
|
+
common: number;
|
|
216
|
+
};
|
|
217
|
+
filtersApplied: {
|
|
218
|
+
added: unknown[];
|
|
219
|
+
removed: unknown[];
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* Response from `POST /v1/receipts/{id}/replay` (v0.9+ #159).
|
|
224
|
+
*
|
|
225
|
+
* Semantic: current code + original policy. Replay re-runs the
|
|
226
|
+
* original retrieval against the *current* memory state but with
|
|
227
|
+
* the *original* policy bundle frozen on the receipt's
|
|
228
|
+
* `policySnapshot`. The original receipt is never modified;
|
|
229
|
+
* `replayReceiptId` points at a new `mode="as_of_replay"` receipt
|
|
230
|
+
* linked back to the source via `parentReceiptId`.
|
|
231
|
+
*
|
|
232
|
+
* `replayReceiptId` is `null` when the replay-receipt write itself
|
|
233
|
+
* failed (rare, fail-open path). The `diff` envelope is still
|
|
234
|
+
* authoritative in that case.
|
|
235
|
+
*/
|
|
236
|
+
export interface ReceiptReplayResult {
|
|
237
|
+
originalReceiptId: string;
|
|
238
|
+
replayReceiptId: string | null;
|
|
239
|
+
diff: ReceiptReplayDiff;
|
|
240
|
+
}
|
|
241
|
+
/** The set of refusal reasons the server returns from
|
|
242
|
+
* `POST /v1/receipts/{id}/replay` when a receipt cannot be replayed.
|
|
243
|
+
* Used by `StatewaveUnreplayableError.reason` so callers can switch
|
|
244
|
+
* on the structured value without parsing error code strings. */
|
|
245
|
+
export type UnreplayableReason = "missing_policy_snapshot" | "nested_replay" | "invalid_snapshot";
|
|
137
246
|
export interface Timeline {
|
|
138
247
|
subjectId: string;
|
|
139
248
|
episodes: Episode[];
|
|
@@ -166,6 +275,95 @@ export interface CompileJob {
|
|
|
166
275
|
memories?: Memory[];
|
|
167
276
|
error?: string;
|
|
168
277
|
}
|
|
278
|
+
/** Support health-state bucket. */
|
|
279
|
+
export type HealthState = "healthy" | "watch" | "at_risk";
|
|
280
|
+
/** Resolution lifecycle status. */
|
|
281
|
+
export type ResolutionStatus = "open" | "resolved" | "unresolved";
|
|
282
|
+
/** One explainable factor behind a customer health score. */
|
|
283
|
+
export interface HealthFactor {
|
|
284
|
+
/** Stable signal identifier, e.g. `sla_resolution_breaches`. */
|
|
285
|
+
signal: string;
|
|
286
|
+
/** Signed score contribution — a negative impact drags the score down. */
|
|
287
|
+
impact: number;
|
|
288
|
+
/** Human-readable explanation of the factor. */
|
|
289
|
+
detail: string;
|
|
290
|
+
}
|
|
291
|
+
/** Customer health score (0–100) with the factors that drove it. */
|
|
292
|
+
export interface Health {
|
|
293
|
+
subjectId: string;
|
|
294
|
+
score: number;
|
|
295
|
+
state: HealthState;
|
|
296
|
+
factors: HealthFactor[];
|
|
297
|
+
}
|
|
298
|
+
/** SLA metrics for a single support session. */
|
|
299
|
+
export interface SessionSLA {
|
|
300
|
+
sessionId: string;
|
|
301
|
+
/** `resolved` | `open`. */
|
|
302
|
+
status: string;
|
|
303
|
+
firstMessageAt: string | null;
|
|
304
|
+
firstResponseAt: string | null;
|
|
305
|
+
resolvedAt: string | null;
|
|
306
|
+
firstResponseSeconds: number | null;
|
|
307
|
+
resolutionSeconds: number | null;
|
|
308
|
+
openDurationSeconds: number | null;
|
|
309
|
+
firstResponseBreached: boolean;
|
|
310
|
+
resolutionBreached: boolean;
|
|
311
|
+
}
|
|
312
|
+
/** Aggregate SLA metrics for a subject across all of its sessions. */
|
|
313
|
+
export interface SLASummary {
|
|
314
|
+
subjectId: string;
|
|
315
|
+
totalSessions: number;
|
|
316
|
+
resolvedSessions: number;
|
|
317
|
+
openSessions: number;
|
|
318
|
+
avgFirstResponseSeconds: number | null;
|
|
319
|
+
avgResolutionSeconds: number | null;
|
|
320
|
+
firstResponseBreachCount: number;
|
|
321
|
+
resolutionBreachCount: number;
|
|
322
|
+
sessions: SessionSLA[];
|
|
323
|
+
}
|
|
324
|
+
/** A prior resolution surfaced inside a handoff brief. */
|
|
325
|
+
export interface ResolutionSummaryItem {
|
|
326
|
+
sessionId: string;
|
|
327
|
+
status: string;
|
|
328
|
+
summary: string | null;
|
|
329
|
+
resolvedAt: string | null;
|
|
330
|
+
}
|
|
331
|
+
/** Structured escalation brief — the handoff context pack. */
|
|
332
|
+
export interface Handoff {
|
|
333
|
+
subjectId: string;
|
|
334
|
+
sessionId: string;
|
|
335
|
+
reason: string;
|
|
336
|
+
generatedAt: string;
|
|
337
|
+
customerSummary: string;
|
|
338
|
+
activeIssue: string;
|
|
339
|
+
attemptedSteps: string[];
|
|
340
|
+
keyFacts: string[];
|
|
341
|
+
resolutionHistory: ResolutionSummaryItem[];
|
|
342
|
+
recentContext: string[];
|
|
343
|
+
healthScore: number | null;
|
|
344
|
+
healthState: HealthState | null;
|
|
345
|
+
healthFactors: HealthFactor[];
|
|
346
|
+
/** Pre-rendered markdown brief, ready for human or LLM consumption. */
|
|
347
|
+
handoffNotes: string;
|
|
348
|
+
tokenEstimate: number;
|
|
349
|
+
provenance: Record<string, unknown>;
|
|
350
|
+
/** ULID of the state-assembly receipt, when one was emitted. */
|
|
351
|
+
receiptId?: string | null;
|
|
352
|
+
/** True iff a receipt was successfully written for this call. */
|
|
353
|
+
receiptEmitted?: boolean;
|
|
354
|
+
}
|
|
355
|
+
/** Resolution tracking record for a support session. */
|
|
356
|
+
export interface Resolution {
|
|
357
|
+
id: string;
|
|
358
|
+
subjectId: string;
|
|
359
|
+
sessionId: string;
|
|
360
|
+
status: ResolutionStatus;
|
|
361
|
+
resolutionSummary: string | null;
|
|
362
|
+
resolvedAt: string | null;
|
|
363
|
+
metadata: Record<string, unknown>;
|
|
364
|
+
createdAt: string;
|
|
365
|
+
updatedAt: string;
|
|
366
|
+
}
|
|
169
367
|
export interface CreateEpisodeParams {
|
|
170
368
|
subjectId: string;
|
|
171
369
|
source: string;
|
|
@@ -213,6 +411,53 @@ export interface SetMemoryLabelsParams {
|
|
|
213
411
|
*/
|
|
214
412
|
sensitivityLabels: string[];
|
|
215
413
|
}
|
|
414
|
+
export interface GetSLAParams {
|
|
415
|
+
subjectId: string;
|
|
416
|
+
/** First-response SLA threshold in minutes (server default: 5). */
|
|
417
|
+
firstResponseThresholdMinutes?: number;
|
|
418
|
+
/** Resolution SLA threshold in hours (server default: 24). */
|
|
419
|
+
resolutionThresholdHours?: number;
|
|
420
|
+
}
|
|
421
|
+
export interface CreateHandoffParams {
|
|
422
|
+
subjectId: string;
|
|
423
|
+
/** Session being handed off. */
|
|
424
|
+
sessionId: string;
|
|
425
|
+
/** Why the handoff is happening (server default: "escalation"). */
|
|
426
|
+
reason?: string;
|
|
427
|
+
/** Token budget for the assembled brief. */
|
|
428
|
+
maxTokens?: number;
|
|
429
|
+
/**
|
|
430
|
+
* Opt in to emitting a state-assembly receipt for this call. The
|
|
431
|
+
* tenant config can also force emission on or off independently of
|
|
432
|
+
* this flag.
|
|
433
|
+
*/
|
|
434
|
+
emitReceipt?: boolean;
|
|
435
|
+
queryId?: string;
|
|
436
|
+
taskId?: string;
|
|
437
|
+
parentReceiptId?: string;
|
|
438
|
+
/**
|
|
439
|
+
* Caller identity consumed by the sensitivity-label policy layer
|
|
440
|
+
* (#50). When the tenant config sets `require_caller_identity: true`,
|
|
441
|
+
* both `callerId` and `callerType` are mandatory.
|
|
442
|
+
*/
|
|
443
|
+
callerId?: string;
|
|
444
|
+
callerType?: string;
|
|
445
|
+
}
|
|
446
|
+
export interface CreateResolutionParams {
|
|
447
|
+
subjectId: string;
|
|
448
|
+
sessionId: string;
|
|
449
|
+
/** Lifecycle status (server default: "open"). */
|
|
450
|
+
status?: ResolutionStatus;
|
|
451
|
+
/** Short human summary of how the session was resolved. */
|
|
452
|
+
resolutionSummary?: string;
|
|
453
|
+
/** Free-form caller-owned bag; inner keys round-trip verbatim. */
|
|
454
|
+
metadata?: Record<string, unknown>;
|
|
455
|
+
}
|
|
456
|
+
export interface ListResolutionsParams {
|
|
457
|
+
subjectId: string;
|
|
458
|
+
/** Filter to a single status. Omit to list every resolution. */
|
|
459
|
+
status?: ResolutionStatus;
|
|
460
|
+
}
|
|
216
461
|
export interface ClientOptions {
|
|
217
462
|
baseUrl?: string;
|
|
218
463
|
apiKey?: string;
|