@jini-ai/core 0.3.0 → 0.4.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.
Files changed (96) hide show
  1. package/CHANGELOG.md +131 -0
  2. package/README.md +154 -14
  3. package/dist/agent-tools.d.ts +29 -0
  4. package/dist/agent-tools.js +2 -0
  5. package/dist/api-token-auth.d.ts +12 -5
  6. package/dist/api-token-auth.js +7 -7
  7. package/dist/bindings.d.ts +16 -5
  8. package/dist/bindings.js +7 -6
  9. package/dist/composition.d.ts +41 -0
  10. package/dist/composition.js +39 -0
  11. package/dist/contribution-registry.d.ts +16 -0
  12. package/dist/contribution-registry.js +12 -0
  13. package/dist/daemon.d.ts +12 -7
  14. package/dist/daemon.js +21 -9
  15. package/dist/gated-mutations/actor-identity.d.ts +20 -0
  16. package/dist/gated-mutations/actor-identity.js +31 -0
  17. package/dist/gated-mutations/gateway.d.ts +115 -0
  18. package/dist/gated-mutations/gateway.js +123 -0
  19. package/dist/gated-mutations/ports.d.ts +27 -0
  20. package/dist/gated-mutations/ports.js +2 -0
  21. package/dist/gated-mutations/token.d.ts +95 -0
  22. package/dist/gated-mutations/token.js +87 -0
  23. package/dist/gated-mutations.d.ts +5 -0
  24. package/dist/gated-mutations.js +5 -0
  25. package/dist/index.d.ts +9 -3
  26. package/dist/index.js +6 -1
  27. package/dist/model-facing-tool-errors.d.ts +37 -0
  28. package/dist/model-facing-tool-errors.js +71 -0
  29. package/dist/naming.d.ts +31 -0
  30. package/dist/naming.js +33 -0
  31. package/dist/origin-validation.d.ts +64 -19
  32. package/dist/origin-validation.js +43 -33
  33. package/dist/pack-lifecycle.d.ts +19 -12
  34. package/dist/pack-lifecycle.js +14 -14
  35. package/dist/pack.d.ts +41 -13
  36. package/dist/pack.js +2 -2
  37. package/dist/primitives/http.d.ts +57 -0
  38. package/dist/primitives/http.js +2 -0
  39. package/dist/primitives/ids.d.ts +8 -0
  40. package/dist/primitives/ids.js +5 -0
  41. package/dist/primitives/index.d.ts +8 -0
  42. package/dist/primitives/index.js +8 -0
  43. package/dist/primitives/json.d.ts +10 -0
  44. package/dist/primitives/json.js +2 -0
  45. package/dist/primitives/logger.d.ts +26 -0
  46. package/dist/primitives/logger.js +17 -0
  47. package/dist/primitives/paths.d.ts +16 -0
  48. package/dist/primitives/paths.js +38 -0
  49. package/dist/primitives/result.d.ts +9 -0
  50. package/dist/primitives/result.js +2 -0
  51. package/dist/primitives/time.d.ts +17 -0
  52. package/dist/primitives/time.js +13 -0
  53. package/dist/redact.d.ts +43 -8
  54. package/dist/redact.js +121 -50
  55. package/dist/registration-kit.d.ts +323 -0
  56. package/dist/registration-kit.js +396 -0
  57. package/dist/text/index.d.ts +3 -0
  58. package/dist/text/index.js +2 -0
  59. package/dist/text/sanitize-untrusted-text.d.ts +22 -0
  60. package/dist/text/sanitize-untrusted-text.js +108 -0
  61. package/dist/token.d.ts +29 -2
  62. package/dist/token.js +22 -2
  63. package/dist/tool-registry.d.ts +60 -14
  64. package/dist/tool-registry.js +24 -9
  65. package/dist/tool-tokens.js +1 -1
  66. package/package.json +51 -7
  67. package/dist/api-token-auth.d.ts.map +0 -1
  68. package/dist/api-token-auth.js.map +0 -1
  69. package/dist/bindings.d.ts.map +0 -1
  70. package/dist/bindings.js.map +0 -1
  71. package/dist/compose.typecheck.d.ts +0 -2
  72. package/dist/compose.typecheck.d.ts.map +0 -1
  73. package/dist/compose.typecheck.js +0 -29
  74. package/dist/compose.typecheck.js.map +0 -1
  75. package/dist/daemon.d.ts.map +0 -1
  76. package/dist/daemon.js.map +0 -1
  77. package/dist/index.d.ts.map +0 -1
  78. package/dist/index.js.map +0 -1
  79. package/dist/internal.d.ts.map +0 -1
  80. package/dist/internal.js.map +0 -1
  81. package/dist/origin-validation.d.ts.map +0 -1
  82. package/dist/origin-validation.js.map +0 -1
  83. package/dist/pack-lifecycle.d.ts.map +0 -1
  84. package/dist/pack-lifecycle.js.map +0 -1
  85. package/dist/pack.d.ts.map +0 -1
  86. package/dist/pack.js.map +0 -1
  87. package/dist/principal.d.ts.map +0 -1
  88. package/dist/principal.js.map +0 -1
  89. package/dist/redact.d.ts.map +0 -1
  90. package/dist/redact.js.map +0 -1
  91. package/dist/token.d.ts.map +0 -1
  92. package/dist/token.js.map +0 -1
  93. package/dist/tool-registry.d.ts.map +0 -1
  94. package/dist/tool-registry.js.map +0 -1
  95. package/dist/tool-tokens.d.ts.map +0 -1
  96. package/dist/tool-tokens.js.map +0 -1
