@statewavedev/sdk 0.9.0 → 0.10.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/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
@@ -145,6 +151,59 @@ console.log(updated.sensitivityLabels); // → ["financial", "pii"]
145
151
 
146
152
  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
153
 
154
+ ## Support-agent endpoints
155
+
156
+ Statewave's support wedge — customer health scoring, SLA tracking, resolution state, and structured escalation briefs — is exposed through ergonomic SDK methods (server v0.6+).
157
+
158
+ ```typescript
159
+ import { StatewaveClient } from "@statewavedev/sdk";
160
+
161
+ const sw = new StatewaveClient("http://localhost:8100");
162
+
163
+ // Customer health score (0–100) with the explainable factors behind it.
164
+ const health = await sw.getHealth("customer:globex");
165
+ console.log(`${health.score}/100 — ${health.state}`);
166
+ for (const f of health.factors) {
167
+ console.log(` ${f.signal}: ${f.impact >= 0 ? "+" : ""}${f.impact} (${f.detail})`);
168
+ }
169
+
170
+ // SLA metrics — first-response / resolution times and breach counts.
171
+ // Thresholds are optional; they default server-side to 5 min / 24 h.
172
+ const sla = await sw.getSLA({
173
+ subjectId: "customer:globex",
174
+ firstResponseThresholdMinutes: 10,
175
+ resolutionThresholdHours: 48,
176
+ });
177
+ console.log(`${sla.resolvedSessions}/${sla.totalSessions} resolved, ${sla.resolutionBreachCount} SLA breaches`);
178
+
179
+ // Track resolution state for a session (upserts by subject + session).
180
+ await sw.createResolution({
181
+ subjectId: "customer:globex",
182
+ sessionId: "ticket-8842",
183
+ status: "resolved",
184
+ resolutionSummary: "Issued refund for the duplicate charge",
185
+ });
186
+
187
+ // List resolutions, optionally filtered by status.
188
+ const openItems = await sw.listResolutions({
189
+ subjectId: "customer:globex",
190
+ status: "open",
191
+ });
192
+
193
+ // Generate a handoff context pack for escalation or shift change.
194
+ // `handoffNotes` is a pre-rendered markdown brief for human or LLM use.
195
+ const handoff = await sw.createHandoff({
196
+ subjectId: "customer:globex",
197
+ sessionId: "ticket-8842",
198
+ reason: "escalation",
199
+ callerId: "agent-7",
200
+ callerType: "support_agent",
201
+ });
202
+ console.log(handoff.handoffNotes);
203
+ ```
204
+
205
+ `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.
206
+
148
207
  ## Error handling
149
208
 
150
209
  ```typescript
@@ -189,8 +248,13 @@ All response types are fully typed:
189
248
  - `ListSubjectsResult` — paginated subject listing
190
249
  - `Receipt` + `ReceiptSelectedEntry` + `ReceiptPolicy` + `ReceiptOutput` — state-assembly audit artifact (v0.8) and its nested shapes
191
250
  - `ReceiptList` — cursor-paginated receipt listing
251
+ - `Health` + `HealthFactor` — customer health score and its explainable factors
252
+ - `SLASummary` + `SessionSLA` — SLA metrics, aggregate and per-session
253
+ - `Handoff` + `ResolutionSummaryItem` — handoff context pack and its prior-resolution items
254
+ - `Resolution` — resolution tracking record
255
+ - `HealthState` / `ResolutionStatus` — string-literal status unions
192
256
 
193
- Param types: `CreateEpisodeParams`, `SearchMemoriesParams`, `GetContextParams`, `ListReceiptsParams`, `SetMemoryLabelsParams`
257
+ Param types: `CreateEpisodeParams`, `SearchMemoriesParams`, `GetContextParams`, `ListReceiptsParams`, `SetMemoryLabelsParams`, `GetSLAParams`, `CreateHandoffParams`, `CreateResolutionParams`, `ListResolutionsParams`
194
258
 
195
259
  ## Running tests
196
260
 
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, Resolution, SLASummary, SearchMemoriesParams, SearchResult, SetMemoryLabelsParams, Timeline } from "./types.js";
2
2
  /** Structured error from the Statewave API. */
