@pliuz/sdk 0.1.0 → 0.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.cts CHANGED
@@ -1,11 +1,11 @@
1
- import { P as PliuzClient, O as Originator } from './client-BABvN_88.cjs';
2
- export { A as ApiErrorBody, a as ApprovalRequest, b as ApprovalStatus, C as CreateApprovalInput, c as CreateApprovalResponse, D as DEFAULT_BASE_URL, d as DEFAULT_MAX_RETRIES, e as DEFAULT_TIMEOUT_MS, E as ENV_API_KEY, f as ENV_BASE_URL, g as ExecutionInput, h as ExecutionResult, i as ExecutionStatus, j as OriginatorType, k as PliuzClientOptions } from './client-BABvN_88.cjs';
1
+ import { P as PliuzClient, O as Originator } from './client-K9c9HsAc.cjs';
2
+ export { A as ApiErrorBody, a as ApprovalRequest, b as ApprovalStatus, C as CreateApprovalInput, c as CreateApprovalResponse, D as DEFAULT_BASE_URL, d as DEFAULT_MAX_RETRIES, e as DEFAULT_TIMEOUT_MS, E as ENV_API_KEY, f as ENV_BASE_URL, g as ExecutionInput, h as ExecutionResult, i as ExecutionStatus, j as OriginatorType, k as PliuzClientOptions } from './client-K9c9HsAc.cjs';
3
3
 
4
4
  /**
5
5
  * Single source of truth for the package version.
6
6
  * Kept in sync with `package.json` by release tooling at publish time.
7
7
  */
8
- declare const VERSION: "0.1.0";
8
+ declare const VERSION: "0.2.2";
9
9
 
10
10
  /**
11
11
  * Field-level redaction applied client-side BEFORE sending to Pliuz.
@@ -31,11 +31,16 @@ declare const REDACTED_PLACEHOLDER: "<redacted>";
31
31
  * Returns a deep copy of `payload` with values at `paths` replaced by
32
32
  * `REDACTED_PLACEHOLDER`. Does not mutate the input.
33
33
  *
34
+ * When `strict` is true, a syntactically valid path that resolves to no
35
+ * existing field throws {@link PliuzRedactionPathError} instead of being a
36
+ * silent no-op — turn it on to fail loud and avoid leaking a field you meant
37
+ * to redact. Defaults to false (lenient: a typo'd path is ignored).
38
+ *
34
39
  * @example
35
40
  * applyRedaction({ customer: { ssn: '123-45-6789' } }, ['customer.ssn'])
36
41
  * // → { customer: { ssn: '<redacted>' } }
37
42
  */
38
- declare function applyRedaction<T extends Record<string, unknown>>(payload: T, paths: readonly string[] | null | undefined): T;
43
+ declare function applyRedaction<T extends Record<string, unknown>>(payload: T, paths: readonly string[] | null | undefined, strict?: boolean): T;
39
44
 
40
45
  /**
41
46
  * Error hierarchy for the Pliuz SDK.
@@ -65,6 +70,16 @@ declare class PliuzNetworkError extends PliuzError {
65
70
  readonly cause?: unknown | undefined;
66
71
  constructor(message: string, cause?: unknown | undefined);
67
72
  }
73
+ /**
74
+ * A redaction path was syntactically valid but matched no field. Thrown only
75
+ * in strict mode (`redactStrict: true`) to catch the silent-leak footgun where
76
+ * a typo like `'customer.ssnn'` would otherwise be a no-op, leaving the real
77
+ * `customer.ssn` unredacted in the payload sent to Pliuz.
78
+ */
79
+ declare class PliuzRedactionPathError extends PliuzError {
80
+ readonly path: string;
81
+ constructor(path: string);
82
+ }
68
83
  declare class PliuzTimeoutError extends PliuzError {
69
84
  constructor(message: string);
70
85
  }
@@ -112,6 +127,19 @@ declare class PliuzApprovalTimeoutError extends PliuzError {
112
127
  readonly timeoutMs: number;
113
128
  constructor(approvalId: string, timeoutMs: number);
114
129
  }