package/dist/pack.d.ts CHANGED
@@ -26,8 +26,12 @@ import type { AnyToken, ManyToken, Token } from './token.js';
26
26
  * silently falling through to a global container.
27
27
  */
28
28
  export interface PackContainer {
29
- get<T>(t: Token<T, string>): T;
30
- getMany<T>(t: ManyToken<T, string>): T[];
29
+ get<T>(required: {
30
+ token: Token<T, string>;
31
+ }): T;
32
+ getMany<T>(required: {
33
+ token: ManyToken<T, string>;
34
+ }): T[];
31
35
  }
32
36
  export interface Pack<Deps extends readonly AnyToken<unknown, string>[] = readonly AnyToken<unknown, string>[], Services = unknown, Name extends string = string> {
33
37
  readonly name: Name;
@@ -35,30 +39,54 @@ export interface Pack<Deps extends readonly AnyToken<unknown, string>[] = readon
35
39
  readonly services: (c: PackContainer) => Services;
36
40
  /**
37
41
  * `{descriptor, handler, policy}` triples this pack contributes to the composition's shared
38
- * `ToolRegistry`. Called exactly once per composed pack, before any transport is mounted, so a
39
- * pack's routes can assume its own tools are already registered.
42
+ * `ToolRegistry`. Composition roots must call `registerPackTools` exactly once per composed pack,
43
+ * before mounting any transport, so a pack's routes can assume its tools are already registered.
44
+ * This interface and `createDaemon` do not enforce that orchestration; repeated registration
45
+ * invokes the contribution again and duplicate tool IDs are rejected by the registry.
40
46
  *
41
47
  * Atomic with `http`/`cli` by construction: a pack that is not composed contributes neither its
42
48
  * tools nor its routes. See this module's own doc for the failure mode that makes this
43
49
  * load-bearing rather than a convenience.
44
50
  */
45
- readonly tools?: (services: Services) => readonly ToolRegistration[];
46
- readonly http?: (app: unknown, services: Services) => void;
47
- readonly cli?: (reg: unknown, services: Services) => void;
51
+ readonly tools?: (required: {
52
+ services: Services;
53
+ }) => readonly ToolRegistration[];
54
+ readonly http?: (required: {
55
+ app: unknown;
56
+ services: Services;
57
+ }) => void;
58
+ readonly cli?: (required: {
59
+ reg: unknown;
60
+ services: Services;
61
+ }) => void;
48
62
  /**
49
63
  * Releases whatever this pack's `services` acquired (a pty manager, a database handle, an OAuth
50
64
  * callback listener). Composition roots call these in reverse composition order, best-effort:
51
65
  * one pack's failure to dispose must never prevent another's from running.
52
66
  */
53
- readonly dispose?: (services: Services) => Promise<void> | void;
67
+ readonly dispose?: (required: {
68
+ services: Services;
69
+ }) => Promise<void> | void;
70
+ }
71
+ export interface PackContributions<Services> {
72
+ tools?: (required: {
73
+ services: Services;
74
+ }) => readonly ToolRegistration[];
75
+ http?: (required: {
76
+ app: unknown;
77
+ services: Services;
78
+ }) => void;
79
+ cli?: (required: {
80
+ reg: unknown;
81
+ services: Services;
82
+ }) => void;
83
+ dispose?: (required: {
84
+ services: Services;
85
+ }) => Promise<void> | void;
54
86
  }
