agentfootprint 9.7.0 → 9.8.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.
@@ -0,0 +1,303 @@
1
+ /**
2
+ * fileObservability — the typed event stream, one JSON line per event, in a
3
+ * local file.
4
+ *
5
+ * The sink for the shop that has no collector. Every other adapter in this
6
+ * folder ships somewhere: CloudWatch, X-Ray, an OTLP endpoint. A great many
7
+ * on-premises deployments have none of those — they have a directory, a log
8
+ * shipper (Filebeat, Fluent Bit, Vector, `promtail`, `journald`, or a person
9
+ * with `grep`), and a rule that nothing leaves the network. NDJSON on disk is
10
+ * the format all of those already read, so the sink is the file.
11
+ *
12
+ * Zero dependencies: `node:fs`, lazily required at construction so merely
13
+ * importing `agentfootprint/observe` stays browser-safe (this factory is
14
+ * Node-only; calling it in a browser throws by name).
15
+ *
16
+ * ## The line
17
+ *
18
+ * One `JSON.stringify(event)` per line, newline-terminated, appended in
19
+ * dispatch order — the SAME envelope `cloudwatchObservability` puts in a log
20
+ * event, so a query written against one reads the other:
21
+ *
22
+ * ```jsonl
23
+ * {"type":"agentfootprint.agent.turn_start","payload":{…},"meta":{"runId":"…","sessionId":"…"}}
24
+ * {"type":"agentfootprint.stream.tool_end","payload":{…},"meta":{…}}
25
+ * ```
26
+ *
27
+ * Nothing is summarized, bounded or redacted on the way out. **A payload that
28
+ * must not be on that disk must not reach this strategy** — narrow it with
29
+ * `eventTypes` / `tier` / `sampleRate`, or apply a footprintjs
30
+ * `RedactionPolicy` upstream, exactly as with every other sink. (For a bounded
31
+ * record by construction, `auditExport({ payloadMode: 'bounded' })` is the
32
+ * adapter that does that job.)
33
+ *
34
+ * ## Buffered, not synchronous
35
+ *
36
+ * `exportEvent` is sync and never touches the disk: it serializes, buffers, and
37
+ * returns. Batches are appended asynchronously on a size trigger
38
+ * (`maxBufferEvents` / `maxBufferBytes`), on a timer (`flushIntervalMs`), and on
39
+ * `flush()`. A hard kill therefore loses at most the buffer — the price of not
40
+ * making telemetry a term in agent-loop latency. Call `flush()` (or
41
+ * `agent.shutdown()`, which does) at process end; see the 8.12.0 lifecycle laws
42
+ * on {@link BaseStrategy.flush}.
43
+ *
44
+ * ## Rotation is ONE generation, and that is deliberate
45
+ *
46
+ * With `maxBytes` set, a batch that would push the file past the ceiling first
47
+ * renames it to `<path>.1` — **replacing any previous `.1`** — and starts a
48
+ * fresh file. That is the whole policy. There is no `.2`, no compression, no
49
+ * time-based schedule, no cross-process coordination (two processes writing one
50
+ * file each keep their own byte count and will both rotate it). It exists so an
51
+ * unattended agent cannot fill a disk, and for nothing else: **retention is a
52
+ * log-management daemon's job**, and `logrotate` with `copytruncate`, Fluent Bit,
53
+ * or a systemd timer will do it properly. Omit `maxBytes` — the default — and
54
+ * this adapter never renames anything, which is the right choice when a real
55
+ * rotator already owns the file.
56
+ *
57
+ * @example
58
+ * ```ts
59
+ * import { fileObservability } from 'agentfootprint/observe';
60
+ *
61
+ * const telemetry = agent.enable.observability({
62
+ * strategy: fileObservability({
63
+ * path: '/var/log/agentfootprint/events.ndjson',
64
+ * maxBytes: 64 * 1024 * 1024, // safety ceiling; logrotate owns retention
65
+ * }),
66
+ * });
67
+ *
68
+ * // … run …
69
+ * await agent.shutdown(); // flushes + stops everything enabled
70
+ * ```
71
+ */
72
+ import { lazyRequire } from '../../lib/lazyRequire.js';
73
+ import { rateLimitedConsoleSink } from './deliveryErrors.js';
74
+ // ─── Public factory ──────────────────────────────────────────────────
75
+ /**
76
+ * NDJSON-to-a-local-file observability strategy. See
77
+ * {@link FileObservabilityOptions} for the per-option contract, and this
78
+ * module's docstring for the rotation policy and what is NOT bounded.
79
+ */
80
+ export function fileObservability(opts) {
81
+ const strategyName = 'file';
82
+ if (!opts.path || typeof opts.path !== 'string' || opts.path.trim() === '') {
83
+ throw new TypeError(`[${strategyName}Observability] \`path\` is required — the file this sink writes. ` +
84
+ `Pass one, e.g. '/var/log/agentfootprint/events.ndjson'. There is no default: ` +
85
+ `where a run's telemetry lands on your disk is not a decision this library makes.`);
86
+ }
87
+ if (opts.maxBytes !== undefined && !(opts.maxBytes > 0)) {
88
+ throw new TypeError(`[${strategyName}Observability] \`maxBytes\` must be a positive number of bytes ` +
89
+ `(got ${String(opts.maxBytes)}). Omit it for no rotation at all, which is the ` +
90
+ `right choice when logrotate or a log shipper already owns '${opts.path}'.`);
91
+ }
92
+ const path = opts.path;
93
+ const rotatedPath = `${path}.1`;
94
+ const maxBufferEvents = opts.maxBufferEvents ?? 100;
95
+ const maxBufferBytes = opts.maxBufferBytes ?? 65_536;
96
+ const flushIntervalMs = opts.flushIntervalMs ?? 1000;
97
+ const fs = opts._fs ?? createNodeFileSink(strategyName);
98
+ // Claim the file NOW. Two things this buys, both worth the syscall:
99
+ // 1. an unwritable path (no directory, no permission, a directory where a
100
+ // file should be, a read-only mount) is refused at construction, where
101
+ // the caller can still read the message;
102
+ // 2. `statSync` below gives the rotation counter a true starting size, so a
103
+ // restart appends to an existing file rather than believing it is empty.
104
+ let bytesOnDisk = 0;
105
+ try {
106
+ const dir = parentDir(path);
107
+ if (dir)
108
+ fs.mkdirSync(dir, { recursive: true });
109
+ fs.appendFileSync(path, '');
110
+ bytesOnDisk = fs.statSync(path).size;
111
+ }
112
+ catch (err) {
113
+ throw new Error(`[${strategyName}Observability] cannot write '${path}': ${errorMessage(err)}. ` +
114
+ `The parent directory is created for you; a failure here means the path is a ` +
115
+ `directory, the mount is read-only, or the process user lacks permission. ` +
116
+ `Fix the path or the permission — this sink refuses to start rather than ` +
117
+ `silently drop every event.`);
118
+ }
119
+ // Buffered lines — drained by `flush()` / size trigger / timer.
120
+ const buffer = [];
121
+ let bufferBytes = 0;
122
+ let lastFlushPromise = Promise.resolve();
123
+ let timer;
124
+ let stopped = false;
125
+ // The fallback when the consumer wires nothing. Rate-limited; a
126
+ // consumer-supplied sink is not.
127
+ const consoleSink = rateLimitedConsoleSink(strategyName);
128
+ function scheduleTimedFlush() {
129
+ if (timer || flushIntervalMs <= 0 || stopped)
130
+ return;
131
+ timer = setTimeout(() => {
132
+ timer = undefined;
133
+ void doFlush();
134
+ }, flushIntervalMs);
135
+ // Never hold the process open for telemetry. `unref` exists on Node's
136
+ // Timeout and not on the browser's number — feature-detected, so a
137
+ // non-Node runtime with an injected `_fs` still works.
138
+ timer.unref?.();
139
+ }
140
+ /** Route a delivery failure through whatever `_onError` IS RIGHT NOW —
141
+ * reading it at call time, so a consumer who assigns `_onError` after
142
+ * construction still receives them (the 8.11.0 fix). */
143
+ function reportFailure(err) {
144
+ strategy._onError?.(err);
145
+ }
146
+ /** ONE generation. A file already at or over the ceiling is renamed to
147
+ * `<path>.1` (replacing any previous `.1`) and the counter resets. A batch
148
+ * bigger than the whole ceiling is still written — truncating a run's
149
+ * telemetry to fit a dial nobody set for that purpose would be worse. */
150
+ async function rotateIfNeeded(incomingBytes) {
151
+ const ceiling = opts.maxBytes;
152
+ if (ceiling === undefined)
153
+ return;
154
+ if (bytesOnDisk === 0)
155
+ return;
156
+ if (bytesOnDisk + incomingBytes <= ceiling)
157
+ return;
158
+ await fs.rename(path, rotatedPath);
159
+ bytesOnDisk = 0;
160
+ }
161
+ async function doFlush() {
162
+ // `stopped` is deliberately NOT a guard here — same stance as the AWS
163
+ // adapters since 8.11.1. `stop()` stops this strategy ACCEPTING events; it
164
+ // does not authorise throwing away events already accepted, and a `flush()`
165
+ // that cannot make progress after a `stop()` is how shutdown spins forever.
166
+ if (buffer.length === 0)
167
+ return;
168
+ // Snapshot + clear so events emitted during the in-flight write accumulate
169
+ // into the next batch.
170
+ const batch = buffer.splice(0);
171
+ const batchBytes = bufferBytes;
172
+ bufferBytes = 0;
173
+ const data = `${batch.join('\n')}\n`;
174
+ try {
175
+ await rotateIfNeeded(batchBytes);
176
+ await fs.appendFile(path, data);
177
+ bytesOnDisk += byteLength(data);
178
+ }
179
+ catch (err) {
180
+ reportFailure(new Error(`${batch.length} event(s) dropped writing to '${path}': ${errorMessage(err)}`));
181
+ }
182
+ }
183
+ function enqueue(event) {
184
+ if (stopped)
185
+ return;
186
+ // The hot path must not throw (port law). `JSON.stringify` can — a cycle, a
187
+ // BigInt, a throwing getter — and an event that cannot be serialized is a
188
+ // reportable fact, not a reason to break the agent loop.
189
+ let line;
190
+ try {
191
+ line = JSON.stringify(event);
192
+ }
193
+ catch (err) {
194
+ reportFailure(new Error(`event '${event?.type ?? 'unknown'}' could not be serialized: ` + errorMessage(err)));
195
+ return;
196
+ }
197
+ // `JSON.stringify` returns undefined for an undefined input — nothing to
198
+ // write, and a bare "undefined" line would corrupt the NDJSON stream.
199
+ if (line === undefined)
200
+ return;
201
+ buffer.push(line);
202
+ bufferBytes += byteLength(line) + 1; // + the newline this line will carry
203
+ if (buffer.length >= maxBufferEvents || bufferBytes >= maxBufferBytes) {
204
+ // Size trigger. Chain onto the last write rather than racing it — the
205
+ // file's line order IS the dispatch order, and that is the only thing an
206
+ // offline reader can rely on.
207
+ lastFlushPromise = lastFlushPromise.then(doFlush, doFlush);
208
+ }
209
+ else {
210
+ scheduleTimedFlush();
211
+ }
212
+ }
213
+ const strategy = {
214
+ name: strategyName,
215
+ capabilities: { events: true, logs: true },
216
+ ...(opts.eventTypes && { relevantEventTypes: opts.eventTypes }),
217
+ exportEvent: enqueue,
218
+ /**
219
+ * Write what is buffered. Called for you on shutdown (8.12.0) — by the
220
+ * handle `enable.observability()` returns, by `agent.shutdown()`, and by a
221
+ * `standingAgent` closing. Safe at any time, including after `stop()`.
222
+ */
223
+ async flush() {
224
+ // BOUNDED BY CONSTRUCTION — every pass must remove at least one buffered
225
+ // line; a pass that removes none ends the drain instead of retrying. The
226
+ // shape the CloudWatch adapter arrived at in 8.11.1, for the same reason:
227
+ // a drain that cannot finish must return, never spin.
228
+ for (;;) {
229
+ const pending = buffer.length;
230
+ lastFlushPromise = lastFlushPromise.then(doFlush, doFlush);
231
+ await lastFlushPromise;
232
+ if (buffer.length === 0)
233
+ return;
234
+ if (buffer.length >= pending)
235
+ return;
236
+ }
237
+ },
238
+ /** Clear the timer and stop accepting events. Terminal: there is no
239
+ * restart. What it does NOT do is discard the buffer — `flush()` after
240
+ * `stop()` still writes it. */
241
+ stop() {
242
+ stopped = true;
243
+ if (timer) {
244
+ clearTimeout(timer);
245
+ timer = undefined;
246
+ }
247
+ },
248
+ /** Where failures go. Two callers: the dispatch layer (when `exportEvent`
249
+ * itself throws) and this adapter's own write path. */
250
+ _onError(err, event) {
251
+ (opts.onError ?? consoleSink)(err, event);
252
+ },
253
+ };
254
+ return strategy;
255
+ }
256
+ // ─── node:fs binding (lazy) ──────────────────────────────────────────
257
+ /**
258
+ * Bind {@link FileSinkFs} to `node:fs`. Lazily required so that importing
259
+ * `agentfootprint/observe` in a browser bundle never resolves `node:fs` — only
260
+ * CALLING this factory does, and a runtime without it is refused by name.
261
+ */
262
+ function createNodeFileSink(strategyName) {
263
+ let mod;
264
+ try {
265
+ mod = lazyRequire('node:fs');
266
+ }
267
+ catch {
268
+ throw new Error(`[${strategyName}Observability] needs \`node:fs\`, and this runtime has none. ` +
269
+ `It is a Node-only sink by definition — a browser has no local file to append to. ` +
270
+ `In a browser, ship events over your own transport, or pass \`_fs\` to write ` +
271
+ `somewhere you control.`);
272
+ }
273
+ return {
274
+ mkdirSync: (dir, options) => void mod.mkdirSync(dir, options),
275
+ appendFileSync: (file, data) => mod.appendFileSync(file, data),
276
+ statSync: (file) => mod.statSync(file),
277
+ appendFile: (file, data) => mod.promises.appendFile(file, data),
278
+ rename: (from, to) => mod.promises.rename(from, to),
279
+ };
280
+ }
281
+ // ─── Small helpers ───────────────────────────────────────────────────
282
+ function errorMessage(err) {
283
+ return err instanceof Error ? err.message : String(err);
284
+ }
285
+ /** UTF-8 byte length. `Buffer` where there is one, `TextEncoder` otherwise —
286
+ * the byte count decides rotation, so a multi-byte prompt must not be counted
287
+ * as its character length. */
288
+ function byteLength(s) {
289
+ const B = globalThis.Buffer;
290
+ if (B)
291
+ return B.byteLength(s, 'utf8');
292
+ return new TextEncoder().encode(s).length;
293
+ }
294
+ /** The directory part of a path, POSIX or Windows separators, or `''` for a
295
+ * bare filename (nothing to create). Done by hand rather than through
296
+ * `node:path` so the module has exactly one Node import, behind one seam. */
297
+ function parentDir(file) {
298
+ const idx = Math.max(file.lastIndexOf('/'), file.lastIndexOf('\\'));
299
+ if (idx <= 0)
300
+ return '';
301
+ return file.slice(0, idx);
302
+ }
303
+ //# sourceMappingURL=file.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file.js","sourceRoot":"","sources":["../../../../src/adapters/observability/file.ts"],"names":[],"mappings":"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"}
@@ -35,3 +35,4 @@ export type { AuthorizationRequiredMode, ConsentRequest } from './identity/conse
35
35
  export { CredentialConsentRequiredError, type CredentialConsentRequiredContext, } from './identity/CredentialConsentRequiredError.js';