130
+ /**
131
+ * A human approved with EDITED args (action='edit') but the SDK cannot map
132
+ * `final_args` back onto the wrapped function's parameters. The SDK refuses
133
+ * to execute the ORIGINAL (unapproved) args — that would make the audit
134
+ * trail lie about what was approved vs executed. Carries `finalArgs` so the
135
+ * caller can apply the edit manually, or pass an `applyFinalArgs` mapper in
136
+ * GatedOptions to handle edits automatically.
137
+ */
138
+ declare class PliuzEditNotApplicableError extends PliuzError {
139
+ readonly approvalId: string;
140
+ readonly finalArgs: Record<string, unknown>;
141
+ constructor(approvalId: string, finalArgs: Record<string, unknown>);
142
+ }
115
143
  declare function errorForStatus(statusCode: number, errorCode: string, message: string, details?: Record<string, unknown>): PliuzApiError;
116
144
 
117
145
  /**
@@ -147,6 +175,12 @@ interface GatedOptions<TArgs extends readonly unknown[]> {
147
175
  policy?: string;
148
176
  /** Redaction paths applied to tool_args BEFORE sending. */
149
177
  redact?: readonly string[];
178
+ /**
179
+ * When true, a `redact` path that matches no field throws
180
+ * PliuzRedactionPathError instead of being a silent no-op — fail loud so a
181
+ * typo'd path never leaks the field you meant to redact. Default: false.
182
+ */
183
+ redactStrict?: boolean;
150
184
  /** Override the tool name. Default: `fn.name` (may be empty for anonymous). */
151
185
  toolName?: string;
152
186
  /**
@@ -167,6 +201,14 @@ interface GatedOptions<TArgs extends readonly unknown[]> {
167
201
  sessionId?: string;
168
202
  /** Optional originator (default: { type: 'system' }). */
169
203
  originator?: Originator;
204
+ /**
205
+ * Map human-EDITED args (approval decided with action='edit') back onto
206
+ * the wrapped function's argument tuple. Required to honor edits when a
207
+ * custom `toolArgs` mapper is used (the SDK cannot invert an arbitrary
208
+ * mapper). Without it, an edited approval throws
209
+ * PliuzEditNotApplicableError instead of executing the original args.
210
+ */
211
+ applyFinalArgs?: (finalArgs: Record<string, unknown>, originalArgs: TArgs) => TArgs;
170
212
  }
171
213
  /**
172
214
  * Wrap a function so every call is gated by Pliuz.
@@ -176,4 +218,4 @@ interface GatedOptions<TArgs extends readonly unknown[]> {
176
218
  */
177
219
  declare function gated<TArgs extends readonly unknown[], TResult>(options: GatedOptions<TArgs>, fn: (...args: TArgs) => Promise<TResult> | TResult): (...args: TArgs) => Promise<TResult>;
178
220
 
179
- export { DEFAULT_GATED_POLL_INTERVAL_MS, DEFAULT_GATED_TIMEOUT_MS, type GatedOptions, Originator, PliuzApiError, PliuzApprovalExpiredError, PliuzApprovalTimeoutError, PliuzAuthError, PliuzClient, PliuzConflictError, PliuzError, PliuzForbiddenError, PliuzNetworkError, PliuzNotFoundError, PliuzPolicyError, PliuzRateLimitError, PliuzRejectedError, PliuzServerError, PliuzTimeoutError, PliuzValidationError, REDACTED_PLACEHOLDER, VERSION, applyRedaction, errorForStatus, gated };
221
+ export { DEFAULT_GATED_POLL_INTERVAL_MS, DEFAULT_GATED_TIMEOUT_MS, type GatedOptions, Originator, PliuzApiError, PliuzApprovalExpiredError, PliuzApprovalTimeoutError, PliuzAuthError, PliuzClient, PliuzConflictError, PliuzEditNotApplicableError, PliuzError, PliuzForbiddenError, PliuzNetworkError, PliuzNotFoundError, PliuzPolicyError, PliuzRateLimitError, PliuzRedactionPathError, PliuzRejectedError, PliuzServerError, PliuzTimeoutError, PliuzValidationError, REDACTED_PLACEHOLDER, VERSION, applyRedaction, errorForStatus, gated };
package/dist/index.d.ts CHANGED
@@ -1,11 +1,11 @@
1
- import { P as PliuzClient, O as Originator } from './client-BABvN_88.js';
2
- export { A as ApiErrorBody, a as ApprovalRequest, b as ApprovalStatus, C as CreateApprovalInput, c as CreateApprovalResponse, D as DEFAULT_BASE_URL, d as DEFAULT_MAX_RETRIES, e as DEFAULT_TIMEOUT_MS, E as ENV_API_KEY, f as ENV_BASE_URL, g as ExecutionInput, h as ExecutionResult, i as ExecutionStatus, j as OriginatorType, k as PliuzClientOptions } from './client-BABvN_88.js';
1
+ import { P as PliuzClient, O as Originator } from './client-K9c9HsAc.js';
2
+ export { A as ApiErrorBody, a as ApprovalRequest, b as ApprovalStatus, C as CreateApprovalInput, c as CreateApprovalResponse, D as DEFAULT_BASE_URL, d as DEFAULT_MAX_RETRIES, e as DEFAULT_TIMEOUT_MS, E as ENV_API_KEY, f as ENV_BASE_URL, g as ExecutionInput, h as ExecutionResult, i as ExecutionStatus, j as OriginatorType, k as PliuzClientOptions } from './client-K9c9HsAc.js';
3
3
 