3
3
  export declare class StatewaveAPIError extends Error {
4
4
  readonly statusCode: number;
@@ -47,6 +47,35 @@ export declare class StatewaveClient {
47
47
  * to fetch the next page.
48
48
  */
49
49
  listReceipts(params: ListReceiptsParams): Promise<ReceiptList>;
50
+ /**
51
+ * Compute the customer health score (0–100) for a subject, with the
52
+ * explainable factors that drove it. Backs proactive risk triage.
53
+ */
54
+ getHealth(subjectId: string): Promise<Health>;
55
+ /**
56
+ * Compute SLA metrics for a subject — first-response and resolution
57
+ * times plus breach flags, aggregated across the subject's sessions.
58
+ * Both thresholds fall back to the server defaults (5 minutes /
59
+ * 24 hours) when omitted.
60
+ */
61
+ getSLA(params: GetSLAParams): Promise<SLASummary>;
62
+ /**
63
+ * Generate a handoff context pack — a structured escalation brief for
64
+ * shift change or agent transfer. Same caller-identity gate as
65
+ * `getContext`: when the tenant sets `require_caller_identity: true`,
66
+ * both `callerId` and `callerType` are mandatory.
67
+ */
68
+ createHandoff(params: CreateHandoffParams): Promise<Handoff>;
69
+ /**
70
+ * Create or update a resolution record for a support session.
71
+ * Upserts by `subjectId` + `sessionId`.
72
+ */
73
+ createResolution(params: CreateResolutionParams): Promise<Resolution>;
74
+ /**
75
+ * List resolution records for a subject, optionally filtered to a
76
+ * single status.
77
+ */
78
+ listResolutions(params: ListResolutionsParams): Promise<Resolution[]>;
50
79
  getTimeline(subjectId: string): Promise<Timeline>;
51
80
  deleteSubject(subjectId: string): Promise<DeleteResult>;
52
81
  listSubjects(params?: {
package/dist/client.js CHANGED
@@ -195,6 +195,78 @@ export class StatewaveClient {
195
195
  qs.set("limit", String(params.limit));
196
196
  return this.get(`/v1/receipts?${qs}`);
197
197
  }
198
+ // -- Support: health, SLA, handoff, resolutions ----------------------
199
+ /**
200
+ * Compute the customer health score (0–100) for a subject, with the
201
+ * explainable factors that drove it. Backs proactive risk triage.
202
+ */
203
+ async getHealth(subjectId) {
204
+ return this.get(`/v1/subjects/${encodeURIComponent(subjectId)}/health`);
205
+ }
206
+ /**
207
+ * Compute SLA metrics for a subject — first-response and resolution
208
+ * times plus breach flags, aggregated across the subject's sessions.
209
+ * Both thresholds fall back to the server defaults (5 minutes /
210
+ * 24 hours) when omitted.
211
+ */
212
+ async getSLA(params) {
213
+ const qs = new URLSearchParams();
214
+ if (params.firstResponseThresholdMinutes !== undefined) {
215
+ qs.set("first_response_threshold_minutes", String(params.firstResponseThresholdMinutes));
216
+ }
217
+ if (params.resolutionThresholdHours !== undefined) {
218
+ qs.set("resolution_threshold_hours", String(params.resolutionThresholdHours));
219
+ }
220
+ const query = qs.toString();
221
+ return this.get(`/v1/subjects/${encodeURIComponent(params.subjectId)}/sla${query ? `?${query}` : ""}`);
222
+ }
223
+ /**
224
+ * Generate a handoff context pack — a structured escalation brief for
225
+ * shift change or agent transfer. Same caller-identity gate as
226
+ * `getContext`: when the tenant sets `require_caller_identity: true`,
227
+ * both `callerId` and `callerType` are mandatory.
228
+ */
229
+ async createHandoff(params) {
230
+ return this.post("/v1/handoff", {
231
+ subjectId: params.subjectId,
232
+ sessionId: params.sessionId,
233
+ ...(params.reason !== undefined && { reason: params.reason }),
234
+ ...(params.maxTokens !== undefined && { maxTokens: params.maxTokens }),
235
+ ...(params.emitReceipt !== undefined && { emitReceipt: params.emitReceipt }),
236
+ ...(params.queryId !== undefined && { queryId: params.queryId }),
237
+ ...(params.taskId !== undefined && { taskId: params.taskId }),
238
+ ...(params.parentReceiptId !== undefined && {
239
+ parentReceiptId: params.parentReceiptId,
240
+ }),
241
+ ...(params.callerId !== undefined && { callerId: params.callerId }),
242
+ ...(params.callerType !== undefined && { callerType: params.callerType }),
243
+ });
244
+ }
245
+ /**
246
+ * Create or update a resolution record for a support session.
247
+ * Upserts by `subjectId` + `sessionId`.
248
+ */
249
+ async createResolution(params) {
250
+ return this.post("/v1/resolutions", {
251
+ subjectId: params.subjectId,
252
+ sessionId: params.sessionId,
253
+ ...(params.status !== undefined && { status: params.status }),
254
+ ...(params.resolutionSummary !== undefined && {
255
+ resolutionSummary: params.resolutionSummary,
256
+ }),
257
+ ...(params.metadata !== undefined && { metadata: params.metadata }),
258
+ });
259
+ }
260
+ /**
261
+ * List resolution records for a subject, optionally filtered to a
262
+ * single status.
263
+ */
264
+ async listResolutions(params) {
265
+ const qs = new URLSearchParams({ subject_id: params.subjectId });
266
+ if (params.status !== undefined)
267
+ qs.set("status", params.status);
268
+ return this.get(`/v1/resolutions?${qs}`);
269
+ }
198
270
  async getTimeline(subjectId) {
199
271
  return this.get(`/v1/timeline?subject_id=${encodeURIComponent(subjectId)}`);
200
272
  }
package/dist/types.d.ts CHANGED
@@ -166,6 +166,95 @@ export interface CompileJob {
166
166
  memories?: Memory[];
167
167
  error?: string;
168
168
  }
169
+ /** Support health-state bucket. */
170
+ export type HealthState = "healthy" | "watch" | "at_risk";
171
+ /** Resolution lifecycle status. */
172
+ export type ResolutionStatus = "open" | "resolved" | "unresolved";
173
+ /** One explainable factor behind a customer health score. */
174
+ export interface HealthFactor {
175
+ /** Stable signal identifier, e.g. `sla_resolution_breaches`. */
176
+ signal: string;
177
+ /** Signed score contribution — a negative impact drags the score down. */
178
+ impact: number;
179
+ /** Human-readable explanation of the factor. */
180
+ detail: string;
181
+ }
182
+ /** Customer health score (0–100) with the factors that drove it. */
183
+ export interface Health {
184
+ subjectId: string;
185
+ score: number;
186
+ state: HealthState;
187
+ factors: HealthFactor[];
188
+ }
189
+ /** SLA metrics for a single support session. */
190
+ export interface SessionSLA {
191
+ sessionId: string;
192
+ /** `resolved` | `open`. */
193
+ status: string;
194
+ firstMessageAt: string | null;
195
+ firstResponseAt: string | null;
196
+ resolvedAt: string | null;
197
+ firstResponseSeconds: number | null;
198
+ resolutionSeconds: number | null;
199
+ openDurationSeconds: number | null;
200
+ firstResponseBreached: boolean;
201
+ resolutionBreached: boolean;
202
+ }
203
+ /** Aggregate SLA metrics for a subject across all of its sessions. */
204
+ export interface SLASummary {
205
+ subjectId: string;
206
+ totalSessions: number;
207
+ resolvedSessions: number;
208
+ openSessions: number;
209
+ avgFirstResponseSeconds: number | null;
210
+ avgResolutionSeconds: number | null;
211
+ firstResponseBreachCount: number;
212
+ resolutionBreachCount: number;
213
+ sessions: SessionSLA[];
214
+ }
215
+ /** A prior resolution surfaced inside a handoff brief. */
216
+ export interface ResolutionSummaryItem {
217
+ sessionId: string;
218
+ status: string;
219
+ summary: string | null;
220
+ resolvedAt: string | null;
221
+ }
222
+ /** Structured escalation brief — the handoff context pack. */
223
+ export interface Handoff {
224
+ subjectId: string;
225
+ sessionId: string;
226
+ reason: string;
227
+ generatedAt: string;
228
+ customerSummary: string;
229
+ activeIssue: string;
230
+ attemptedSteps: string[];
231
+ keyFacts: string[];
232
+ resolutionHistory: ResolutionSummaryItem[];
233
+ recentContext: string[];
234
+ healthScore: number | null;
235
+ healthState: HealthState | null;
236
+ healthFactors: HealthFactor[];
237
+ /** Pre-rendered markdown brief, ready for human or LLM consumption. */
238
+ handoffNotes: string;
239
+ tokenEstimate: number;
240
+ provenance: Record<string, unknown>;
241
+ /** ULID of the state-assembly receipt, when one was emitted. */
242
+ receiptId?: string | null;
243
+ /** True iff a receipt was successfully written for this call. */
244
+ receiptEmitted?: boolean;
245
+ }
246
+ /** Resolution tracking record for a support session. */
247
+ export interface Resolution {
248
+ id: string;
249
+ subjectId: string;
250
+ sessionId: string;
251
+ status: ResolutionStatus;
252
+ resolutionSummary: string | null;
253
+ resolvedAt: string | null;
254
+ metadata: Record<string, unknown>;
255
+ createdAt: string;
256
+ updatedAt: string;
257
+ }
169
258
  export interface CreateEpisodeParams {
170
259
  subjectId: string;
171
260
  source: string;
@@ -213,6 +302,53 @@ export interface SetMemoryLabelsParams {
213
302
  */
214
303
  sensitivityLabels: string[];
215
304
  }
305
+ export interface GetSLAParams {
306
+ subjectId: string;
307
+ /** First-response SLA threshold in minutes (server default: 5). */
308
+ firstResponseThresholdMinutes?: number;
309
+ /** Resolution SLA threshold in hours (server default: 24). */
310
+ resolutionThresholdHours?: number;
311
+ }
312
+ export interface CreateHandoffParams {
313
+ subjectId: string;
314
+ /** Session being handed off. */
315
+ sessionId: string;
316
+ /** Why the handoff is happening (server default: "escalation"). */
317
+ reason?: string;
318
+ /** Token budget for the assembled brief. */
319
+ maxTokens?: number;
320
+ /**
321
+ * Opt in to emitting a state-assembly receipt for this call. The
322
+ * tenant config can also force emission on or off independently of
323
+ * this flag.
324
+ */
325
+ emitReceipt?: boolean;
326
+ queryId?: string;
327
+ taskId?: string;
328
+ parentReceiptId?: string;
329
+ /**
330
+ * Caller identity consumed by the sensitivity-label policy layer
331
+ * (#50). When the tenant config sets `require_caller_identity: true`,
332
+ * both `callerId` and `callerType` are mandatory.
333
+ */
334
+ callerId?: string;
335
+ callerType?: string;
336
+ }
337
+ export interface CreateResolutionParams {
338
+ subjectId: string;
339
+ sessionId: string;
340
+ /** Lifecycle status (server default: "open"). */
341
+ status?: ResolutionStatus;
342
+ /** Short human summary of how the session was resolved. */
343
+ resolutionSummary?: string;
344
+ /** Free-form caller-owned bag; inner keys round-trip verbatim. */
345
+ metadata?: Record<string, unknown>;
346
+ }
347
+ export interface ListResolutionsParams {
348
+ subjectId: string;
349
+ /** Filter to a single status. Omit to list every resolution. */
350
+ status?: ResolutionStatus;
351
+ }
216
352
  export interface ClientOptions {
217
353
  baseUrl?: string;
218
354
  apiKey?: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@statewavedev/sdk",
3
- "version": "0.9.0",
3
+ "version": "0.10.0",
4
4
  "description": "Official TypeScript SDK for Statewave — the open-source memory runtime for AI agents.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",