36
36
  export { withCredentialRetry, type WithCredentialRetryOptions, } from './identity/withCredentialRetry.js';
37
37
  export { agentCoreIdentity, type AgentCoreIdentityOptions, type AgentCoreIdentityClientLike, type AgentCoreOauthResponse, } from './adapters/identity/agentcore.js';
38
+ export { vaultCredentials, type VaultCredentialsOptions } from './adapters/identity/vault.js';
@@ -33,4 +33,8 @@ export { staticTokens } from './identity/staticTokens.js';
33
33
  export { CredentialConsentRequiredError, } from './identity/CredentialConsentRequiredError.js';
34
34
  export { withCredentialRetry, } from './identity/withCredentialRetry.js';
35
35
  export { agentCoreIdentity, } from './adapters/identity/agentcore.js';
36
+ // HashiCorp-Vault-compatible KV v2, over plain HTTP (9.8.0) — no SDK, no
37
+ // vendor client. V1 is token auth + KV v2 + no leases, and every other shape
38
+ // is refused by name rather than guessed at; see the module docstring.
39
+ export { vaultCredentials } from './adapters/identity/vault.js';
36
40
  //# sourceMappingURL=identity.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"identity.js","sourceRoot":"","sources":["../../src/identity.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAWH,OAAO,EAAE,kBAAkB,EAAE,8BAA8B,EAAE,MAAM,qBAAqB,CAAC;AACzF,OAAO,EACL,MAAM,EACN,MAAM,EACN,KAAK,EACL,OAAO,GAKR,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,YAAY,EAA4B,MAAM,4BAA4B,CAAC;AAKpF,OAAO,EACL,8BAA8B,GAE/B,MAAM,8CAA8C,CAAC;AACtD,OAAO,EACL,mBAAmB,GAEpB,MAAM,mCAAmC,CAAC;AAC3C,OAAO,EACL,iBAAiB,GAIlB,MAAM,kCAAkC,CAAC"}