4
4
  /**
5
5
  * Single source of truth for the package version.
6
6
  * Kept in sync with `package.json` by release tooling at publish time.
7
7
  */
8
- declare const VERSION: "0.1.0";
8
+ declare const VERSION: "0.2.2";
9
9
 
10
10
  /**
11
11
  * Field-level redaction applied client-side BEFORE sending to Pliuz.
@@ -31,11 +31,16 @@ declare const REDACTED_PLACEHOLDER: "<redacted>";
31
31
  * Returns a deep copy of `payload` with values at `paths` replaced by
32
32
  * `REDACTED_PLACEHOLDER`. Does not mutate the input.
33
33
  *
34
+ * When `strict` is true, a syntactically valid path that resolves to no
35
+ * existing field throws {@link PliuzRedactionPathError} instead of being a
36
+ * silent no-op — turn it on to fail loud and avoid leaking a field you meant
37
+ * to redact. Defaults to false (lenient: a typo'd path is ignored).
38
+ *
34
39
  * @example
35
40
  * applyRedaction({ customer: { ssn: '123-45-6789' } }, ['customer.ssn'])
36
41
  * // → { customer: { ssn: '<redacted>' } }
37
42
  */
38
- declare function applyRedaction<T extends Record<string, unknown>>(payload: T, paths: readonly string[] | null | undefined): T;
43
+ declare function applyRedaction<T extends Record<string, unknown>>(payload: T, paths: readonly string[] | null | undefined, strict?: boolean): T;
39
44
 
40
45
  /**
41
46
  * Error hierarchy for the Pliuz SDK.
@@ -65,6 +70,16 @@ declare class PliuzNetworkError extends PliuzError {
65
70
  readonly cause?: unknown | undefined;
66
71
  constructor(message: string, cause?: unknown | undefined);
67
72
  }
73
+ /**
74
+ * A redaction path was syntactically valid but matched no field. Thrown only
75
+ * in strict mode (`redactStrict: true`) to catch the silent-leak footgun where
76
+ * a typo like `'customer.ssnn'` would otherwise be a no-op, leaving the real
77
+ * `customer.ssn` unredacted in the payload sent to Pliuz.
78
+ */
79
+ declare class PliuzRedactionPathError extends PliuzError {
80
+ readonly path: string;
81
+ constructor(path: string);
82
+ }
68
83
  declare class PliuzTimeoutError extends PliuzError {
69
84
  constructor(message: string);
70
85
  }
@@ -112,6 +127,19 @@ declare class PliuzApprovalTimeoutError extends PliuzError {
112
127
  readonly timeoutMs: number;
113
128
  constructor(approvalId: string, timeoutMs: number);
114
129
  }
130
+ /**
131
+ * A human approved with EDITED args (action='edit') but the SDK cannot map
132
+ * `final_args` back onto the wrapped function's parameters. The SDK refuses
133
+ * to execute the ORIGINAL (unapproved) args — that would make the audit
134
+ * trail lie about what was approved vs executed. Carries `finalArgs` so the
135
+ * caller can apply the edit manually, or pass an `applyFinalArgs` mapper in
136
+ * GatedOptions to handle edits automatically.
137
+ */
138
+ declare class PliuzEditNotApplicableError extends PliuzError {
139
+ readonly approvalId: string;
140
+ readonly finalArgs: Record<string, unknown>;
141
+ constructor(approvalId: string, finalArgs: Record<string, unknown>);
142
+ }
115
143
  declare function errorForStatus(statusCode: number, errorCode: string, message: string, details?: Record<string, unknown>): PliuzApiError;
