@pliuz/sdk 0.1.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/CHANGELOG.md +46 -0
- package/LICENSE +201 -0
- package/README.md +332 -0
- package/dist/adapters/ai.cjs +578 -0
- package/dist/adapters/ai.cjs.map +1 -0
- package/dist/adapters/ai.d.cts +74 -0
- package/dist/adapters/ai.d.ts +74 -0
- package/dist/adapters/ai.js +575 -0
- package/dist/adapters/ai.js.map +1 -0
- package/dist/client-BABvN_88.d.cts +153 -0
- package/dist/client-BABvN_88.d.ts +153 -0
- package/dist/index.cjs +561 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +179 -0
- package/dist/index.d.ts +179 -0
- package/dist/index.js +532 -0
- package/dist/index.js.map +1 -0
- package/examples/basic.ts +70 -0
- package/examples/gated-basic.ts +62 -0
- package/examples/vercel-ai-agent.ts +65 -0
- package/package.json +91 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,532 @@
|
|
|
1
|
+
import { createHash } from 'crypto';
|
|
2
|
+
|
|
3
|
+
// src/version.ts
|
|
4
|
+
var VERSION = "0.1.0";
|
|
5
|
+
|
|
6
|
+
// src/errors.ts
|
|
7
|
+
var PliuzError = class extends Error {
|
|
8
|
+
constructor(message) {
|
|
9
|
+
super(message);
|
|
10
|
+
this.name = "PliuzError";
|
|
11
|
+
Object.setPrototypeOf(this, new.target.prototype);
|
|
12
|
+
}
|
|
13
|
+
};
|
|
14
|
+
var PliuzNetworkError = class extends PliuzError {
|
|
15
|
+
constructor(message, cause) {
|
|
16
|
+
super(message);
|
|
17
|
+
this.cause = cause;
|
|
18
|
+
this.name = "PliuzNetworkError";
|
|
19
|
+
Object.setPrototypeOf(this, new.target.prototype);
|
|
20
|
+
}
|
|
21
|
+
cause;
|
|
22
|
+
};
|
|
23
|
+
var PliuzTimeoutError = class extends PliuzError {
|
|
24
|
+
constructor(message) {
|
|
25
|
+
super(message);
|
|
26
|
+
this.name = "PliuzTimeoutError";
|
|
27
|
+
Object.setPrototypeOf(this, new.target.prototype);
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
var PliuzApiError = class extends PliuzError {
|
|
31
|
+
constructor(statusCode, errorCode, message, details) {
|
|
32
|
+
super(message);
|
|
33
|
+
this.statusCode = statusCode;
|
|
34
|
+
this.errorCode = errorCode;
|
|
35
|
+
this.details = details;
|
|
36
|
+
this.name = "PliuzApiError";
|
|
37
|
+
Object.setPrototypeOf(this, new.target.prototype);
|
|
38
|
+
}
|
|
39
|
+
statusCode;
|
|
40
|
+
errorCode;
|
|
41
|
+
details;
|
|
42
|
+
};
|
|
43
|
+
var PliuzValidationError = class extends PliuzApiError {
|
|
44
|
+
constructor(statusCode, errorCode, message, details) {
|
|
45
|
+
super(statusCode, errorCode, message, details);
|
|
46
|
+
this.name = "PliuzValidationError";
|
|
47
|
+
Object.setPrototypeOf(this, new.target.prototype);
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
var PliuzAuthError = class extends PliuzApiError {
|
|
51
|
+
constructor(statusCode, errorCode, message, details) {
|
|
52
|
+
super(statusCode, errorCode, message, details);
|
|
53
|
+
this.name = "PliuzAuthError";
|
|
54
|
+
Object.setPrototypeOf(this, new.target.prototype);
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
var PliuzForbiddenError = class extends PliuzApiError {
|
|
58
|
+
constructor(statusCode, errorCode, message, details) {
|
|
59
|
+
super(statusCode, errorCode, message, details);
|
|
60
|
+
this.name = "PliuzForbiddenError";
|
|
61
|
+
Object.setPrototypeOf(this, new.target.prototype);
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
var PliuzNotFoundError = class extends PliuzApiError {
|
|
65
|
+
constructor(statusCode, errorCode, message, details) {
|
|
66
|
+
super(statusCode, errorCode, message, details);
|
|
67
|
+
this.name = "PliuzNotFoundError";
|
|
68
|
+
Object.setPrototypeOf(this, new.target.prototype);
|
|
69
|
+
}
|
|
70
|
+
};
|
|
71
|
+
var PliuzConflictError = class extends PliuzApiError {
|
|
72
|
+
constructor(statusCode, errorCode, message, details) {
|
|
73
|
+
super(statusCode, errorCode, message, details);
|
|
74
|
+
this.name = "PliuzConflictError";
|
|
75
|
+
Object.setPrototypeOf(this, new.target.prototype);
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
var PliuzPolicyError = class extends PliuzApiError {
|
|
79
|
+
constructor(statusCode, errorCode, message, details) {
|
|
80
|
+
super(statusCode, errorCode, message, details);
|
|
81
|
+
this.name = "PliuzPolicyError";
|
|
82
|
+
Object.setPrototypeOf(this, new.target.prototype);
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
var PliuzRateLimitError = class extends PliuzApiError {
|
|
86
|
+
constructor(statusCode, errorCode, message, details) {
|
|
87
|
+
super(statusCode, errorCode, message, details);
|
|
88
|
+
this.name = "PliuzRateLimitError";
|
|
89
|
+
Object.setPrototypeOf(this, new.target.prototype);
|
|
90
|
+
}
|
|
91
|
+
};
|
|
92
|
+
var PliuzServerError = class extends PliuzApiError {
|
|
93
|
+
constructor(statusCode, errorCode, message, details) {
|
|
94
|
+
super(statusCode, errorCode, message, details);
|
|
95
|
+
this.name = "PliuzServerError";
|
|
96
|
+
Object.setPrototypeOf(this, new.target.prototype);
|
|
97
|
+
}
|
|
98
|
+
};
|
|
99
|
+
var PliuzRejectedError = class extends PliuzError {
|
|
100
|
+
constructor(approvalId, reason) {
|
|
101
|
+
const msg = reason ? `approval ${approvalId} was rejected: ${reason}` : `approval ${approvalId} was rejected`;
|
|
102
|
+
super(msg);
|
|
103
|
+
this.approvalId = approvalId;
|
|
104
|
+
this.reason = reason;
|
|
105
|
+
this.name = "PliuzRejectedError";
|
|
106
|
+
Object.setPrototypeOf(this, new.target.prototype);
|
|
107
|
+
}
|
|
108
|
+
approvalId;
|
|
109
|
+
reason;
|
|
110
|
+
};
|
|
111
|
+
var PliuzApprovalExpiredError = class extends PliuzError {
|
|
112
|
+
constructor(approvalId) {
|
|
113
|
+
super(`approval ${approvalId} expired before a human decided`);
|
|
114
|
+
this.approvalId = approvalId;
|
|
115
|
+
this.name = "PliuzApprovalExpiredError";
|
|
116
|
+
Object.setPrototypeOf(this, new.target.prototype);
|
|
117
|
+
}
|
|
118
|
+
approvalId;
|
|
119
|
+
};
|
|
120
|
+
var PliuzApprovalTimeoutError = class extends PliuzError {
|
|
121
|
+
constructor(approvalId, timeoutMs) {
|
|
122
|
+
super(
|
|
123
|
+
`approval ${approvalId} did not resolve within ${timeoutMs}ms (SDK polling timeout \u2014 approval may still be pending server-side)`
|
|
124
|
+
);
|
|
125
|
+
this.approvalId = approvalId;
|
|
126
|
+
this.timeoutMs = timeoutMs;
|
|
127
|
+
this.name = "PliuzApprovalTimeoutError";
|
|
128
|
+
Object.setPrototypeOf(this, new.target.prototype);
|
|
129
|
+
}
|
|
130
|
+
approvalId;
|
|
131
|
+
timeoutMs;
|
|
132
|
+
};
|
|
133
|
+
var STATUS_TO_ERROR = {
|
|
134
|
+
400: PliuzValidationError,
|
|
135
|
+
401: PliuzAuthError,
|
|
136
|
+
403: PliuzForbiddenError,
|
|
137
|
+
404: PliuzNotFoundError,
|
|
138
|
+
409: PliuzConflictError,
|
|
139
|
+
422: PliuzPolicyError,
|
|
140
|
+
429: PliuzRateLimitError
|
|
141
|
+
};
|
|
142
|
+
function errorForStatus(statusCode, errorCode, message, details) {
|
|
143
|
+
if (statusCode >= 500 && statusCode < 600) {
|
|
144
|
+
return new PliuzServerError(statusCode, errorCode, message, details);
|
|
145
|
+
}
|
|
146
|
+
const Cls = STATUS_TO_ERROR[statusCode] ?? PliuzApiError;
|
|
147
|
+
return new Cls(statusCode, errorCode, message, details);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// src/_http.ts
|
|
151
|
+
var IDEMPOTENT_METHODS = /* @__PURE__ */ new Set(["GET", "HEAD", "OPTIONS", "PUT", "DELETE"]);
|
|
152
|
+
var RETRYABLE_STATUS = /* @__PURE__ */ new Set([408, 429, 500, 502, 503, 504]);
|
|
153
|
+
function userAgent() {
|
|
154
|
+
return `pliuz-typescript/${VERSION}`;
|
|
155
|
+
}
|
|
156
|
+
function buildHeaders(apiKey) {
|
|
157
|
+
return {
|
|
158
|
+
"X-Pliuz-Api-Key": apiKey,
|
|
159
|
+
"User-Agent": userAgent(),
|
|
160
|
+
Accept: "application/json",
|
|
161
|
+
"Content-Type": "application/json"
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
function backoffMs(attempt, baseMs = 500, capMs = 8e3) {
|
|
165
|
+
const expo = Math.min(capMs, baseMs * 2 ** (attempt - 1));
|
|
166
|
+
return Math.floor(Math.random() * expo);
|
|
167
|
+
}
|
|
168
|
+
function isRetryable(method, status, retryOnPost) {
|
|
169
|
+
if (!RETRYABLE_STATUS.has(status)) return false;
|
|
170
|
+
if (IDEMPOTENT_METHODS.has(method)) return true;
|
|
171
|
+
if (method === "POST" && retryOnPost) return true;
|
|
172
|
+
return false;
|
|
173
|
+
}
|
|
174
|
+
async function parseError(response) {
|
|
175
|
+
let body = {};
|
|
176
|
+
try {
|
|
177
|
+
body = await response.json();
|
|
178
|
+
} catch {
|
|
179
|
+
}
|
|
180
|
+
const errorCode = body.error ?? "unknown_error";
|
|
181
|
+
const message = body.message ?? response.statusText ?? "Pliuz API error";
|
|
182
|
+
const details = body.details;
|
|
183
|
+
return errorForStatus(response.status, errorCode, message, details);
|
|
184
|
+
}
|
|
185
|
+
function sleep(ms) {
|
|
186
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
187
|
+
}
|
|
188
|
+
async function request(opts) {
|
|
189
|
+
const { method, url, apiKey, body, timeoutMs, maxRetries, retryOnPost = false, fetchImpl } = opts;
|
|
190
|
+
const headers = buildHeaders(apiKey);
|
|
191
|
+
let lastError = null;
|
|
192
|
+
for (let attempt = 1; attempt <= maxRetries + 1; attempt++) {
|
|
193
|
+
const controller = new AbortController();
|
|
194
|
+
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
195
|
+
let response;
|
|
196
|
+
try {
|
|
197
|
+
response = await fetchImpl(url, {
|
|
198
|
+
method,
|
|
199
|
+
headers,
|
|
200
|
+
body: body == null ? void 0 : JSON.stringify(body),
|
|
201
|
+
signal: controller.signal
|
|
202
|
+
});
|
|
203
|
+
} catch (e) {
|
|
204
|
+
clearTimeout(timer);
|
|
205
|
+
const isTimeout = e instanceof Error && (e.name === "AbortError" || /abort|timeout/i.test(e.message));
|
|
206
|
+
lastError = isTimeout ? new PliuzTimeoutError(`Pliuz request timed out after ${timeoutMs}ms`) : new PliuzNetworkError(
|
|
207
|
+
`Pliuz network error: ${e instanceof Error ? e.message : String(e)}`,
|
|
208
|
+
e
|
|
209
|
+
);
|
|
210
|
+
if (attempt > maxRetries) throw lastError;
|
|
211
|
+
await sleep(backoffMs(attempt));
|
|
212
|
+
continue;
|
|
213
|
+
}
|
|
214
|
+
clearTimeout(timer);
|
|
215
|
+
if (response.status >= 200 && response.status < 300) {
|
|
216
|
+
try {
|
|
217
|
+
return await response.json();
|
|
218
|
+
} catch (e) {
|
|
219
|
+
throw new PliuzApiError(
|
|
220
|
+
response.status,
|
|
221
|
+
"invalid_json",
|
|
222
|
+
`Pliuz returned non-JSON 2xx body: ${e instanceof Error ? e.message : String(e)}`
|
|
223
|
+
);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
if (isRetryable(method, response.status, retryOnPost)) {
|
|
227
|
+
if (attempt > maxRetries) {
|
|
228
|
+
throw await parseError(response);
|
|
229
|
+
}
|
|
230
|
+
await sleep(backoffMs(attempt));
|
|
231
|
+
continue;
|
|
232
|
+
}
|
|
233
|
+
throw await parseError(response);
|
|
234
|
+
}
|
|
235
|
+
throw lastError ?? new PliuzApiError(0, "exhausted", "retry loop exhausted unexpectedly");
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
// src/client.ts
|
|
239
|
+
var DEFAULT_BASE_URL = "https://pliuz-dev.vercel.app";
|
|
240
|
+
var DEFAULT_TIMEOUT_MS = 3e4;
|
|
241
|
+
var DEFAULT_MAX_RETRIES = 3;
|
|
242
|
+
var ENV_API_KEY = "PLIUZ_API_KEY";
|
|
243
|
+
var ENV_BASE_URL = "PLIUZ_BASE_URL";
|
|
244
|
+
function resolveApiKey(explicit) {
|
|
245
|
+
if (explicit) return explicit;
|
|
246
|
+
if (typeof process !== "undefined" && process.env?.[ENV_API_KEY]) {
|
|
247
|
+
return process.env[ENV_API_KEY];
|
|
248
|
+
}
|
|
249
|
+
throw new Error(`Pliuz API key not provided. Pass apiKey or set $${ENV_API_KEY}.`);
|
|
250
|
+
}
|
|
251
|
+
function resolveBaseUrl(explicit) {
|
|
252
|
+
if (explicit) return explicit.replace(/\/+$/, "");
|
|
253
|
+
if (typeof process !== "undefined" && process.env?.[ENV_BASE_URL]) {
|
|
254
|
+
return process.env[ENV_BASE_URL].replace(/\/+$/, "");
|
|
255
|
+
}
|
|
256
|
+
return DEFAULT_BASE_URL;
|
|
257
|
+
}
|
|
258
|
+
function approvalUrl(baseUrl, approvalId, suffix) {
|
|
259
|
+
let path = "/api/v1/approvals";
|
|
260
|
+
if (approvalId) path += `/${encodeURIComponent(approvalId)}`;
|
|
261
|
+
if (suffix) path += `/${suffix}`;
|
|
262
|
+
return baseUrl + path;
|
|
263
|
+
}
|
|
264
|
+
var PliuzClient = class {
|
|
265
|
+
apiKey;
|
|
266
|
+
baseUrl;
|
|
267
|
+
timeoutMs;
|
|
268
|
+
maxRetries;
|
|
269
|
+
fetchImpl;
|
|
270
|
+
constructor(options = {}) {
|
|
271
|
+
this.apiKey = resolveApiKey(options.apiKey);
|
|
272
|
+
this.baseUrl = resolveBaseUrl(options.baseUrl);
|
|
273
|
+
this.timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
274
|
+
this.maxRetries = options.maxRetries ?? DEFAULT_MAX_RETRIES;
|
|
275
|
+
const fetchImpl = options.fetch ?? globalThis.fetch;
|
|
276
|
+
if (!fetchImpl) {
|
|
277
|
+
throw new Error(
|
|
278
|
+
"No fetch implementation available. Pliuz requires Node \u226518 or a polyfill."
|
|
279
|
+
);
|
|
280
|
+
}
|
|
281
|
+
this.fetchImpl = fetchImpl;
|
|
282
|
+
}
|
|
283
|
+
/**
|
|
284
|
+
* Create (or idempotently replay) an approval request.
|
|
285
|
+
*
|
|
286
|
+
* With `idempotency_key`, the call retries on network failure without
|
|
287
|
+
* risk of duplicates (backend dedupes within 24h).
|
|
288
|
+
*/
|
|
289
|
+
async createApproval(input) {
|
|
290
|
+
const body = {
|
|
291
|
+
tool_name: input.tool_name,
|
|
292
|
+
tool_args: input.tool_args,
|
|
293
|
+
context_messages: input.context_messages ?? null,
|
|
294
|
+
originator: input.originator ?? { type: "system" },
|
|
295
|
+
session_id: input.session_id ?? null,
|
|
296
|
+
client_metadata: input.client_metadata ?? null,
|
|
297
|
+
idempotency_key: input.idempotency_key ?? null
|
|
298
|
+
};
|
|
299
|
+
return request({
|
|
300
|
+
method: "POST",
|
|
301
|
+
url: approvalUrl(this.baseUrl),
|
|
302
|
+
apiKey: this.apiKey,
|
|
303
|
+
body,
|
|
304
|
+
timeoutMs: this.timeoutMs,
|
|
305
|
+
maxRetries: this.maxRetries,
|
|
306
|
+
retryOnPost: Boolean(input.idempotency_key),
|
|
307
|
+
fetchImpl: this.fetchImpl
|
|
308
|
+
});
|
|
309
|
+
}
|
|
310
|
+
/**
|
|
311
|
+
* Fetch the current state of an approval request.
|
|
312
|
+
* Used by polling in `gated()` to detect status transitions.
|
|
313
|
+
*/
|
|
314
|
+
async getApproval(approvalId) {
|
|
315
|
+
return request({
|
|
316
|
+
method: "GET",
|
|
317
|
+
url: approvalUrl(this.baseUrl, approvalId),
|
|
318
|
+
apiKey: this.apiKey,
|
|
319
|
+
timeoutMs: this.timeoutMs,
|
|
320
|
+
maxRetries: this.maxRetries,
|
|
321
|
+
fetchImpl: this.fetchImpl
|
|
322
|
+
});
|
|
323
|
+
}
|
|
324
|
+
/**
|
|
325
|
+
* Close the audit loop by reporting the outcome of the gated action.
|
|
326
|
+
*
|
|
327
|
+
* Single-shot: a second call returns 409 (PliuzConflictError).
|
|
328
|
+
* Only the originating agent can report — others get 403 (PliuzForbiddenError).
|
|
329
|
+
*/
|
|
330
|
+
async reportExecution(approvalId, input) {
|
|
331
|
+
const body = {
|
|
332
|
+
status: input.status,
|
|
333
|
+
error: input.error ?? null,
|
|
334
|
+
latency_ms: input.latency_ms ?? null,
|
|
335
|
+
target_response_excerpt: input.target_response_excerpt ?? null
|
|
336
|
+
};
|
|
337
|
+
return request({
|
|
338
|
+
method: "POST",
|
|
339
|
+
url: approvalUrl(this.baseUrl, approvalId, "execution"),
|
|
340
|
+
apiKey: this.apiKey,
|
|
341
|
+
body,
|
|
342
|
+
timeoutMs: this.timeoutMs,
|
|
343
|
+
maxRetries: this.maxRetries,
|
|
344
|
+
retryOnPost: false,
|
|
345
|
+
fetchImpl: this.fetchImpl
|
|
346
|
+
});
|
|
347
|
+
}
|
|
348
|
+
};
|
|
349
|
+
|
|
350
|
+
// src/redaction.ts
|
|
351
|
+
var REDACTED_PLACEHOLDER = "<redacted>";
|
|
352
|
+
function applyRedaction(payload, paths) {
|
|
353
|
+
if (!paths || paths.length === 0) {
|
|
354
|
+
return deepClone(payload);
|
|
355
|
+
}
|
|
356
|
+
const result = deepClone(payload);
|
|
357
|
+
for (const path of paths) {
|
|
358
|
+
redactPath(result, parsePath(path));
|
|
359
|
+
}
|
|
360
|
+
return result;
|
|
361
|
+
}
|
|
362
|
+
function parsePath(path) {
|
|
363
|
+
if (!path) {
|
|
364
|
+
throw new Error("redaction path cannot be empty");
|
|
365
|
+
}
|
|
366
|
+
const segments = [];
|
|
367
|
+
for (const raw of path.split(".")) {
|
|
368
|
+
if (raw.endsWith("[*]")) {
|
|
369
|
+
const key = raw.slice(0, -3);
|
|
370
|
+
if (!key) {
|
|
371
|
+
throw new Error(`redaction path segment before [*] cannot be empty: ${path}`);
|
|
372
|
+
}
|
|
373
|
+
segments.push({ key, isArrayWildcard: true });
|
|
374
|
+
} else if (raw.includes("[") || raw.includes("]")) {
|
|
375
|
+
throw new Error(`unsupported redaction syntax in '${path}' \u2014 only [*] is allowed`);
|
|
376
|
+
} else {
|
|
377
|
+
if (!raw) {
|
|
378
|
+
throw new Error(`redaction path has empty segment: ${path}`);
|
|
379
|
+
}
|
|
380
|
+
segments.push({ key: raw, isArrayWildcard: false });
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
return segments;
|
|
384
|
+
}
|
|
385
|
+
function redactPath(node, segments) {
|
|
386
|
+
if (segments.length === 0) return;
|
|
387
|
+
if (!isPlainObject(node)) return;
|
|
388
|
+
const [head, ...rest] = segments;
|
|
389
|
+
if (!head) return;
|
|
390
|
+
const { key, isArrayWildcard } = head;
|
|
391
|
+
if (!(key in node)) return;
|
|
392
|
+
if (isArrayWildcard) {
|
|
393
|
+
const target = node[key];
|
|
394
|
+
if (!Array.isArray(target)) return;
|
|
395
|
+
if (rest.length === 0) {
|
|
396
|
+
node[key] = target.map(() => REDACTED_PLACEHOLDER);
|
|
397
|
+
return;
|
|
398
|
+
}
|
|
399
|
+
for (const item of target) {
|
|
400
|
+
redactPath(item, rest);
|
|
401
|
+
}
|
|
402
|
+
} else {
|
|
403
|
+
if (rest.length === 0) {
|
|
404
|
+
node[key] = REDACTED_PLACEHOLDER;
|
|
405
|
+
} else {
|
|
406
|
+
redactPath(node[key], rest);
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
function isPlainObject(v) {
|
|
411
|
+
return typeof v === "object" && v !== null && !Array.isArray(v);
|
|
412
|
+
}
|
|
413
|
+
function deepClone(v) {
|
|
414
|
+
if (typeof structuredClone === "function") {
|
|
415
|
+
return structuredClone(v);
|
|
416
|
+
}
|
|
417
|
+
return JSON.parse(JSON.stringify(v));
|
|
418
|
+
}
|
|
419
|
+
var DEFAULT_GATED_TIMEOUT_MS = 3e5;
|
|
420
|
+
var DEFAULT_GATED_POLL_INTERVAL_MS = 2e3;
|
|
421
|
+
var TERMINAL_OK = /* @__PURE__ */ new Set(["approved"]);
|
|
422
|
+
var TERMINAL_REJECT = /* @__PURE__ */ new Set(["rejected"]);
|
|
423
|
+
var TERMINAL_EXPIRED = /* @__PURE__ */ new Set(["expired"]);
|
|
424
|
+
function gated(options, fn) {
|
|
425
|
+
const toolName = options.toolName ?? (fn.name || "anonymous");
|
|
426
|
+
const toolArgsMapper = options.toolArgs ?? ((...args) => ({ args }));
|
|
427
|
+
const timeoutMs = options.timeoutMs ?? DEFAULT_GATED_TIMEOUT_MS;
|
|
428
|
+
const pollIntervalMs = options.pollIntervalMs ?? DEFAULT_GATED_POLL_INTERVAL_MS;
|
|
429
|
+
const redactPaths = options.redact;
|
|
430
|
+
return async (...args) => {
|
|
431
|
+
const activeClient = options.client ?? new PliuzClient();
|
|
432
|
+
const rawArgs = toolArgsMapper(...args);
|
|
433
|
+
const sendArgs = redactPaths && redactPaths.length > 0 ? applyRedaction(rawArgs, redactPaths) : rawArgs;
|
|
434
|
+
const idempotencyKey = idempotencyKeyFor(toolName, sendArgs, options.sessionId);
|
|
435
|
+
const metadata = {};
|
|
436
|
+
if (options.policy) metadata.policy = options.policy;
|
|
437
|
+
const response = await activeClient.createApproval({
|
|
438
|
+
tool_name: toolName,
|
|
439
|
+
tool_args: sendArgs,
|
|
440
|
+
context_messages: options.contextMessages ?? null,
|
|
441
|
+
originator: options.originator,
|
|
442
|
+
session_id: options.sessionId ?? null,
|
|
443
|
+
client_metadata: Object.keys(metadata).length > 0 ? metadata : null,
|
|
444
|
+
idempotency_key: idempotencyKey
|
|
445
|
+
});
|
|
446
|
+
const approvalId = response.id;
|
|
447
|
+
if (TERMINAL_REJECT.has(response.status)) {
|
|
448
|
+
throw new PliuzRejectedError(approvalId, response.message ?? null);
|
|
449
|
+
}
|
|
450
|
+
if (TERMINAL_EXPIRED.has(response.status)) {
|
|
451
|
+
throw new PliuzApprovalExpiredError(approvalId);
|
|
452
|
+
}
|
|
453
|
+
if (!TERMINAL_OK.has(response.status)) {
|
|
454
|
+
const deadline = Date.now() + timeoutMs;
|
|
455
|
+
while (true) {
|
|
456
|
+
if (Date.now() >= deadline) {
|
|
457
|
+
throw new PliuzApprovalTimeoutError(approvalId, timeoutMs);
|
|
458
|
+
}
|
|
459
|
+
await sleep2(pollIntervalMs);
|
|
460
|
+
const approval = await activeClient.getApproval(approvalId);
|
|
461
|
+
if (terminalOrThrow(approval)) break;
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
const started = Date.now();
|
|
465
|
+
try {
|
|
466
|
+
const result = await fn(...args);
|
|
467
|
+
const latencyMs = Date.now() - started;
|
|
468
|
+
void safeReport(activeClient, approvalId, "success", null, latencyMs, result);
|
|
469
|
+
return result;
|
|
470
|
+
} catch (e) {
|
|
471
|
+
const latencyMs = Date.now() - started;
|
|
472
|
+
const errMsg = e instanceof Error ? e.message : String(e);
|
|
473
|
+
void safeReport(activeClient, approvalId, "error", errMsg.slice(0, 2e3), latencyMs, null);
|
|
474
|
+
throw e;
|
|
475
|
+
}
|
|
476
|
+
};
|
|
477
|
+
}
|
|
478
|
+
function idempotencyKeyFor(toolName, toolArgs, sessionId) {
|
|
479
|
+
JSON.stringify(
|
|
480
|
+
{ tool: toolName, args: toolArgs, session: sessionId ?? null },
|
|
481
|
+
Object.keys({ tool: 0, args: 0, session: 0 }).sort()
|
|
482
|
+
);
|
|
483
|
+
const canonical = canonicalJSON({ tool: toolName, args: toolArgs, session: sessionId ?? null });
|
|
484
|
+
return "g_" + createHash("sha256").update(canonical).digest("hex").slice(0, 30);
|
|
485
|
+
}
|
|
486
|
+
function canonicalJSON(value) {
|
|
487
|
+
if (value === null || typeof value !== "object") {
|
|
488
|
+
return JSON.stringify(value);
|
|
489
|
+
}
|
|
490
|
+
if (Array.isArray(value)) {
|
|
491
|
+
return "[" + value.map(canonicalJSON).join(",") + "]";
|
|
492
|
+
}
|
|
493
|
+
const obj = value;
|
|
494
|
+
const keys = Object.keys(obj).sort();
|
|
495
|
+
return "{" + keys.map((k) => JSON.stringify(k) + ":" + canonicalJSON(obj[k])).join(",") + "}";
|
|
496
|
+
}
|
|
497
|
+
function terminalOrThrow(approval) {
|
|
498
|
+
if (TERMINAL_OK.has(approval.status)) return true;
|
|
499
|
+
if (TERMINAL_REJECT.has(approval.status)) {
|
|
500
|
+
throw new PliuzRejectedError(approval.id, approval.decision_reason);
|
|
501
|
+
}
|
|
502
|
+
if (TERMINAL_EXPIRED.has(approval.status)) {
|
|
503
|
+
throw new PliuzApprovalExpiredError(approval.id);
|
|
504
|
+
}
|
|
505
|
+
return false;
|
|
506
|
+
}
|
|
507
|
+
async function safeReport(client, approvalId, status, errorMessage, latencyMs, result) {
|
|
508
|
+
let excerpt = null;
|
|
509
|
+
if (result != null) {
|
|
510
|
+
try {
|
|
511
|
+
excerpt = JSON.stringify(result).slice(0, 2e3);
|
|
512
|
+
} catch {
|
|
513
|
+
excerpt = String(result).slice(0, 2e3);
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
try {
|
|
517
|
+
await client.reportExecution(approvalId, {
|
|
518
|
+
status,
|
|
519
|
+
error: errorMessage,
|
|
520
|
+
latency_ms: latencyMs,
|
|
521
|
+
target_response_excerpt: excerpt
|
|
522
|
+
});
|
|
523
|
+
} catch {
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
function sleep2(ms) {
|
|
527
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
export { DEFAULT_BASE_URL, DEFAULT_GATED_POLL_INTERVAL_MS, DEFAULT_GATED_TIMEOUT_MS, DEFAULT_MAX_RETRIES, DEFAULT_TIMEOUT_MS, ENV_API_KEY, ENV_BASE_URL, PliuzApiError, PliuzApprovalExpiredError, PliuzApprovalTimeoutError, PliuzAuthError, PliuzClient, PliuzConflictError, PliuzError, PliuzForbiddenError, PliuzNetworkError, PliuzNotFoundError, PliuzPolicyError, PliuzRateLimitError, PliuzRejectedError, PliuzServerError, PliuzTimeoutError, PliuzValidationError, REDACTED_PLACEHOLDER, VERSION, applyRedaction, errorForStatus, gated };
|
|
531
|
+
//# sourceMappingURL=index.js.map
|
|
532
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/version.ts","../src/errors.ts","../src/_http.ts","../src/client.ts","../src/redaction.ts","../src/gated.ts"],"names":["sleep"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Single source of truth for the package version.\n * Kept in sync with `package.json` by release tooling at publish time.\n */\nexport const VERSION = '0.1.0' as const\n","/**\n * Error hierarchy for the Pliuz SDK.\n *\n * All exceptions extend `PliuzError`. HTTP-level failures use `PliuzApiError`\n * subclasses keyed by status code, so callers can do narrow `instanceof` checks:\n *\n * ```ts\n * try {\n * await pliuz.createApproval(...)\n * } catch (e) {\n * if (e instanceof PliuzAuthError) {\n * // refresh API key\n * } else if (e instanceof PliuzPolicyError) {\n * // no policy matched — define a catch-all\n * } else if (e instanceof PliuzApiError) {\n * // other 4xx/5xx\n * }\n * throw e\n * }\n * ```\n */\n\nexport class PliuzError extends Error {\n constructor(message: string) {\n super(message)\n this.name = 'PliuzError'\n // Maintain proper prototype chain for `instanceof` after transpile (CJS target)\n Object.setPrototypeOf(this, new.target.prototype)\n }\n}\n\nexport class PliuzNetworkError extends PliuzError {\n constructor(message: string, public override readonly cause?: unknown) {\n super(message)\n this.name = 'PliuzNetworkError'\n Object.setPrototypeOf(this, new.target.prototype)\n }\n}\n\nexport class PliuzTimeoutError extends PliuzError {\n constructor(message: string) {\n super(message)\n this.name = 'PliuzTimeoutError'\n Object.setPrototypeOf(this, new.target.prototype)\n }\n}\n\nexport class PliuzApiError extends PliuzError {\n constructor(\n public readonly statusCode: number,\n public readonly errorCode: string,\n message: string,\n public readonly details?: Record<string, unknown>,\n ) {\n super(message)\n this.name = 'PliuzApiError'\n Object.setPrototypeOf(this, new.target.prototype)\n }\n}\n\nexport class PliuzValidationError extends PliuzApiError {\n constructor(\n statusCode: number,\n errorCode: string,\n message: string,\n details?: Record<string, unknown>,\n ) {\n super(statusCode, errorCode, message, details)\n this.name = 'PliuzValidationError'\n Object.setPrototypeOf(this, new.target.prototype)\n }\n}\n\nexport class PliuzAuthError extends PliuzApiError {\n constructor(\n statusCode: number,\n errorCode: string,\n message: string,\n details?: Record<string, unknown>,\n ) {\n super(statusCode, errorCode, message, details)\n this.name = 'PliuzAuthError'\n Object.setPrototypeOf(this, new.target.prototype)\n }\n}\n\nexport class PliuzForbiddenError extends PliuzApiError {\n constructor(\n statusCode: number,\n errorCode: string,\n message: string,\n details?: Record<string, unknown>,\n ) {\n super(statusCode, errorCode, message, details)\n this.name = 'PliuzForbiddenError'\n Object.setPrototypeOf(this, new.target.prototype)\n }\n}\n\nexport class PliuzNotFoundError extends PliuzApiError {\n constructor(\n statusCode: number,\n errorCode: string,\n message: string,\n details?: Record<string, unknown>,\n ) {\n super(statusCode, errorCode, message, details)\n this.name = 'PliuzNotFoundError'\n Object.setPrototypeOf(this, new.target.prototype)\n }\n}\n\nexport class PliuzConflictError extends PliuzApiError {\n constructor(\n statusCode: number,\n errorCode: string,\n message: string,\n details?: Record<string, unknown>,\n ) {\n super(statusCode, errorCode, message, details)\n this.name = 'PliuzConflictError'\n Object.setPrototypeOf(this, new.target.prototype)\n }\n}\n\nexport class PliuzPolicyError extends PliuzApiError {\n constructor(\n statusCode: number,\n errorCode: string,\n message: string,\n details?: Record<string, unknown>,\n ) {\n super(statusCode, errorCode, message, details)\n this.name = 'PliuzPolicyError'\n Object.setPrototypeOf(this, new.target.prototype)\n }\n}\n\nexport class PliuzRateLimitError extends PliuzApiError {\n constructor(\n statusCode: number,\n errorCode: string,\n message: string,\n details?: Record<string, unknown>,\n ) {\n super(statusCode, errorCode, message, details)\n this.name = 'PliuzRateLimitError'\n Object.setPrototypeOf(this, new.target.prototype)\n }\n}\n\nexport class PliuzServerError extends PliuzApiError {\n constructor(\n statusCode: number,\n errorCode: string,\n message: string,\n details?: Record<string, unknown>,\n ) {\n super(statusCode, errorCode, message, details)\n this.name = 'PliuzServerError'\n Object.setPrototypeOf(this, new.target.prototype)\n }\n}\n\n// ---------------------------------------------------------------------------\n// gated() lifecycle errors — raised by the wrapper, NOT by direct client calls.\n// ---------------------------------------------------------------------------\n\nexport class PliuzRejectedError extends PliuzError {\n constructor(public readonly approvalId: string, public readonly reason: string | null) {\n const msg = reason\n ? `approval ${approvalId} was rejected: ${reason}`\n : `approval ${approvalId} was rejected`\n super(msg)\n this.name = 'PliuzRejectedError'\n Object.setPrototypeOf(this, new.target.prototype)\n }\n}\n\nexport class PliuzApprovalExpiredError extends PliuzError {\n constructor(public readonly approvalId: string) {\n super(`approval ${approvalId} expired before a human decided`)\n this.name = 'PliuzApprovalExpiredError'\n Object.setPrototypeOf(this, new.target.prototype)\n }\n}\n\nexport class PliuzApprovalTimeoutError extends PliuzError {\n constructor(public readonly approvalId: string, public readonly timeoutMs: number) {\n super(\n `approval ${approvalId} did not resolve within ${timeoutMs}ms ` +\n '(SDK polling timeout — approval may still be pending server-side)',\n )\n this.name = 'PliuzApprovalTimeoutError'\n Object.setPrototypeOf(this, new.target.prototype)\n }\n}\n\nconst STATUS_TO_ERROR: Record<number, new (s: number, c: string, m: string, d?: Record<string, unknown>) => PliuzApiError> = {\n 400: PliuzValidationError,\n 401: PliuzAuthError,\n 403: PliuzForbiddenError,\n 404: PliuzNotFoundError,\n 409: PliuzConflictError,\n 422: PliuzPolicyError,\n 429: PliuzRateLimitError,\n}\n\nexport function errorForStatus(\n statusCode: number,\n errorCode: string,\n message: string,\n details?: Record<string, unknown>,\n): PliuzApiError {\n if (statusCode >= 500 && statusCode < 600) {\n return new PliuzServerError(statusCode, errorCode, message, details)\n }\n const Cls = STATUS_TO_ERROR[statusCode] ?? PliuzApiError\n return new Cls(statusCode, errorCode, message, details)\n}\n","/**\n * Internal HTTP layer — shared by PliuzClient.\n *\n * Centralizes: header injection, error mapping, exponential-backoff retries\n * for idempotent operations, and JSON parsing.\n *\n * NOT public API — only `PliuzClient` should import from here.\n */\n\nimport { VERSION } from './version.js'\nimport type { ApiErrorBody } from './types.js'\nimport {\n PliuzApiError,\n PliuzNetworkError,\n PliuzTimeoutError,\n errorForStatus,\n} from './errors.js'\n\n// HTTP methods safe to retry without risking duplicate side-effects.\nconst IDEMPOTENT_METHODS = new Set(['GET', 'HEAD', 'OPTIONS', 'PUT', 'DELETE'])\n\n// Retry-able status codes — server hiccups, NOT client errors.\nconst RETRYABLE_STATUS = new Set([408, 429, 500, 502, 503, 504])\n\nexport interface RequestOptions {\n method: 'GET' | 'POST' | 'PUT' | 'DELETE'\n url: string\n apiKey: string\n body?: Record<string, unknown> | null\n timeoutMs: number\n maxRetries: number\n retryOnPost?: boolean\n fetchImpl: typeof fetch\n}\n\nfunction userAgent(): string {\n return `pliuz-typescript/${VERSION}`\n}\n\nfunction buildHeaders(apiKey: string): Record<string, string> {\n return {\n 'X-Pliuz-Api-Key': apiKey,\n 'User-Agent': userAgent(),\n Accept: 'application/json',\n 'Content-Type': 'application/json',\n }\n}\n\nfunction backoffMs(attempt: number, baseMs = 500, capMs = 8000): number {\n // Exponential with full jitter (AWS architecture blog formula).\n const expo = Math.min(capMs, baseMs * 2 ** (attempt - 1))\n return Math.floor(Math.random() * expo)\n}\n\nfunction isRetryable(method: string, status: number, retryOnPost: boolean): boolean {\n if (!RETRYABLE_STATUS.has(status)) return false\n if (IDEMPOTENT_METHODS.has(method)) return true\n if (method === 'POST' && retryOnPost) return true\n return false\n}\n\nasync function parseError(response: Response): Promise<PliuzApiError> {\n let body: Partial<ApiErrorBody> = {}\n try {\n body = (await response.json()) as Partial<ApiErrorBody>\n } catch {\n /* non-JSON body — fall through with empty */\n }\n const errorCode = body.error ?? 'unknown_error'\n const message = body.message ?? response.statusText ?? 'Pliuz API error'\n const details = body.details\n return errorForStatus(response.status, errorCode, message, details)\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms))\n}\n\nexport async function request<T>(opts: RequestOptions): Promise<T> {\n const { method, url, apiKey, body, timeoutMs, maxRetries, retryOnPost = false, fetchImpl } = opts\n const headers = buildHeaders(apiKey)\n\n let lastError: Error | null = null\n\n for (let attempt = 1; attempt <= maxRetries + 1; attempt++) {\n const controller = new AbortController()\n const timer = setTimeout(() => controller.abort(), timeoutMs)\n\n let response: Response\n try {\n response = await fetchImpl(url, {\n method,\n headers,\n body: body == null ? undefined : JSON.stringify(body),\n signal: controller.signal,\n })\n } catch (e) {\n clearTimeout(timer)\n const isTimeout =\n e instanceof Error &&\n (e.name === 'AbortError' || /abort|timeout/i.test(e.message))\n lastError = isTimeout\n ? new PliuzTimeoutError(`Pliuz request timed out after ${timeoutMs}ms`)\n : new PliuzNetworkError(\n `Pliuz network error: ${e instanceof Error ? e.message : String(e)}`,\n e,\n )\n if (attempt > maxRetries) throw lastError\n await sleep(backoffMs(attempt))\n continue\n }\n clearTimeout(timer)\n\n if (response.status >= 200 && response.status < 300) {\n try {\n return (await response.json()) as T\n } catch (e) {\n throw new PliuzApiError(\n response.status,\n 'invalid_json',\n `Pliuz returned non-JSON 2xx body: ${e instanceof Error ? e.message : String(e)}`,\n )\n }\n }\n\n if (isRetryable(method, response.status, retryOnPost)) {\n if (attempt > maxRetries) {\n throw await parseError(response)\n }\n await sleep(backoffMs(attempt))\n continue\n }\n\n throw await parseError(response)\n }\n\n // Unreachable — the loop always returns or throws.\n throw lastError ?? new PliuzApiError(0, 'exhausted', 'retry loop exhausted unexpectedly')\n}\n","/**\n * PliuzClient — the SDK entry point.\n *\n * Reads PLIUZ_API_KEY env var by default (Node only). Apply redaction\n * client-side via `applyRedaction` before passing tool_args; the client\n * does NOT redact for you (separation of concerns — the `gated()` wrapper\n * in Fase 7 will wire them together).\n *\n * @example\n * import { PliuzClient, applyRedaction } from '@pliuz/sdk'\n *\n * const pliuz = new PliuzClient() // reads PLIUZ_API_KEY\n *\n * const response = await pliuz.createApproval({\n * tool_name: 'issue_refund',\n * tool_args: applyRedaction(\n * { customer: { ssn: '...', id: 'cus_123' }, amount_cents: 5000 },\n * ['customer.ssn'],\n * ),\n * idempotency_key: 'refund-cus_123-2026-05-21-001',\n * })\n */\n\nimport { request } from './_http.js'\nimport type {\n ApprovalRequest,\n CreateApprovalInput,\n CreateApprovalResponse,\n ExecutionInput,\n ExecutionResult,\n} from './types.js'\n\nexport const DEFAULT_BASE_URL = 'https://pliuz-dev.vercel.app'\nexport const DEFAULT_TIMEOUT_MS = 30_000\nexport const DEFAULT_MAX_RETRIES = 3\nexport const ENV_API_KEY = 'PLIUZ_API_KEY'\nexport const ENV_BASE_URL = 'PLIUZ_BASE_URL'\n\nexport interface PliuzClientOptions {\n /**\n * Pliuz API key (per-agent). Defaults to `process.env.PLIUZ_API_KEY`.\n * NEVER hardcode in source — read from env or a secrets manager.\n */\n apiKey?: string\n\n /**\n * Base URL (no trailing slash). Defaults to `process.env.PLIUZ_BASE_URL`\n * or `https://pliuz-dev.vercel.app`.\n */\n baseUrl?: string\n\n /** Per-request timeout in milliseconds. Default: 30 000. */\n timeoutMs?: number\n\n /** Max retries for transient failures (network / 5xx / 429). Default: 3. */\n maxRetries?: number\n\n /**\n * Custom fetch implementation. Defaults to global `fetch` (Node ≥18).\n * Use for testing (MSW, vitest-fetch-mock) or to inject proxy/keepalive.\n */\n fetch?: typeof fetch\n}\n\nfunction resolveApiKey(explicit: string | undefined): string {\n if (explicit) return explicit\n if (typeof process !== 'undefined' && process.env?.[ENV_API_KEY]) {\n return process.env[ENV_API_KEY]\n }\n throw new Error(`Pliuz API key not provided. Pass apiKey or set $${ENV_API_KEY}.`)\n}\n\nfunction resolveBaseUrl(explicit: string | undefined): string {\n if (explicit) return explicit.replace(/\\/+$/, '')\n if (typeof process !== 'undefined' && process.env?.[ENV_BASE_URL]) {\n return process.env[ENV_BASE_URL].replace(/\\/+$/, '')\n }\n return DEFAULT_BASE_URL\n}\n\nfunction approvalUrl(baseUrl: string, approvalId?: string, suffix?: string): string {\n let path = '/api/v1/approvals'\n if (approvalId) path += `/${encodeURIComponent(approvalId)}`\n if (suffix) path += `/${suffix}`\n return baseUrl + path\n}\n\nexport class PliuzClient {\n private readonly apiKey: string\n private readonly baseUrl: string\n private readonly timeoutMs: number\n private readonly maxRetries: number\n private readonly fetchImpl: typeof fetch\n\n constructor(options: PliuzClientOptions = {}) {\n this.apiKey = resolveApiKey(options.apiKey)\n this.baseUrl = resolveBaseUrl(options.baseUrl)\n this.timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS\n this.maxRetries = options.maxRetries ?? DEFAULT_MAX_RETRIES\n\n const fetchImpl = options.fetch ?? globalThis.fetch\n if (!fetchImpl) {\n throw new Error(\n 'No fetch implementation available. Pliuz requires Node ≥18 or a polyfill.',\n )\n }\n this.fetchImpl = fetchImpl\n }\n\n /**\n * Create (or idempotently replay) an approval request.\n *\n * With `idempotency_key`, the call retries on network failure without\n * risk of duplicates (backend dedupes within 24h).\n */\n async createApproval(input: CreateApprovalInput): Promise<CreateApprovalResponse> {\n const body = {\n tool_name: input.tool_name,\n tool_args: input.tool_args,\n context_messages: input.context_messages ?? null,\n originator: input.originator ?? { type: 'system' },\n session_id: input.session_id ?? null,\n client_metadata: input.client_metadata ?? null,\n idempotency_key: input.idempotency_key ?? null,\n }\n return request<CreateApprovalResponse>({\n method: 'POST',\n url: approvalUrl(this.baseUrl),\n apiKey: this.apiKey,\n body,\n timeoutMs: this.timeoutMs,\n maxRetries: this.maxRetries,\n retryOnPost: Boolean(input.idempotency_key),\n fetchImpl: this.fetchImpl,\n })\n }\n\n /**\n * Fetch the current state of an approval request.\n * Used by polling in `gated()` to detect status transitions.\n */\n async getApproval(approvalId: string): Promise<ApprovalRequest> {\n return request<ApprovalRequest>({\n method: 'GET',\n url: approvalUrl(this.baseUrl, approvalId),\n apiKey: this.apiKey,\n timeoutMs: this.timeoutMs,\n maxRetries: this.maxRetries,\n fetchImpl: this.fetchImpl,\n })\n }\n\n /**\n * Close the audit loop by reporting the outcome of the gated action.\n *\n * Single-shot: a second call returns 409 (PliuzConflictError).\n * Only the originating agent can report — others get 403 (PliuzForbiddenError).\n */\n async reportExecution(\n approvalId: string,\n input: ExecutionInput,\n ): Promise<ExecutionResult> {\n const body = {\n status: input.status,\n error: input.error ?? null,\n latency_ms: input.latency_ms ?? null,\n target_response_excerpt: input.target_response_excerpt ?? null,\n }\n return request<ExecutionResult>({\n method: 'POST',\n url: approvalUrl(this.baseUrl, approvalId, 'execution'),\n apiKey: this.apiKey,\n body,\n timeoutMs: this.timeoutMs,\n maxRetries: this.maxRetries,\n retryOnPost: false,\n fetchImpl: this.fetchImpl,\n })\n }\n}\n","/**\n * Field-level redaction applied client-side BEFORE sending to Pliuz.\n *\n * Non-negotiable architectural decision: sensitive fields (PII, secrets, PHI)\n * must never leave the caller's process in plaintext. The SDK applies\n * redaction on the client side, transmits the redacted payload, and the\n * backend never sees the raw values.\n *\n * ## Path syntax\n *\n * \"customer.ssn\" → payload.customer.ssn\n * \"items[*].card_number\" → every item's card_number\n * \"headers.authorization\" → payload.headers.authorization\n *\n * Limitations (intentional — keep the surface tiny):\n * - No JSONPath wildcards beyond `[*]`\n * - No filter expressions\n * - Missing keys are silently ignored\n */\n\nexport const REDACTED_PLACEHOLDER = '<redacted>' as const\n\ntype PathSegment = { key: string; isArrayWildcard: boolean }\n\n/**\n * Returns a deep copy of `payload` with values at `paths` replaced by\n * `REDACTED_PLACEHOLDER`. Does not mutate the input.\n *\n * @example\n * applyRedaction({ customer: { ssn: '123-45-6789' } }, ['customer.ssn'])\n * // → { customer: { ssn: '<redacted>' } }\n */\nexport function applyRedaction<T extends Record<string, unknown>>(\n payload: T,\n paths: readonly string[] | null | undefined,\n): T {\n if (!paths || paths.length === 0) {\n return deepClone(payload)\n }\n const result = deepClone(payload)\n for (const path of paths) {\n redactPath(result as Record<string, unknown>, parsePath(path))\n }\n return result\n}\n\n// ---------------------------------------------------------------------------\n// Internal\n// ---------------------------------------------------------------------------\n\nfunction parsePath(path: string): PathSegment[] {\n if (!path) {\n throw new Error('redaction path cannot be empty')\n }\n\n const segments: PathSegment[] = []\n for (const raw of path.split('.')) {\n if (raw.endsWith('[*]')) {\n const key = raw.slice(0, -3)\n if (!key) {\n throw new Error(`redaction path segment before [*] cannot be empty: ${path}`)\n }\n segments.push({ key, isArrayWildcard: true })\n } else if (raw.includes('[') || raw.includes(']')) {\n throw new Error(`unsupported redaction syntax in '${path}' — only [*] is allowed`)\n } else {\n if (!raw) {\n throw new Error(`redaction path has empty segment: ${path}`)\n }\n segments.push({ key: raw, isArrayWildcard: false })\n }\n }\n return segments\n}\n\nfunction redactPath(node: unknown, segments: PathSegment[]): void {\n if (segments.length === 0) return\n if (!isPlainObject(node)) return\n\n const [head, ...rest] = segments\n if (!head) return\n const { key, isArrayWildcard } = head\n if (!(key in node)) return\n\n if (isArrayWildcard) {\n const target = node[key]\n if (!Array.isArray(target)) return\n if (rest.length === 0) {\n node[key] = target.map(() => REDACTED_PLACEHOLDER)\n return\n }\n for (const item of target) {\n redactPath(item, rest)\n }\n } else {\n if (rest.length === 0) {\n node[key] = REDACTED_PLACEHOLDER\n } else {\n redactPath(node[key], rest)\n }\n }\n}\n\nfunction isPlainObject(v: unknown): v is Record<string, unknown> {\n return typeof v === 'object' && v !== null && !Array.isArray(v)\n}\n\nfunction deepClone<T>(v: T): T {\n // Prefer structuredClone when available (Node 17+). Falls back to JSON\n // for environments without it (e.g. some bundler test setups).\n if (typeof structuredClone === 'function') {\n return structuredClone(v)\n }\n return JSON.parse(JSON.stringify(v)) as T\n}\n","/**\n * The `gated()` wrapper — the headline API of the Pliuz TypeScript SDK.\n *\n * Wraps any function (sync or async) so every call is gated by a Pliuz\n * approval request. The wrapped function always returns `Promise<TResult>`\n * even if the original is synchronous — gating is inherently async because\n * it involves an HTTP call.\n *\n * @example\n * import { gated } from '@pliuz/sdk'\n *\n * const issueRefund = gated(\n * {\n * policy: 'refund',\n * redact: ['customer.ssn'],\n * toolArgs: (customerId: string, amountCents: number) => ({\n * customer_id: customerId,\n * amount_cents: amountCents,\n * }),\n * },\n * async (customerId, amountCents) => stripe.refund(customerId, amountCents),\n * )\n *\n * const result = await issueRefund('cus_123', 5000)\n */\n\nimport { createHash } from 'node:crypto'\n\nimport { PliuzClient } from './client.js'\nimport {\n PliuzApprovalExpiredError,\n PliuzApprovalTimeoutError,\n PliuzRejectedError,\n} from './errors.js'\nimport { applyRedaction } from './redaction.js'\nimport type {\n ApprovalRequest,\n ExecutionStatus,\n Originator,\n} from './types.js'\n\nexport const DEFAULT_GATED_TIMEOUT_MS = 300_000 // 5 min — matches Pliuz default SLA\nexport const DEFAULT_GATED_POLL_INTERVAL_MS = 2000\n\nexport interface GatedOptions<TArgs extends readonly unknown[]> {\n /** Policy slug to bind this gate to. Default: backend resolves by toolName. */\n policy?: string\n\n /** Redaction paths applied to tool_args BEFORE sending. */\n redact?: readonly string[]\n\n /** Override the tool name. Default: `fn.name` (may be empty for anonymous). */\n toolName?: string\n\n /**\n * Map the wrapped function's args into a tool_args object.\n * Default: `(...args) => ({ args })` — wraps all positional args under\n * a single `args` key. Provide a custom mapper for cleaner audit trails.\n */\n toolArgs?: (...args: TArgs) => Record<string, unknown>\n\n /** Max ms to poll for the approval to resolve. Throws on timeout. */\n timeoutMs?: number\n\n /** Ms between GET /approvals/:id polls. */\n pollIntervalMs?: number\n\n /** Reuse an existing PliuzClient. Default: `new PliuzClient()`. */\n client?: PliuzClient\n\n /** Optional context for the human approver. */\n contextMessages?: readonly string[]\n\n /** Optional session id (groups related approvals in the UI). */\n sessionId?: string\n\n /** Optional originator (default: { type: 'system' }). */\n originator?: Originator\n}\n\nconst TERMINAL_OK = new Set<string>(['approved'])\nconst TERMINAL_REJECT = new Set<string>(['rejected'])\nconst TERMINAL_EXPIRED = new Set<string>(['expired'])\n\n/**\n * Wrap a function so every call is gated by Pliuz.\n *\n * The returned function ALWAYS returns a Promise, even if the input is sync.\n * This is intentional — gating necessarily involves async HTTP calls.\n */\nexport function gated<TArgs extends readonly unknown[], TResult>(\n options: GatedOptions<TArgs>,\n fn: (...args: TArgs) => Promise<TResult> | TResult,\n): (...args: TArgs) => Promise<TResult> {\n const toolName = options.toolName ?? (fn.name || 'anonymous')\n const toolArgsMapper =\n options.toolArgs ??\n ((...args: TArgs): Record<string, unknown> => ({ args: args as unknown as unknown[] }))\n const timeoutMs = options.timeoutMs ?? DEFAULT_GATED_TIMEOUT_MS\n const pollIntervalMs = options.pollIntervalMs ?? DEFAULT_GATED_POLL_INTERVAL_MS\n const redactPaths = options.redact\n\n return async (...args: TArgs): Promise<TResult> => {\n const activeClient = options.client ?? new PliuzClient()\n const rawArgs = toolArgsMapper(...args)\n const sendArgs = redactPaths && redactPaths.length > 0\n ? applyRedaction(rawArgs, redactPaths)\n : rawArgs\n const idempotencyKey = idempotencyKeyFor(toolName, sendArgs, options.sessionId)\n const metadata: Record<string, unknown> = {}\n if (options.policy) metadata.policy = options.policy\n\n const response = await activeClient.createApproval({\n tool_name: toolName,\n tool_args: sendArgs,\n context_messages: options.contextMessages ?? null,\n originator: options.originator,\n session_id: options.sessionId ?? null,\n client_metadata: Object.keys(metadata).length > 0 ? metadata : null,\n idempotency_key: idempotencyKey,\n })\n\n const approvalId = response.id\n\n if (TERMINAL_REJECT.has(response.status)) {\n throw new PliuzRejectedError(approvalId, response.message ?? null)\n }\n if (TERMINAL_EXPIRED.has(response.status)) {\n throw new PliuzApprovalExpiredError(approvalId)\n }\n\n if (!TERMINAL_OK.has(response.status)) {\n // Poll\n const deadline = Date.now() + timeoutMs\n while (true) {\n if (Date.now() >= deadline) {\n throw new PliuzApprovalTimeoutError(approvalId, timeoutMs)\n }\n await sleep(pollIntervalMs)\n const approval = await activeClient.getApproval(approvalId)\n if (terminalOrThrow(approval)) break\n }\n }\n\n // Execute + report\n const started = Date.now()\n try {\n const result = await fn(...args)\n const latencyMs = Date.now() - started\n void safeReport(activeClient, approvalId, 'success', null, latencyMs, result)\n return result\n } catch (e) {\n const latencyMs = Date.now() - started\n const errMsg = e instanceof Error ? e.message : String(e)\n void safeReport(activeClient, approvalId, 'error', errMsg.slice(0, 2000), latencyMs, null)\n throw e\n }\n }\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nfunction idempotencyKeyFor(\n toolName: string,\n toolArgs: Record<string, unknown>,\n sessionId: string | undefined,\n): string {\n const payload = JSON.stringify(\n { tool: toolName, args: toolArgs, session: sessionId ?? null },\n Object.keys({ tool: 0, args: 0, session: 0 }).sort(),\n )\n // Canonicalize via sorted-key JSON stringify for deterministic hash\n const canonical = canonicalJSON({ tool: toolName, args: toolArgs, session: sessionId ?? null })\n void payload // silence unused\n return 'g_' + createHash('sha256').update(canonical).digest('hex').slice(0, 30)\n}\n\nfunction canonicalJSON(value: unknown): string {\n if (value === null || typeof value !== 'object') {\n return JSON.stringify(value)\n }\n if (Array.isArray(value)) {\n return '[' + value.map(canonicalJSON).join(',') + ']'\n }\n const obj = value as Record<string, unknown>\n const keys = Object.keys(obj).sort()\n return (\n '{' +\n keys.map((k) => JSON.stringify(k) + ':' + canonicalJSON(obj[k])).join(',') +\n '}'\n )\n}\n\nfunction terminalOrThrow(approval: ApprovalRequest): boolean {\n if (TERMINAL_OK.has(approval.status)) return true\n if (TERMINAL_REJECT.has(approval.status)) {\n throw new PliuzRejectedError(approval.id, approval.decision_reason)\n }\n if (TERMINAL_EXPIRED.has(approval.status)) {\n throw new PliuzApprovalExpiredError(approval.id)\n }\n return false\n}\n\nasync function safeReport(\n client: PliuzClient,\n approvalId: string,\n status: ExecutionStatus,\n errorMessage: string | null,\n latencyMs: number,\n result: unknown,\n): Promise<void> {\n let excerpt: string | null = null\n if (result != null) {\n try {\n excerpt = JSON.stringify(result).slice(0, 2000)\n } catch {\n excerpt = String(result).slice(0, 2000)\n }\n }\n try {\n await client.reportExecution(approvalId, {\n status,\n error: errorMessage,\n latency_ms: latencyMs,\n target_response_excerpt: excerpt,\n })\n } catch {\n // Intentionally swallow — if reporting fails the audit log surfaces\n // 'execution unreported' but we don't want to crash the caller's flow.\n }\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms))\n}\n"]}
|