1
+ {"version":3,"file":"identity.js","sourceRoot":"","sources":["../../src/identity.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAWH,OAAO,EAAE,kBAAkB,EAAE,8BAA8B,EAAE,MAAM,qBAAqB,CAAC;AACzF,OAAO,EACL,MAAM,EACN,MAAM,EACN,KAAK,EACL,OAAO,GAKR,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,YAAY,EAA4B,MAAM,4BAA4B,CAAC;AAKpF,OAAO,EACL,8BAA8B,GAE/B,MAAM,8CAA8C,CAAC;AACtD,OAAO,EACL,mBAAmB,GAEpB,MAAM,mCAAmC,CAAC;AAC3C,OAAO,EACL,iBAAiB,GAIlB,MAAM,kCAAkC,CAAC;AAC1C,yEAAyE;AACzE,6EAA6E;AAC7E,uEAAuE;AACvE,OAAO,EAAE,gBAAgB,EAAgC,MAAM,8BAA8B,CAAC"}
@@ -30,7 +30,9 @@
30
30
  * - agentcoreObservability ← v2.8.1
31
31
  * - cloudwatchObservability ← v2.8.2
32
32
  * - xrayObservability ← v2.8.3
33
- * - otelObservability ← v2.9.0 (this release)
33
+ * - otelObservability ← v2.9.0
34
+ * - fileObservability ← 9.8.0 (no vendor at all — NDJSON on disk,
35
+ * for the on-premises shop with no collector)
34
36
  *
35
37
  * Note: `datadogObservability` was on the v2.9 roadmap, but Datadog
36
38
  * APM accepts OTLP — point your OTel SDK at Datadog's OTLP endpoint
@@ -46,5 +48,6 @@ export { agentcoreObservability, type AgentcoreObservabilityOptions, } from './a
46
48
  export { cloudwatchObservability, type CloudwatchObservabilityOptions, } from './adapters/observability/cloudwatch.js';
47
49
  export { xrayObservability, type XrayObservabilityOptions, type XRayLikeClient, } from './adapters/observability/xray.js';
48
50
  export { otelObservability, type OtelObservabilityOptions, type OtelObservabilityStrategy, type OtelDecisionEvidenceRecorder, type OtelTracerLike, type OtelSpanLike, type OtelSpanOptions, type OtelAttributeValue, } from './adapters/observability/otel.js';
51
+ export { fileObservability, type FileObservabilityOptions, type FileSinkFs, } from './adapters/observability/file.js';
49
52
  export { auditExport, verifyAuditBundle, AUDIT_BUNDLE_FORMAT, AUDIT_GENESIS_EVENT_TYPE, AUDIT_ZERO_HASH, type AuditBundle, type AuditBundleHeader, type AuditExportOptions, type AuditExportStrategy, type AuditRecord, type AuditVerifyResult, } from './adapters/observability/audit.js';
50
53
  export { canonicalJson, CANONICAL_JSON_VERSION } from './lib/canonicalJson.js';
@@ -30,7 +30,9 @@
30
30
  * - agentcoreObservability ← v2.8.1
31
31
  * - cloudwatchObservability ← v2.8.2
32
32
  * - xrayObservability ← v2.8.3
33
- * - otelObservability ← v2.9.0 (this release)
33
+ * - otelObservability ← v2.9.0
34
+ * - fileObservability ← 9.8.0 (no vendor at all — NDJSON on disk,
35
+ * for the on-premises shop with no collector)
34
36
  *
35
37
  * Note: `datadogObservability` was on the v2.9 roadmap, but Datadog
36
38
  * APM accepts OTLP — point your OTel SDK at Datadog's OTLP endpoint
@@ -46,6 +48,10 @@ export { agentcoreObservability, } from './adapters/observability/agentcore.js';
46
48
  export { cloudwatchObservability, } from './adapters/observability/cloudwatch.js';
47
49
  export { xrayObservability, } from './adapters/observability/xray.js';
48
50
  export { otelObservability, } from './adapters/observability/otel.js';
51
+ // NDJSON to a local file (9.8.0) — the sink for a deployment with no
52
+ // collector to ship to. Vendor-free: `node:fs`, lazily required the same
53
+ // way the vendor SDKs are, so importing this barrel stays browser-safe.
54
+ export { fileObservability, } from './adapters/observability/file.js';
49
55
  // Tamper-evident audit export (#20) — the one vendor-free strategy in
50
56
  // this subpath (its only runtime requirement is `node:crypto`, lazily
51
57
  // imported the same way the vendor SDKs are).
@@ -1 +1 @@
1
- {"version":3,"file":"observability-providers.js","sourceRoot":"","sources":["../../src/observability-providers.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2CG;AAEH,OAAO,EACL,sBAAsB,GAEvB,MAAM,uCAAuC,CAAC;AAC/C,OAAO,EACL,uBAAuB,GAExB,MAAM,wCAAwC,CAAC;AAChD,OAAO,EACL,iBAAiB,GAGlB,MAAM,kCAAkC,CAAC;AAC1C,OAAO,EACL,iBAAiB,GAQlB,MAAM,kCAAkC,CAAC;AAC1C,sEAAsE;AACtE,sEAAsE;AACtE,8CAA8C;AAC9C,OAAO,EACL,WAAW,EACX,iBAAiB,EACjB,mBAAmB,EACnB,wBAAwB,EACxB,eAAe,GAOhB,MAAM,mCAAmC,CAAC;AAC3C,OAAO,EAAE,aAAa,EAAE,sBAAsB,EAAE,MAAM,wBAAwB,CAAC"}
1
+ {"version":3,"file":"observability-providers.js","sourceRoot":"","sources":["../../src/observability-providers.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6CG;AAEH,OAAO,EACL,sBAAsB,GAEvB,MAAM,uCAAuC,CAAC;AAC/C,OAAO,EACL,uBAAuB,GAExB,MAAM,wCAAwC,CAAC;AAChD,OAAO,EACL,iBAAiB,GAGlB,MAAM,kCAAkC,CAAC;AAC1C,OAAO,EACL,iBAAiB,GAQlB,MAAM,kCAAkC,CAAC;AAC1C,qEAAqE;AACrE,yEAAyE;AACzE,wEAAwE;AACxE,OAAO,EACL,iBAAiB,GAGlB,MAAM,kCAAkC,CAAC;AAC1C,sEAAsE;AACtE,sEAAsE;AACtE,8CAA8C;AAC9C,OAAO,EACL,WAAW,EACX,iBAAiB,EACjB,mBAAmB,EACnB,wBAAwB,EACxB,eAAe,GAOhB,MAAM,mCAAmC,CAAC;AAC3C,OAAO,EAAE,aAAa,EAAE,sBAAsB,EAAE,MAAM,wBAAwB,CAAC"}
package/dist/identity.js CHANGED
@@ -29,7 +29,7 @@
29
29
  * symbols, one door. Import from the door.
30
30
  */
31
31
  Object.defineProperty(exports, "__esModule", { value: true });
32
- exports.agentCoreIdentity = exports.withCredentialRetry = exports.CredentialConsentRequiredError = exports.staticTokens = exports.headers = exports.basic = exports.apiKey = exports.bearer = exports.unconfiguredCredentialProvider = exports.isCredentialIssued = void 0;
32
+ exports.vaultCredentials = exports.agentCoreIdentity = exports.withCredentialRetry = exports.CredentialConsentRequiredError = exports.staticTokens = exports.headers = exports.basic = exports.apiKey = exports.bearer = exports.unconfiguredCredentialProvider = exports.isCredentialIssued = void 0;
33
33
  var types_js_1 = require("./identity/types.js");
34
34
  Object.defineProperty(exports, "isCredentialIssued", { enumerable: true, get: function () { return types_js_1.isCredentialIssued; } });
35
35
  Object.defineProperty(exports, "unconfiguredCredentialProvider", { enumerable: true, get: function () { return types_js_1.unconfiguredCredentialProvider; } });
@@ -46,4 +46,9 @@ var withCredentialRetry_js_1 = require("./identity/withCredentialRetry.js");
46
46
  Object.defineProperty(exports, "withCredentialRetry", { enumerable: true, get: function () { return withCredentialRetry_js_1.withCredentialRetry; } });
47
47
  var agentcore_js_1 = require("./adapters/identity/agentcore.js");
48
48
  Object.defineProperty(exports, "agentCoreIdentity", { enumerable: true, get: function () { return agentcore_js_1.agentCoreIdentity; } });
49
+ // HashiCorp-Vault-compatible KV v2, over plain HTTP (9.8.0) — no SDK, no
50
+ // vendor client. V1 is token auth + KV v2 + no leases, and every other shape
51
+ // is refused by name rather than guessed at; see the module docstring.
52
+ var vault_js_1 = require("./adapters/identity/vault.js");
53
+ Object.defineProperty(exports, "vaultCredentials", { enumerable: true, get: function () { return vault_js_1.vaultCredentials; } });
49
54
  //# sourceMappingURL=identity.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"identity.js","sourceRoot":"","sources":["../src/identity.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;;;AAWH,gDAAyF;AAAhF,8GAAA,kBAAkB,OAAA;AAAE,0HAAA,8BAA8B,OAAA;AAC3D,gDAS6B;AAR3B,kGAAA,MAAM,OAAA;AACN,kGAAA,MAAM,OAAA;AACN,iGAAA,KAAK,OAAA;AACL,mGAAA,OAAO,OAAA;AAMT,8DAAoF;AAA3E,+GAAA,YAAY,OAAA;AAKrB,kGAGsD;AAFpD,mJAAA,8BAA8B,OAAA;AAGhC,4EAG2C;AAFzC,6HAAA,mBAAmB,OAAA;AAGrB,iEAK0C;AAJxC,iHAAA,iBAAiB,OAAA"}
1
+ {"version":3,"file":"identity.js","sourceRoot":"","sources":["../src/identity.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;;;AAWH,gDAAyF;AAAhF,8GAAA,kBAAkB,OAAA;AAAE,0HAAA,8BAA8B,OAAA;AAC3D,gDAS6B;AAR3B,kGAAA,MAAM,OAAA;AACN,kGAAA,MAAM,OAAA;AACN,iGAAA,KAAK,OAAA;AACL,mGAAA,OAAO,OAAA;AAMT,8DAAoF;AAA3E,+GAAA,YAAY,OAAA;AAKrB,kGAGsD;AAFpD,mJAAA,8BAA8B,OAAA;AAGhC,4EAG2C;AAFzC,6HAAA,mBAAmB,OAAA;AAGrB,iEAK0C;AAJxC,iHAAA,iBAAiB,OAAA;AAKnB,yEAAyE;AACzE,6EAA6E;AAC7E,uEAAuE;AACvE,yDAA8F;AAArF,4GAAA,gBAAgB,OAAA"}
@@ -31,7 +31,9 @@
31
31
  * - agentcoreObservability ← v2.8.1
32
32
  * - cloudwatchObservability ← v2.8.2
33
33
  * - xrayObservability ← v2.8.3
34
- * - otelObservability ← v2.9.0 (this release)
34
+ * - otelObservability ← v2.9.0
35
+ * - fileObservability ← 9.8.0 (no vendor at all — NDJSON on disk,
36
+ * for the on-premises shop with no collector)
35
37
  *
36
38
  * Note: `datadogObservability` was on the v2.9 roadmap, but Datadog
37
39
  * APM accepts OTLP — point your OTel SDK at Datadog's OTLP endpoint
@@ -44,7 +46,7 @@
44
46
  * symbols, one door. Import from the door.
45
47
  */
46
48
  Object.defineProperty(exports, "__esModule", { value: true });
47
- exports.CANONICAL_JSON_VERSION = exports.canonicalJson = exports.AUDIT_ZERO_HASH = exports.AUDIT_GENESIS_EVENT_TYPE = exports.AUDIT_BUNDLE_FORMAT = exports.verifyAuditBundle = exports.auditExport = exports.otelObservability = exports.xrayObservability = exports.cloudwatchObservability = exports.agentcoreObservability = void 0;
49
+ exports.CANONICAL_JSON_VERSION = exports.canonicalJson = exports.AUDIT_ZERO_HASH = exports.AUDIT_GENESIS_EVENT_TYPE = exports.AUDIT_BUNDLE_FORMAT = exports.verifyAuditBundle = exports.auditExport = exports.fileObservability = exports.otelObservability = exports.xrayObservability = exports.cloudwatchObservability = exports.agentcoreObservability = void 0;
48
50
  var agentcore_js_1 = require("./adapters/observability/agentcore.js");
49
51
  Object.defineProperty(exports, "agentcoreObservability", { enumerable: true, get: function () { return agentcore_js_1.agentcoreObservability; } });
50
52
  var cloudwatch_js_1 = require("./adapters/observability/cloudwatch.js");
@@ -53,6 +55,11 @@ var xray_js_1 = require("./adapters/observability/xray.js");
53
55
  Object.defineProperty(exports, "xrayObservability", { enumerable: true, get: function () { return xray_js_1.xrayObservability; } });
54
56
  var otel_js_1 = require("./adapters/observability/otel.js");
55
57
  Object.defineProperty(exports, "otelObservability", { enumerable: true, get: function () { return otel_js_1.otelObservability; } });
58
+ // NDJSON to a local file (9.8.0) — the sink for a deployment with no
59
+ // collector to ship to. Vendor-free: `node:fs`, lazily required the same
60
+ // way the vendor SDKs are, so importing this barrel stays browser-safe.
61
+ var file_js_1 = require("./adapters/observability/file.js");
62
+ Object.defineProperty(exports, "fileObservability", { enumerable: true, get: function () { return file_js_1.fileObservability; } });
56
63
  // Tamper-evident audit export (#20) — the one vendor-free strategy in
57
64
  // this subpath (its only runtime requirement is `node:crypto`, lazily
58
65
  // imported the same way the vendor SDKs are).
@@ -1 +1 @@
1
- {"version":3,"file":"observability-providers.js","sourceRoot":"","sources":["../src/observability-providers.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2CG;;;AAEH,sEAG+C;AAF7C,sHAAA,sBAAsB,OAAA;AAGxB,wEAGgD;AAF9C,wHAAA,uBAAuB,OAAA;AAGzB,4DAI0C;AAHxC,4GAAA,iBAAiB,OAAA;AAInB,4DAS0C;AARxC,4GAAA,iBAAiB,OAAA;AASnB,sEAAsE;AACtE,sEAAsE;AACtE,8CAA8C;AAC9C,8DAY2C;AAXzC,uGAAA,WAAW,OAAA;AACX,6GAAA,iBAAiB,OAAA;AACjB,+GAAA,mBAAmB,OAAA;AACnB,oHAAA,wBAAwB,OAAA;AACxB,2GAAA,eAAe,OAAA;AAQjB,2DAA+E;AAAtE,iHAAA,aAAa,OAAA;AAAE,0HAAA,sBAAsB,OAAA"}
1
+ {"version":3,"file":"observability-providers.js","sourceRoot":"","sources":["../src/observability-providers.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6CG;;;AAEH,sEAG+C;AAF7C,sHAAA,sBAAsB,OAAA;AAGxB,wEAGgD;AAF9C,wHAAA,uBAAuB,OAAA;AAGzB,4DAI0C;AAHxC,4GAAA,iBAAiB,OAAA;AAInB,4DAS0C;AARxC,4GAAA,iBAAiB,OAAA;AASnB,qEAAqE;AACrE,yEAAyE;AACzE,wEAAwE;AACxE,4DAI0C;AAHxC,4GAAA,iBAAiB,OAAA;AAInB,sEAAsE;AACtE,sEAAsE;AACtE,8CAA8C;AAC9C,8DAY2C;AAXzC,uGAAA,WAAW,OAAA;AACX,6GAAA,iBAAiB,OAAA;AACjB,+GAAA,mBAAmB,OAAA;AACnB,oHAAA,wBAAwB,OAAA;AACxB,2GAAA,eAAe,OAAA;AAQjB,2DAA+E;AAAtE,iHAAA,aAAa,OAAA;AAAE,0HAAA,sBAAsB,OAAA"}
@@ -0,0 +1,147 @@
1
+ /**
2
+ * vaultCredentials — a {@link CredentialProvider} over a HashiCorp-Vault-compatible
3
+ * KV v2 secret store, spoken as plain HTTP.
4
+ *
5
+ * import { vaultCredentials } from 'agentfootprint/security';
6
+ *
7
+ * const credentials = vaultCredentials({
8
+ * address: 'https://vault.internal:8200', // https, or say `allowHttp` out loud
9
+ * mount: 'secret', // KV v2 mount, default 'secret'
10
+ * paths: { github: 'ci/github' }, // service → path INSIDE the mount
11
+ * }); // token: VAULT_TOKEN, or `token`
12
+ *
13
+ * Zero dependencies and no SDK: one `GET` per resolution through the runtime's
14
+ * own `fetch`. Vault's HTTP API is small, stable and the thing every
15
+ * Vault-compatible store (OpenBao, and the Vault-API modes of several managed
16
+ * stores) implements — so the adapter that speaks HTTP works against more
17
+ * backends than the adapter that imports one vendor's client.
18
+ *
19
+ * ## V1 is deliberately one shape, and says so by name
20
+ *
21
+ * | Axis | V1 | Anything else |
22
+ * |---|---|---|
23
+ * | Auth | a **token** (`token` option, else `VAULT_TOKEN`) | AppRole / Kubernetes / JWT / AWS IAM login are **refused by name**, naming the option that would carry them |
24
+ * | Secret engine | **KV v2** (`<mount>/data/<path>`, the `data.data` envelope) | a KV v1 mount is refused by name once the response shape gives it away |
25
+ * | Leases / renewal | **none** — every `getCredential` re-reads the secret | a lease-aware provider is a different object, and the library's model since 9.7.0 is re-resolve-per-call |
26
+ *
27
+ * That is not modesty, it is the honest edge: an auth method the author cannot
28
+ * exercise against a real cluster would be a guess wearing an adapter's clothes.
29
+ * Each refusal names the option it would arrive on, so "tell us your auth shape"
30
+ * is a field report rather than an issue title.
31
+ *
32
+ * ## Field → credential kind
33
+ *
34
+ * A KV v2 read returns `{ data: { data: { …your fields… }, metadata: {…} } }`.
35
+ * The inner object is mapped to a {@link Credential} by the FIRST rule that
36
+ * matches, so a secret written the ordinary way needs no configuration:
37
+ *
38
+ * | Fields present | Becomes | Header it applies |
39
+ * |---|---|---|
40
+ * | `token` | `bearer(token)` | `authorization: Bearer …` |
41
+ * | `api_key` \| `apiKey` \| `key` | `apiKey(value, header ?? 'x-api-key')` | that header |
42
+ * | `username` + `password` | `basic(username, password)` | `authorization: Basic …` |
43
+ * | `headers` (an object of strings) | `headers(map)` | all of them |
44
+ *
45
+ * A secret matching none of them is refused — naming the PATH and the four
46
+ * shapes, never the secret. `toCredential` is the seam for a shop whose fields
47
+ * are named otherwise; it sees the secret and returns a `Credential`, and
48
+ * returning `undefined` falls back to the table above.
49
+ *
50
+ * ## Secrecy (the 8.6.0 two-clause law, applied here)
51
+ *
52
+ * A thrown message reaches the model as a tool result AND rides
53
+ * `agentfootprint.credential.failed`. So every error this adapter raises names
54
+ * **the service, the path and the HTTP status, and nothing from the response
55
+ * body or the token**. Nothing here logs, and no secret value, no `X-Vault-Token`
56
+ * header and no field name from the payload appears in any message it can throw
57
+ * — pinned by a grep-shaped test over every failure path. The credential it
58
+ * returns hides its own secret fields (non-enumerable) and carries `toHeaders`,
59
+ * so `structuredClone` rejects it and it cannot enter tracked scope by accident.
60
+ *
61
+ * @example Dev → prod is the same two lines
62
+ * ```ts
63
+ * // dev
64
+ * const credentials = staticTokens({ github: 'ghp_dev_xxx' });
65
+ * // prod — the tool code does not change
66
+ * const credentials = vaultCredentials({ address: process.env.VAULT_ADDR! });
67
+ * Agent.create({ provider, model, credentials }).build();
68
+ * ```
69
+ */
70
+ import type { Credential, CredentialProvider } from '../../identity/types.js';
71
+ /** The base options every form shares. */
72
+ interface VaultCredentialsBase {
73
+ /** Vault's base URL, e.g. `https://vault.internal:8200` — **required**, and
74
+ * **https** unless {@link VaultCredentialsBase.allowHttp} says otherwise. No
75
+ * `VAULT_ADDR` fallback: an agent that silently picks up an address from the
76
+ * environment is an agent that reads a different vault when the environment
77
+ * changes under it. Name it. */
78
+ readonly address: string;
79
+ /** The Vault token. Falls back to `VAULT_TOKEN` (the variable every Vault
80
+ * tool already sets). This is a secret: it is sent as `X-Vault-Token` and
81
+ * appears in no message this adapter can throw. */
82
+ readonly token?: string;
83
+ /** Auth method. **`'token'` is the only one V1 implements.** Anything else is
84
+ * refused at construction, by name, with what it would take — see
85
+ * {@link vaultCredentials}. */
86
+ readonly auth?: 'token';
87
+ /** KV v2 mount point. Default `'secret'` (Vault's own default for the KV v2
88
+ * engine). The read URL is `<address>/v1/<mount>/data/<path>`. */
89
+ readonly mount?: string;
90
+ /** Vault Enterprise / HCP namespace, sent as `X-Vault-Namespace`. Omit for
91
+ * open-source Vault and OpenBao, which have no namespaces. */
92
+ readonly namespace?: string;
93
+ /** Map the secret's fields to a {@link Credential} yourself. Returns
94
+ * `undefined` to fall back to the built-in table (`token` / `api_key` /
95
+ * `username`+`password` / `headers`). The seam for a shop whose field names
96
+ * are its own — and the reason this adapter does not need an option per
97
+ * spelling. **Never log or return the fields from here**; they are the
98
+ * secret. */
99
+ readonly toCredential?: (secret: Readonly<Record<string, unknown>>, service: string) => Credential | undefined;
100
+ /** Header name for the `api_key` shape when the secret does not carry its own
101
+ * `header` field. Default `'x-api-key'`. */
102
+ readonly apiKeyHeader?: string;
103
+ /** Request timeout in ms. Default 5000 — a credential resolution sits in
104
+ * front of a tool call, so a hung vault must fail rather than hang a run. */
105
+ readonly timeoutMs?: number;
106
+ /** Allow a plain-`http://` address. **Refused unless you set this**, because
107
+ * the Vault token travels in a request header: over plaintext HTTP, anyone
108
+ * on the path reads a token that can usually read every secret it can reach.
109
+ * Set it only for a loopback dev server (`http://127.0.0.1:8200`). */
110
+ readonly allowHttp?: boolean;
111
+ /** Stable provider id (default `'vault'`). Shows up in "which provider vended
112
+ * this". */
113
+ readonly id?: string;
114
+ /** Test seam — inject `fetch`. Bypasses the network entirely. */
115
+ readonly _fetch?: typeof fetch;
116
+ }
117
+ /**
118
+ * How a `service` becomes a path inside the mount. Three arms, and they
119
+ * EXCLUDE each other — two spellings of one rule can disagree, so the type
120
+ * refuses the pair and so does the constructor.
121
+ */
122
+ type VaultPathMapping = {
123
+ /** `service → path inside the mount`, the {@link staticTokens} shape one
124
+ * level up: the same literal map, holding a path instead of a token. An
125
+ * unknown service is refused by name, listing the known ones. */
126
+ readonly paths: Readonly<Record<string, string>>;
127
+ readonly resolve?: never;
128
+ } | {
129
+ /** `service → path`, computed. Return `undefined` to refuse a service.
130
+ * For the convention-driven shop: ``(s) => `agents/${s}` ``. */
131
+ readonly resolve: (service: string) => string | undefined;
132
+ readonly paths?: never;
133
+ } | {
134
+ /** Neither: the **service id IS the path** under the mount, so
135
+ * `service: 'github'` reads `<mount>/data/github`. */
136
+ readonly paths?: undefined;
137
+ readonly resolve?: undefined;
138
+ };
139
+ export type VaultCredentialsOptions = VaultCredentialsBase & VaultPathMapping;
140
+ /**
141
+ * Build a {@link CredentialProvider} that reads KV v2 secrets from a
142
+ * Vault-compatible store. See {@link VaultCredentialsOptions} for the
143
+ * per-option contract and this module's docstring for the V1 boundary.
144
+ */
145
+ export declare function vaultCredentials(options: VaultCredentialsOptions): CredentialProvider;
146
+ export {};
147
+ //# sourceMappingURL=vault.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vault.d.ts","sourceRoot":"","sources":["../../../../src/adapters/identity/vault.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoEG;AAEH,OAAO,KAAK,EACV,UAAU,EACV,kBAAkB,EAGnB,MAAM,yBAAyB,CAAC;AAKjC,0CAA0C;AAC1C,UAAU,oBAAoB;IAC5B;;;;qCAIiC;IACjC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB;;wDAEoD;IACpD,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB;;oCAEgC;IAChC,QAAQ,CAAC,IAAI,CAAC,EAAE,OAAO,CAAC;IACxB;uEACmE;IACnE,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB;mEAC+D;IAC/D,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B;;;;;kBAKc;IACd,QAAQ,CAAC,YAAY,CAAC,EAAE,CACtB,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,EACzC,OAAO,EAAE,MAAM,KACZ,UAAU,GAAG,SAAS,CAAC;IAC5B;iDAC6C;IAC7C,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAC/B;kFAC8E;IAC9E,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B;;;2EAGuE;IACvE,QAAQ,CAAC,SAAS,CAAC,EAAE,OAAO,CAAC;IAC7B;iBACa;IACb,QAAQ,CAAC,EAAE,CAAC,EAAE,MAAM,CAAC;IACrB,iEAAiE;IACjE,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,KAAK,CAAC;CAChC;AAED;;;;GAIG;AACH,KAAK,gBAAgB,GACjB;IACE;;sEAEkE;IAClE,QAAQ,CAAC,KAAK,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IACjD,QAAQ,CAAC,OAAO,CAAC,EAAE,KAAK,CAAC;CAC1B,GACD;IACE;qEACiE;IACjE,QAAQ,CAAC,OAAO,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,MAAM,GAAG,SAAS,CAAC;IAC1D,QAAQ,CAAC,KAAK,CAAC,EAAE,KAAK,CAAC;CACxB,GACD;IACE;2DACuD;IACvD,QAAQ,CAAC,KAAK,CAAC,EAAE,SAAS,CAAC;IAC3B,QAAQ,CAAC,OAAO,CAAC,EAAE,SAAS,CAAC;CAC9B,CAAC;AAEN,MAAM,MAAM,uBAAuB,GAAG,oBAAoB,GAAG,gBAAgB,CAAC;AAmB9E;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,uBAAuB,GAAG,kBAAkB,CAoIrF"}