116
144
 
117
145
  /**
@@ -147,6 +175,12 @@ interface GatedOptions<TArgs extends readonly unknown[]> {
147
175
  policy?: string;
148
176
  /** Redaction paths applied to tool_args BEFORE sending. */
149
177
  redact?: readonly string[];
178
+ /**
179
+ * When true, a `redact` path that matches no field throws
180
+ * PliuzRedactionPathError instead of being a silent no-op — fail loud so a
181
+ * typo'd path never leaks the field you meant to redact. Default: false.
182
+ */
183
+ redactStrict?: boolean;
150
184
  /** Override the tool name. Default: `fn.name` (may be empty for anonymous). */
151
185
  toolName?: string;
152
186
  /**
@@ -167,6 +201,14 @@ interface GatedOptions<TArgs extends readonly unknown[]> {
167
201
  sessionId?: string;
168
202
  /** Optional originator (default: { type: 'system' }). */
169
203
  originator?: Originator;
204
+ /**
205
+ * Map human-EDITED args (approval decided with action='edit') back onto
206
+ * the wrapped function's argument tuple. Required to honor edits when a
207
+ * custom `toolArgs` mapper is used (the SDK cannot invert an arbitrary
208
+ * mapper). Without it, an edited approval throws
209
+ * PliuzEditNotApplicableError instead of executing the original args.
210
+ */
211
+ applyFinalArgs?: (finalArgs: Record<string, unknown>, originalArgs: TArgs) => TArgs;
170
212
  }
171
213
  /**
172
214
  * Wrap a function so every call is gated by Pliuz.
@@ -176,4 +218,4 @@ interface GatedOptions<TArgs extends readonly unknown[]> {
176
218
  */
177
219
  declare function gated<TArgs extends readonly unknown[], TResult>(options: GatedOptions<TArgs>, fn: (...args: TArgs) => Promise<TResult> | TResult): (...args: TArgs) => Promise<TResult>;
178
220
 
179
- export { DEFAULT_GATED_POLL_INTERVAL_MS, DEFAULT_GATED_TIMEOUT_MS, type GatedOptions, Originator, PliuzApiError, PliuzApprovalExpiredError, PliuzApprovalTimeoutError, PliuzAuthError, PliuzClient, PliuzConflictError, PliuzError, PliuzForbiddenError, PliuzNetworkError, PliuzNotFoundError, PliuzPolicyError, PliuzRateLimitError, PliuzRejectedError, PliuzServerError, PliuzTimeoutError, PliuzValidationError, REDACTED_PLACEHOLDER, VERSION, applyRedaction, errorForStatus, gated };
221
+ export { DEFAULT_GATED_POLL_INTERVAL_MS, DEFAULT_GATED_TIMEOUT_MS, type GatedOptions, Originator, PliuzApiError, PliuzApprovalExpiredError, PliuzApprovalTimeoutError, PliuzAuthError, PliuzClient, PliuzConflictError, PliuzEditNotApplicableError, PliuzError, PliuzForbiddenError, PliuzNetworkError, PliuzNotFoundError, PliuzPolicyError, PliuzRateLimitError, PliuzRedactionPathError, PliuzRejectedError, PliuzServerError, PliuzTimeoutError, PliuzValidationError, REDACTED_PLACEHOLDER, VERSION, applyRedaction, errorForStatus, gated };
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { createHash } from 'crypto';
2
2
 
3
3
  // src/version.ts
4
- var VERSION = "0.1.0";
4
+ var VERSION = "0.2.2";
5
5
 
6
6
  // src/errors.ts
7
7
  var PliuzError = class extends Error {
@@ -20,6 +20,17 @@ var PliuzNetworkError = class extends PliuzError {
20
20
  }
21
21
  cause;
22
22
  };