55
87
  export declare function definePack<const Name extends string, const Deps extends readonly AnyToken<unknown, string>[], Services>(def: {
56
88
  name: Name;
57
89
  deps: Deps;
58
90
  services: (c: PackContainer) => Services;
59
- tools?: (services: Services) => readonly ToolRegistration[];
60
- http?: (app: unknown, services: Services) => void;
61
- cli?: (reg: unknown, services: Services) => void;
62
- dispose?: (services: Services) => Promise<void> | void;
63
- }): Pack<Deps, Services, Name>;
91
+ }, optional?: PackContributions<Services>): Pack<Deps, Services, Name>;
64
92
  //# sourceMappingURL=pack.d.ts.map
package/dist/pack.js CHANGED
@@ -1,4 +1,4 @@
1
- export function definePack(def) {
2
- return def;
1
+ export function definePack(def, optional = {}) {
2
+ return { ...def, ...optional };
3
3
  }
4
4
  //# sourceMappingURL=pack.js.map
@@ -0,0 +1,57 @@
1
+ /** One shared contract keeps binary, mail and webhook consumers behind the same egress guard.
2
+ * A guarded request requires a socket idle budget; total elapsed time is opt-in. */
3
+ export type HttpRequest = {
4
+ /** HEAD supports certificate/header diagnostics without downloading the page body. */
5
+ method: "POST" | "GET" | "HEAD" | "PUT" | "PATCH" | "DELETE";
6
+ url: string;
7
+ headers: Readonly<Record<string, string>>;
8
+ /** Preserve the signed bytes: re-serialization between signing and sending changes the signature. */
9
+ body?: string;
10
+ /** Whole-operation deadline, including DNS, redirects and body reads. Off by default.
11
+ * Retry/backoff belongs to the caller; its deadline can cover the whole retry operation. */
12
+ totalDeadlineMs?: number;
13
+ /** Bound the whole operation, including DNS and body reads, beyond the per-attempt timeout. */
14
+ signal?: AbortSignal;
15
+ /** Caller byte ceilings are bounded by the client policy, never a way to widen its cap. */
16
+ maxResponseBytes?: number;
17
+ } & ({
18
+ idleTimeoutMs: number; /** Legacy alias for the socket idle budget. */
19
+ timeoutMs?: number;
20
+ } | {
21
+ timeoutMs: number;
22
+ idleTimeoutMs?: number;
23
+ });
24
+ export interface HttpResponse {
25
+ status: number;
26
+ headers: Readonly<Record<string, string>>;
27
+ /** Bounded by the egress cap: a producer must not expose an unlimited text body through this port. */
28
+ bodyText: string;
29
+ /** Keep individual lines: Expires contains commas, so comma-splitting loses cookie boundaries. */
30
+ setCookies?: readonly string[];
31
+ /** UTF-8 decoding replaces invalid sequences and corrupts binary payloads. Expose the decompressed
32
+ * transport bytes here so binary consumers need not bypass DNS checks, peer pinning or redirects.
33
+ * Optional for older adapters/doubles; absence does not imply an empty payload. */
34
+ bodyBytes?: Uint8Array;
35
+ /** Coarse OR of text/byte clipping. A clipped image is corrupt, not merely smaller. Lossy UTF-8
36
+ * can expand invalid bytes into three-byte replacement characters and clip text even when all
37
+ * bytes fit, so binary consumers must use bodyBytesTruncated. Absence is an unknown verdict. */
38
+ bodyTruncated?: boolean;
39
+ /** Byte-specific completeness. A producer's unshaped truncation must count against both shapes
40
+ * rather than silently declaring bytes complete. Absent when no byte payload was supplied. */
41
+ bodyBytesTruncated?: boolean;
42
+ /** Provenance must name the guarded hop that supplied these bytes, not the requested URL or an
43
+ * unfollowed Location. Every followed hop is re-vetted. Older producers may omit this field,
44
+ * in which case consumers fall back to the request URL rather than inventing a blank source. */
45
+ finalUrl?: string;
46
+ }
47
+ export type RequestRedirect = "follow" | "error" | "manual";
48
+ /** Consumers receive the policy-enforcing client; the raw transport is a composition seam.
49
+ * Keeping construction at the host boundary prevents consumers from bypassing egress checks. */
50
+ export interface HttpClientPort {
51
+ send(required: {
52
+ request: HttpRequest;
53
+ }, optional?: {
54
+ redirect?: RequestRedirect;
55
+ }): Promise<HttpResponse>;
56
+ }
57
+ //# sourceMappingURL=http.d.ts.map
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=http.js.map
@@ -0,0 +1,8 @@
1
+ /** Unbranded identifier; UUID-producing adapters and opaque host IDs share this port. */
2
+ export type UUID = string;
3
+ export interface IdGenerator {
4
+ newId(): string;
5
+ }
6
+ /** Uses the host's cryptographic UUID generator; no insecure random fallback. */
7
+ export declare function createRandomUuidGenerator(): IdGenerator;
8
+ //# sourceMappingURL=ids.d.ts.map
@@ -0,0 +1,5 @@
1
+ /** Uses the host's cryptographic UUID generator; no insecure random fallback. */
2
+ export function createRandomUuidGenerator() {
3
+ return { newId: () => globalThis.crypto.randomUUID() };
4
+ }
5
+ //# sourceMappingURL=ids.js.map
@@ -0,0 +1,8 @@
1
+ export * from './time.js';
2
+ export * from './ids.js';
3
+ export * from './json.js';
4
+ export * from './http.js';
5
+ export * from './logger.js';
6
+ export * from './result.js';
7
+ export * from './paths.js';
8
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,8 @@
1
+ export * from './time.js';
2
+ export * from './ids.js';
3
+ export * from './json.js';
4
+ export * from './http.js';
5
+ export * from './logger.js';
6
+ export * from './result.js';
7
+ export * from './paths.js';
8
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,10 @@
1
+ /** JSON wire values shared by contracts and domain packages. */
2
+ export type JsonPrimitive = string | number | boolean | null;
3
+ export type JsonValue = JsonPrimitive | JsonValue[] | {
4
+ [key: string]: JsonValue;
5
+ };
6
+ export type JsonObject = {
7
+ [key: string]: JsonValue;
8
+ };
9
+ export type JsonArray = JsonValue[];
10
+ //# sourceMappingURL=json.d.ts.map
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=json.js.map
@@ -0,0 +1,26 @@
1
+ /** Host-independent logging contract: diagnostics travel as optional structured data. */
2
+ export interface Logger {
3
+ info(required: {
4
+ message: string;
5
+ }, optional?: {
6
+ meta?: Record<string, unknown>;
7
+ error?: unknown;
8
+ }): void;
9
+ warn(required: {
10
+ message: string;
11
+ }, optional?: {
12
+ meta?: Record<string, unknown>;
13
+ error?: unknown;
14
+ }): void;
15
+ error(required: {
16
+ message: string;
17
+ }, optional?: {
18
+ meta?: Record<string, unknown>;
19
+ error?: unknown;
20
+ }): void;
21
+ }
22
+ /** Minimal console adapter with a required host-owned prefix and structured metadata. */
23
+ export declare function createConsoleLogger({ prefix }: {
24
+ prefix: string;
25
+ }): Logger;
26
+ //# sourceMappingURL=logger.d.ts.map
@@ -0,0 +1,17 @@
1
+ /** Minimal console adapter with a required host-owned prefix and structured metadata. */
2
+ export function createConsoleLogger({ prefix }) {
3
+ const write = (level, message, optional) => {
4
+ const args = [prefix ? `${prefix} ${message}` : message];
5
+ if (optional.meta !== undefined)
6
+ args.push(optional.meta);
7
+ if (optional.error !== undefined)
8
+ args.push(optional.error);
9
+ console[level](...args);
10
+ };
11
+ return {
12
+ info: ({ message }, optional = {}) => write('info', message, optional),
13
+ warn: ({ message }, optional = {}) => write('warn', message, optional),
14
+ error: ({ message }, optional = {}) => write('error', message, optional),
15
+ };
16
+ }
17
+ //# sourceMappingURL=logger.js.map
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Check lexical containment using whole directory segments, including equality.
3
+ * Hosts resolve cwd-relative paths and symlinks before calling: checking a textual
4
+ * prefix alone can permit traversal or a symlink escape. Keeping that effect outside
5
+ * this helper makes the kernel browser-safe. Windows hosts select case-insensitive
6
+ * comparison and backslash separators; drive roots and UNC share roots remain distinct.
7
+ * POSIX backslashes remain literal characters, so distinct directories cannot alias.
8
+ */
9
+ export declare function pathContains({ root, target }: {
10
+ root: string;
11
+ target: string;
12
+ }, { caseSensitive, separator }?: {
13
+ caseSensitive?: boolean;
14
+ separator?: '/' | '\\';
15
+ }): boolean;
16
+ //# sourceMappingURL=paths.d.ts.map
@@ -0,0 +1,38 @@
1
+ /** Pure lexical normalization of an already absolute path; no cwd, filesystem or symlink access. */
2
+ function normalizeAbsolutePath(value, separator) {
3
+ const path = separator === '\\' ? value.replace(/\\/g, '/') : value;
4
+ const drive = separator === '\\' ? /^[A-Za-z]:\//.exec(path)?.[0] : undefined;
5
+ const unc = separator === '\\' ? /^\/\/[^/]+\/[^/]+(?:\/|$)/.exec(path)?.[0] : undefined;
6
+ const prefix = drive ?? unc ?? (path.startsWith('/') ? '/' : undefined);
7
+ if (!prefix)
8
+ throw new RangeError('pathContains requires resolved absolute paths');
9
+ const segments = [];
10
+ for (const segment of path.slice(prefix.length).split('/')) {
11
+ if (!segment || segment === '.')
12
+ continue;
13
+ if (segment === '..')
14
+ segments.pop();
15
+ else
16
+ segments.push(segment);
17
+ }
18
+ return prefix.replace(/\/+$/, '') + '/' + segments.join('/');
19
+ }
20
+ /**
21
+ * Check lexical containment using whole directory segments, including equality.
22
+ * Hosts resolve cwd-relative paths and symlinks before calling: checking a textual
23
+ * prefix alone can permit traversal or a symlink escape. Keeping that effect outside
24
+ * this helper makes the kernel browser-safe. Windows hosts select case-insensitive
25
+ * comparison and backslash separators; drive roots and UNC share roots remain distinct.
26
+ * POSIX backslashes remain literal characters, so distinct directories cannot alias.
27
+ */
28
+ export function pathContains({ root, target }, { caseSensitive = true, separator = '/' } = {}) {
29
+ const normalize = (value) => {
30
+ const path = normalizeAbsolutePath(value, separator);
31
+ return caseSensitive ? path : path.toLowerCase();
32
+ };
33
+ const normalizedRoot = normalize(root);
34
+ const normalizedTarget = normalize(target);
35
+ const boundary = normalizedRoot.endsWith('/') ? normalizedRoot : normalizedRoot + '/';
36
+ return normalizedTarget === normalizedRoot || normalizedTarget.startsWith(boundary);
37
+ }
38
+ //# sourceMappingURL=paths.js.map
@@ -0,0 +1,9 @@
1
+ /** Explicit success/failure result without exceptions or domain-specific error assumptions. */
2
+ export type Result<T, E = Error> = {
3
+ ok: true;
4
+ value: T;
5
+ } | {
6
+ ok: false;
7
+ error: E;
8
+ };
9
+ //# sourceMappingURL=result.d.ts.map
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=result.js.map
@@ -0,0 +1,17 @@
1
+ /** Unbranded wire timestamp: hosts can share clocks without conversion adapters. */
2
+ export type ISODateTime = string;
3
+ /** Milliseconds since the Unix epoch. Zero-argument getters need no empty argument object. */
4
+ export interface Clock {
5
+ nowMs(): number;
6
+ }
7
+ /** Default wall clock; inject a Clock to control time in application code. */
8
+ export declare function createSystemClock(): Clock;
9
+ /** Converts epoch milliseconds using the standard UTC ISO representation. Invalid time throws. */
10
+ export declare function toIsoDateTime({ epochMs }: {
11
+ epochMs: number;
12
+ }): ISODateTime;
13
+ /** Samples the supplied clock once and formats its time in UTC. */
14
+ export declare function nowIso({ clock }: {
15
+ clock: Clock;
16
+ }): ISODateTime;
17
+ //# sourceMappingURL=time.d.ts.map
@@ -0,0 +1,13 @@
1
+ /** Default wall clock; inject a Clock to control time in application code. */
2
+ export function createSystemClock() {
3
+ return { nowMs: () => Date.now() };
4
+ }
5
+ /** Converts epoch milliseconds using the standard UTC ISO representation. Invalid time throws. */
6
+ export function toIsoDateTime({ epochMs }) {
7
+ return new Date(epochMs).toISOString();
8
+ }
9
+ /** Samples the supplied clock once and formats its time in UTC. */
10
+ export function nowIso({ clock }) {
11
+ return toIsoDateTime({ epochMs: clock.nowMs() });
12
+ }
13
+ //# sourceMappingURL=time.js.map
package/dist/redact.d.ts CHANGED
@@ -1,19 +1,23 @@
1
1
  /**
2
2
  * @module redact
3
3
  *
4
- * Lexical secret / PII scrubber for telemetry payloads. Verbatim port of
5
- * OD's `apps/daemon/src/redact.ts` — no product-identity strings or
6
- * hardcoded env vars in the original, so no genericization was needed.
4
+ * Lexical secret / PII scrubber for telemetry payloads. Originally a verbatim port of
5
+ * OD's daemon redactor — no product-identity strings or hardcoded env vars in the original,
6
+ * so no genericization was needed. The shared kernel now also covers the CLI/provider rule
7
+ * union and accepts explicit host secrets; the original categories and rationale stay here.
7
8
  *
8
9
  * Runs before any prompt or assistant text is sent to an external
9
- * observability sink. The patterns here are intentionally conservative:
10
+ * observability sink. The default policy is intentionally conservative:
10
11
  * each one matches a well-defined token shape with extremely low
11
12
  * false-positive rate (API keys have a fixed prefix, JWTs have the
12
13
  * "header.payload.signature" triple, credit-card matches go through a Luhn
13
14
  * check). What this file does NOT do — and any caller-facing copy must
14
15
  * reflect that — is detect names, addresses, business secrets, or anything
15
16
  * that requires understanding the meaning of the surrounding text. That's
16
- * an ML / LLM problem this module doesn't take on.
17
+ * an ML / LLM problem this module doesn't take on. Callers can explicitly opt
18
+ * into `policy: 'aggressive'` to also mask bare 20+ character opaque runs;
19
+ * that CLI-style heuristic trades identifier/path readability for broader masking
20
+ * and must never be used by diagnostics or secret-scan guards.
17
21
  *
18
22
  * Output format: every match is replaced by `[REDACTED:<kind>]` so a
19
23
  * reviewer reading a trace can see exactly which category fired without
@@ -27,21 +31,52 @@
27
31
  * - AWS access key shape: 'AKIA' + 16 uppercase alphanumerics.
28
32
  * - Luhn algorithm (credit cards): https://en.wikipedia.org/wiki/Luhn_algorithm
29
33
  */
34
+ /** Credential-shape vocabulary for secret-only redaction; no I/O or host dependencies. */
35
+ export interface SecretShapePattern {
36
+ readonly name: string;
37
+ readonly pattern: RegExp;
38
+ }
39
+ export declare const PEM_PATTERN_NAME = "PEM private key block";
40
+ export declare const SECRET_SHAPE_PATTERNS: readonly SecretShapePattern[];
41
+ export interface SecretRedactionPattern {
42
+ name: string;
43
+ regex: RegExp;
44
+ }
45
+ /** Optional caller-owned secrets and named lexical rules, without ambient credential lookup. */
46
+ export interface SecretRedactionOptions {
47
+ /** Conservative by default; aggressive also masks bare 20+ character opaque runs. */
48
+ policy?: 'conservative' | 'aggressive';
49
+ exactSecrets?: ReadonlyArray<string | undefined | null>;
50
+ extraPatterns?: readonly SecretRedactionPattern[];
51
+ }
52
+ export declare const LABELED_SECRET_RE: RegExp;
53
+ export declare const OPAQUE_TOKEN_RE: RegExp;
54
+ export declare const NON_SECRET_TOKEN_RE: RegExp;
30
55
  /**
31
56
  * Returns `input` with every recognised secret / PII pattern replaced by a
32
57
  * `[REDACTED:<kind>]` marker. Idempotent — re-running on already redacted
33
- * text only matches new tokens.
58
+ * text only matches new tokens. Defaults to conservative; `{ policy: 'aggressive' }`
59
+ * additionally masks bare 20+ character opaque runs. Exact secrets and extra patterns
60
+ * are honored under either policy.
34
61
  *
35
62
  * Empty / non-string input passes through unchanged so the caller can use
36
63
  * this as a no-op on optional fields.
37
64
  */
38
- export declare function redactSecrets(input: string): string;
65
+ export declare function redactSecrets({ input }: {
66
+ input: string;
67
+ }, options?: SecretRedactionOptions): string;
39
68
  /**
40
69
  * Same as {@link redactSecrets} but also returns per-category counts so the
41
70
  * caller can attach an audit summary to the trace metadata ('we stripped 2
42
71
  * keys + 1 email before send').
72
+ *
73
+ * Exact secrets run first, longest first: lexical rules or a shorter supplied secret must not
74
+ * split a known credential and leave a suffix behind. Opt-in aggressive opaque rules precede
75
+ * PII rules for the same reason (a numeric token can also contain phone/card candidates).
43
76
  */
44
- export declare function redactSecretsWithCounts(input: string): {
77
+ export declare function redactSecretsWithCounts({ input }: {
78
+ input: string;
79
+ }, options?: SecretRedactionOptions): {
45
80
  redacted: string;
46
81
  counts: Record<string, number>;
47
82
  };