23
+ var PliuzRedactionPathError = class extends PliuzError {
24
+ constructor(path) {
25
+ super(
26
+ `redaction path '${path}' did not match any field in the payload (strict mode). Fix the path or set redactStrict=false to ignore misses.`
27
+ );
28
+ this.path = path;
29
+ this.name = "PliuzRedactionPathError";
30
+ Object.setPrototypeOf(this, new.target.prototype);
31
+ }
32
+ path;
33
+ };
23
34
  var PliuzTimeoutError = class extends PliuzError {
24
35
  constructor(message) {
25
36
  super(message);
@@ -130,6 +141,19 @@ var PliuzApprovalTimeoutError = class extends PliuzError {
130
141
  approvalId;
131
142
  timeoutMs;
132
143
  };
144
+ var PliuzEditNotApplicableError = class extends PliuzError {
145
+ constructor(approvalId, finalArgs) {
146
+ super(
147
+ `approval ${approvalId} was approved with edited args, but they cannot be mapped back to the wrapped function (custom toolArgs mapper without applyFinalArgs, or incompatible shape) \u2014 refusing to execute the original (unapproved) args. Apply e.finalArgs manually.`
148
+ );
149
+ this.approvalId = approvalId;
150
+ this.finalArgs = finalArgs;
151
+ this.name = "PliuzEditNotApplicableError";
152
+ Object.setPrototypeOf(this, new.target.prototype);
153
+ }
154
+ approvalId;
155
+ finalArgs;
156
+ };
133
157
  var STATUS_TO_ERROR = {
134
158
  400: PliuzValidationError,
135
159
  401: PliuzAuthError,
@@ -236,7 +260,7 @@ async function request(opts) {
236
260
  }
237
261
 
238
262
  // src/client.ts
239
- var DEFAULT_BASE_URL = "https://pliuz-dev.vercel.app";
263
+ var DEFAULT_BASE_URL = "https://pliuz.com";
240
264
  var DEFAULT_TIMEOUT_MS = 3e4;
241
265
  var DEFAULT_MAX_RETRIES = 3;
242
266
  var ENV_API_KEY = "PLIUZ_API_KEY";
@@ -261,6 +285,7 @@ function approvalUrl(baseUrl, approvalId, suffix) {
261
285
  if (suffix) path += `/${suffix}`;
262
286
  return baseUrl + path;
263
287
  }
288
+ var MAX_WAIT_SECONDS = 25;
264
289
  var PliuzClient = class {
265
290
  apiKey;
266
291
  baseUrl;
@@ -310,13 +335,22 @@ var PliuzClient = class {
310
335
  /**
311
336
  * Fetch the current state of an approval request.
312
337
  * Used by polling in `gated()` to detect status transitions.
338
+ *
339
+ * Pass `waitSeconds` (1..25) to long-poll: the server holds the request
340
+ * open and returns the instant the approval stops being `pending`, or after
341
+ * `waitSeconds` (still pending). Cuts polling volume and decision latency
342
+ * ~12x vs fixed-interval polling. The per-request HTTP timeout is bumped to
343
+ * cover the wait window.
313
344
  */
314
- async getApproval(approvalId) {
345
+ async getApproval(approvalId, waitSeconds) {
346
+ const wait = waitSeconds && waitSeconds > 0 ? Math.min(Math.floor(waitSeconds), MAX_WAIT_SECONDS) : 0;
347
+ const url = wait > 0 ? `${approvalUrl(this.baseUrl, approvalId)}?wait=${wait}` : approvalUrl(this.baseUrl, approvalId);
315
348
  return request({
316
349
  method: "GET",
317
- url: approvalUrl(this.baseUrl, approvalId),
350
+ url,
318
351
  apiKey: this.apiKey,
319
- timeoutMs: this.timeoutMs,
352
+ // Give the socket head-room over the server-side wait window.
353
+ timeoutMs: wait > 0 ? Math.max(this.timeoutMs, wait * 1e3 + 5e3) : this.timeoutMs,
320
354
  maxRetries: this.maxRetries,
321
355
  fetchImpl: this.fetchImpl
322
356
  });
@@ -349,13 +383,16 @@ var PliuzClient = class {
349
383
 
350
384
  // src/redaction.ts
351
385
  var REDACTED_PLACEHOLDER = "<redacted>";
352
- function applyRedaction(payload, paths) {
386
+ function applyRedaction(payload, paths, strict = false) {
353
387
  if (!paths || paths.length === 0) {
354
388
  return deepClone(payload);
355
389
  }
356
390
  const result = deepClone(payload);
357
391
  for (const path of paths) {
358
- redactPath(result, parsePath(path));
392
+ const matched = redactPath(result, parsePath(path));
393
+ if (strict && !matched) {
394
+ throw new PliuzRedactionPathError(path);
395
+ }
359
396
  }
360
397
  return result;
361
398
  }
@@ -383,28 +420,30 @@ function parsePath(path) {
383
420
  return segments;
384
421
  }
385
422
  function redactPath(node, segments) {
386
- if (segments.length === 0) return;
387
- if (!isPlainObject(node)) return;
423
+ if (segments.length === 0) return true;
424
+ if (!isPlainObject(node)) return false;
388
425
  const [head, ...rest] = segments;
389
- if (!head) return;
426
+ if (!head) return false;
390
427
  const { key, isArrayWildcard } = head;
391
- if (!(key in node)) return;
428
+ if (!(key in node)) return false;
392
429
  if (isArrayWildcard) {
393
430
  const target = node[key];
394
- if (!Array.isArray(target)) return;
431
+ if (!Array.isArray(target)) return false;
395
432
  if (rest.length === 0) {
396
433
  node[key] = target.map(() => REDACTED_PLACEHOLDER);
397
- return;
434
+ return true;
398
435
  }
436
+ let matched = target.length > 0;
399
437
  for (const item of target) {
400
- redactPath(item, rest);
438
+ if (!redactPath(item, rest)) matched = false;
401
439
  }
440
+ return matched;
402
441
  } else {
403
442
  if (rest.length === 0) {
404
443
  node[key] = REDACTED_PLACEHOLDER;
405
- } else {
406
- redactPath(node[key], rest);
444
+ return true;
407
445
  }
446
+ return redactPath(node[key], rest);
408
447
  }
409
448
  }
410
449
  function isPlainObject(v) {
@@ -430,8 +469,8 @@ function gated(options, fn) {
430
469
  return async (...args) => {
431
470
  const activeClient = options.client ?? new PliuzClient();
432
471
  const rawArgs = toolArgsMapper(...args);
433
- const sendArgs = redactPaths && redactPaths.length > 0 ? applyRedaction(rawArgs, redactPaths) : rawArgs;
434
- const idempotencyKey = idempotencyKeyFor(toolName, sendArgs, options.sessionId);
472
+ const sendArgs = redactPaths && redactPaths.length > 0 ? applyRedaction(rawArgs, redactPaths, options.redactStrict ?? false) : rawArgs;
473
+ const idempotencyKey = idempotencyKeyFor(toolName, rawArgs, options.sessionId);
435
474
  const metadata = {};
436
475
  if (options.policy) metadata.policy = options.policy;
437
476
  const response = await activeClient.createApproval({
@@ -450,27 +489,38 @@ function gated(options, fn) {
450
489
  if (TERMINAL_EXPIRED.has(response.status)) {
451
490
  throw new PliuzApprovalExpiredError(approvalId);
452
491
  }
492
+ let approval = null;
453
493
  if (!TERMINAL_OK.has(response.status)) {
454
494
  const deadline = Date.now() + timeoutMs;
455
495
  while (true) {
456
- if (Date.now() >= deadline) {
496
+ const remainingMs = deadline - Date.now();
497
+ if (remainingMs <= 0) {
457
498
  throw new PliuzApprovalTimeoutError(approvalId, timeoutMs);
458
499
  }
459
- await sleep2(pollIntervalMs);
460
- const approval = await activeClient.getApproval(approvalId);
500
+ const waitSeconds = Math.max(1, Math.ceil(Math.min(remainingMs, pollIntervalMs * 1e3) / 1e3));
501
+ const callStart = Date.now();
502
+ approval = await activeClient.getApproval(approvalId, waitSeconds);
461
503
  if (terminalOrThrow(approval)) break;
504
+ if (Date.now() - callStart < 250) await sleep2(pollIntervalMs);
462
505
  }
506
+ } else if (response.is_replay) {
507
+ approval = await activeClient.getApproval(approvalId);
508
+ }
509
+ let callArgs = args;
510
+ const finalArgs = approval?.final_args ?? null;
511
+ if (finalArgs) {
512
+ callArgs = resolveEditedArgs(approvalId, rawArgs, finalArgs, args, options);
463
513
  }
464
514
  const started = Date.now();
465
515
  try {
466
- const result = await fn(...args);
516
+ const result = await fn(...callArgs);
467
517
  const latencyMs = Date.now() - started;
468
- void safeReport(activeClient, approvalId, "success", null, latencyMs, result);
518
+ await safeReport(activeClient, approvalId, "success", null, latencyMs, result, redactPaths);
469
519
  return result;
470
520
  } catch (e) {
471
521
  const latencyMs = Date.now() - started;
472
522
  const errMsg = e instanceof Error ? e.message : String(e);
473
- void safeReport(activeClient, approvalId, "error", errMsg.slice(0, 2e3), latencyMs, null);
523
+ await safeReport(activeClient, approvalId, "error", errMsg.slice(0, 2e3), latencyMs, null, redactPaths);
474
524
  throw e;
475
525
  }
476
526
  };
@@ -494,6 +544,30 @@ function canonicalJSON(value) {
494
544
  const keys = Object.keys(obj).sort();
495
545
  return "{" + keys.map((k) => JSON.stringify(k) + ":" + canonicalJSON(obj[k])).join(",") + "}";
496
546
  }
547
+ function mergeFinalArgs(raw, final) {
548
+ if (isPlainRecord(raw) && isPlainRecord(final)) {
549
+ const out = {};
550
+ for (const [k, v] of Object.entries(final)) {
551
+ out[k] = k in raw ? mergeFinalArgs(raw[k], v) : v;
552
+ }
553
+ return out;
554
+ }
555
+ if (final === REDACTED_PLACEHOLDER) return raw;
556
+ return final;
557
+ }
558
+ function isPlainRecord(v) {
559
+ return typeof v === "object" && v !== null && !Array.isArray(v);
560
+ }
561
+ function resolveEditedArgs(approvalId, rawArgs, finalArgs, originalArgs, options) {
562
+ const merged = mergeFinalArgs(rawArgs, finalArgs);
563
+ if (options.applyFinalArgs) {
564
+ return options.applyFinalArgs(merged, originalArgs);
565
+ }
566
+ if (options.toolArgs === void 0 && Array.isArray(merged.args)) {
567
+ return merged.args;
568
+ }
569
+ throw new PliuzEditNotApplicableError(approvalId, merged);
570
+ }
497
571
  function terminalOrThrow(approval) {
498
572
  if (TERMINAL_OK.has(approval.status)) return true;
499
573
  if (TERMINAL_REJECT.has(approval.status)) {
@@ -504,13 +578,14 @@ function terminalOrThrow(approval) {
504
578
  }
505
579
  return false;
506
580
  }
507
- async function safeReport(client, approvalId, status, errorMessage, latencyMs, result) {
581
+ async function safeReport(client, approvalId, status, errorMessage, latencyMs, result, redactPaths) {
508
582
  let excerpt = null;
509
583
  if (result != null) {
584
+ const toDump = redactPaths && redactPaths.length > 0 && isPlainRecord(result) ? applyRedaction(result, redactPaths) : result;
510
585
  try {
511
- excerpt = JSON.stringify(result).slice(0, 2e3);
586
+ excerpt = JSON.stringify(toDump).slice(0, 2e3);
512
587
  } catch {
513
- excerpt = String(result).slice(0, 2e3);
588
+ excerpt = String(toDump).slice(0, 2e3);
514
589
  }
515
590
  }
516
591
  try {
@@ -527,6 +602,6 @@ function sleep2(ms) {
527
602
  return new Promise((resolve) => setTimeout(resolve, ms));
528
603
  }
529
604
 
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 };
605
+ 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, PliuzEditNotApplicableError, PliuzError, PliuzForbiddenError, PliuzNetworkError, PliuzNotFoundError, PliuzPolicyError, PliuzRateLimitError, PliuzRedactionPathError, PliuzRejectedError, PliuzServerError, PliuzTimeoutError, PliuzValidationError, REDACTED_PLACEHOLDER, VERSION, applyRedaction, errorForStatus, gated };
531
606
  //# sourceMappingURL=index.js.map
532
607
  //# sourceMappingURL=index.js.map