agentfootprint 9.7.0 → 9.9.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/dist/adapters/identity/vault.js +336 -0
- package/dist/adapters/identity/vault.js.map +1 -0
- package/dist/adapters/observability/audit.js +2 -28
- package/dist/adapters/observability/audit.js.map +1 -1
- package/dist/adapters/observability/file.js +307 -0
- package/dist/adapters/observability/file.js.map +1 -0
- package/dist/adapters/observability/githubBugReporter.js +461 -0
- package/dist/adapters/observability/githubBugReporter.js.map +1 -0
- package/dist/adapters/observability/githubDeviceSignIn.js +263 -0
- package/dist/adapters/observability/githubDeviceSignIn.js.map +1 -0
- package/dist/esm/adapters/identity/vault.d.ts +146 -0
- package/dist/esm/adapters/identity/vault.js +332 -0
- package/dist/esm/adapters/identity/vault.js.map +1 -0
- package/dist/esm/adapters/observability/audit.js +1 -27
- package/dist/esm/adapters/observability/audit.js.map +1 -1
- package/dist/esm/adapters/observability/file.d.ts +145 -0
- package/dist/esm/adapters/observability/file.js +303 -0
- package/dist/esm/adapters/observability/file.js.map +1 -0
- package/dist/esm/adapters/observability/githubBugReporter.d.ts +154 -0
- package/dist/esm/adapters/observability/githubBugReporter.js +457 -0
- package/dist/esm/adapters/observability/githubBugReporter.js.map +1 -0
- package/dist/esm/adapters/observability/githubDeviceSignIn.d.ts +131 -0
- package/dist/esm/adapters/observability/githubDeviceSignIn.js +259 -0
- package/dist/esm/adapters/observability/githubDeviceSignIn.js.map +1 -0
- package/dist/esm/identity.d.ts +1 -0
- package/dist/esm/identity.js +4 -0
- package/dist/esm/identity.js.map +1 -1
- package/dist/esm/lib/bug-report/build.d.ts +103 -0
- package/dist/esm/lib/bug-report/build.js +648 -0
- package/dist/esm/lib/bug-report/build.js.map +1 -0
- package/dist/esm/lib/bug-report/index.d.ts +14 -0
- package/dist/esm/lib/bug-report/index.js +13 -0
- package/dist/esm/lib/bug-report/index.js.map +1 -0
- package/dist/esm/lib/bug-report/transcript.d.ts +61 -0
- package/dist/esm/lib/bug-report/transcript.js +124 -0
- package/dist/esm/lib/bug-report/transcript.js.map +1 -0
- package/dist/esm/lib/bug-report/types.d.ts +206 -0
- package/dist/esm/lib/bug-report/types.js +12 -0
- package/dist/esm/lib/bug-report/types.js.map +1 -0
- package/dist/esm/lib/bug-report/zip.d.ts +71 -0
- package/dist/esm/lib/bug-report/zip.js +202 -0
- package/dist/esm/lib/bug-report/zip.js.map +1 -0
- package/dist/esm/lib/libraryVersion.d.ts +23 -0
- package/dist/esm/lib/libraryVersion.js +46 -0
- package/dist/esm/lib/libraryVersion.js.map +1 -0
- package/dist/esm/lib/trace-toolpack/openRecording.d.ts +17 -0
- package/dist/esm/lib/trace-toolpack/openRecording.js +8 -2
- package/dist/esm/lib/trace-toolpack/openRecording.js.map +1 -1
- package/dist/esm/observability-providers.d.ts +6 -1
- package/dist/esm/observability-providers.js +16 -1
- package/dist/esm/observability-providers.js.map +1 -1
- package/dist/esm/observe.d.ts +1 -0
- package/dist/esm/observe.js +6 -0
- package/dist/esm/observe.js.map +1 -1
- package/dist/identity.js +6 -1
- package/dist/identity.js.map +1 -1
- package/dist/lib/bug-report/build.js +656 -0
- package/dist/lib/bug-report/build.js.map +1 -0
- package/dist/lib/bug-report/index.js +18 -0
- package/dist/lib/bug-report/index.js.map +1 -0
- package/dist/lib/bug-report/transcript.js +128 -0
- package/dist/lib/bug-report/transcript.js.map +1 -0
- package/dist/lib/bug-report/types.js +13 -0
- package/dist/lib/bug-report/types.js.map +1 -0
- package/dist/lib/bug-report/zip.js +207 -0
- package/dist/lib/bug-report/zip.js.map +1 -0
- package/dist/lib/libraryVersion.js +51 -0
- package/dist/lib/libraryVersion.js.map +1 -0
- package/dist/lib/trace-toolpack/openRecording.js +9 -2
- package/dist/lib/trace-toolpack/openRecording.js.map +1 -1
- package/dist/observability-providers.js +20 -2
- package/dist/observability-providers.js.map +1 -1
- package/dist/observe.js +14 -6
- package/dist/observe.js.map +1 -1
- package/dist/types/adapters/identity/vault.d.ts +147 -0
- package/dist/types/adapters/identity/vault.d.ts.map +1 -0
- package/dist/types/adapters/observability/audit.d.ts.map +1 -1
- package/dist/types/adapters/observability/file.d.ts +146 -0
- package/dist/types/adapters/observability/file.d.ts.map +1 -0
- package/dist/types/adapters/observability/githubBugReporter.d.ts +155 -0
- package/dist/types/adapters/observability/githubBugReporter.d.ts.map +1 -0
- package/dist/types/adapters/observability/githubDeviceSignIn.d.ts +132 -0
- package/dist/types/adapters/observability/githubDeviceSignIn.d.ts.map +1 -0
- package/dist/types/identity.d.ts +1 -0
- package/dist/types/identity.d.ts.map +1 -1
- package/dist/types/lib/bug-report/build.d.ts +104 -0
- package/dist/types/lib/bug-report/build.d.ts.map +1 -0
- package/dist/types/lib/bug-report/index.d.ts +15 -0
- package/dist/types/lib/bug-report/index.d.ts.map +1 -0
- package/dist/types/lib/bug-report/transcript.d.ts +62 -0
- package/dist/types/lib/bug-report/transcript.d.ts.map +1 -0
- package/dist/types/lib/bug-report/types.d.ts +207 -0
- package/dist/types/lib/bug-report/types.d.ts.map +1 -0
- package/dist/types/lib/bug-report/zip.d.ts +72 -0
- package/dist/types/lib/bug-report/zip.d.ts.map +1 -0
- package/dist/types/lib/libraryVersion.d.ts +24 -0
- package/dist/types/lib/libraryVersion.d.ts.map +1 -0
- package/dist/types/lib/trace-toolpack/openRecording.d.ts +17 -0
- package/dist/types/lib/trace-toolpack/openRecording.d.ts.map +1 -1
- package/dist/types/observability-providers.d.ts +6 -1
- package/dist/types/observability-providers.d.ts.map +1 -1
- package/dist/types/observe.d.ts +1 -0
- package/dist/types/observe.d.ts.map +1 -1
- package/package.json +1 -1
|
@@ -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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"}
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* githubBugReporter — file a bug report, with the run attached, into GitHub.
|
|
3
|
+
*
|
|
4
|
+
* import { exportBugReport, githubBugReporter } from 'agentfootprint/observe';
|
|
5
|
+
*
|
|
6
|
+
* const reporter = githubBugReporter({
|
|
7
|
+
* issueRepo: 'acme/checkout-agent', // where the ISSUE goes
|
|
8
|
+
* evidenceRepo: 'acme/agent-evidence', // where the ZIP goes (default: issueRepo)
|
|
9
|
+
* }); // token: GITHUB_TOKEN, or `token`
|
|
10
|
+
*
|
|
11
|
+
* const { issueUrl, zipUrl } = await reporter.file(report);
|
|
12
|
+
*
|
|
13
|
+
* Two HTTP calls and no SDK: `PUT /repos/{evidenceRepo}/contents/{path}` commits
|
|
14
|
+
* the zip, `POST /repos/{issueRepo}/issues` files the issue with a manifest
|
|
15
|
+
* table and a link to the committed bundle. Plain `fetch`, zero dependencies,
|
|
16
|
+
* `apiBase` for GitHub Enterprise Server — so this works unchanged on a network
|
|
17
|
+
* that never reaches github.com.
|
|
18
|
+
*
|
|
19
|
+
* ## TWIN TARGETS: the issue and the evidence may live in different repos
|
|
20
|
+
*
|
|
21
|
+
* The case this exists for: a field tester finds a bug in a LIBRARY. The issue
|
|
22
|
+
* belongs in the library's public repo, where the maintainers and the next
|
|
23
|
+
* person to hit it will find it. The evidence — a real run, with real prompts,
|
|
24
|
+
* real tool arguments and real retrieved documents — does not. So the zip goes
|
|
25
|
+
* into a PRIVATE repo the maintainers can read, and the issue links it and says
|
|
26
|
+
* plainly that the evidence is private.
|
|
27
|
+
*
|
|
28
|
+
* ```ts
|
|
29
|
+
* githubBugReporter({
|
|
30
|
+
* issueRepo: 'footprintjs/agentfootprint', // public — the conversation
|
|
31
|
+
* evidenceRepo: 'acme/af-bug-evidence', // private — the run
|
|
32
|
+
* });
|
|
33
|
+
* ```
|
|
34
|
+
*
|
|
35
|
+
* ## DEFAULT-TARGET DOCTRINE
|
|
36
|
+
*
|
|
37
|
+
* **File into the application's OWN repo.** That is the default (`evidenceRepo`
|
|
38
|
+
* defaults to `issueRepo`) and it is the right default: the run belongs to the
|
|
39
|
+
* organisation that produced it. Sending a run's evidence across an
|
|
40
|
+
* organisational boundary — to a vendor, to an upstream library, to anyone
|
|
41
|
+
* whose access your company did not grant — is a HUMAN act with consequences a
|
|
42
|
+
* library cannot weigh. This adapter will do it, because a field tester filing
|
|
43
|
+
* upstream is a real and valuable thing; it will not do it quietly. The
|
|
44
|
+
* consent manifest (`describeBugReport`) exists so a person sees exactly what
|
|
45
|
+
* would leave before it does, and this reporter refuses to commit evidence to a
|
|
46
|
+
* PUBLIC repo unless the caller says `acknowledgePublicEvidence: true` out loud.
|
|
47
|
+
*
|
|
48
|
+
* ## Provisioning the token: fine-grained, and scoped to two repos
|
|
49
|
+
*
|
|
50
|
+
* Use a **fine-grained personal access token** (GitHub → Settings → Developer
|
|
51
|
+
* settings → Fine-grained tokens), scoped to ONLY `issueRepo` and
|
|
52
|
+
* `evidenceRepo`, with exactly two permissions — **Contents: read and write**
|
|
53
|
+
* (to commit the zip) and **Issues: read and write** (to file the issue) — and
|
|
54
|
+
* an expiry date. Put it in the server's environment as `GITHUB_TOKEN`, or pass
|
|
55
|
+
* it as `token`.
|
|
56
|
+
*
|
|
57
|
+
* The contrast matters: a CLASSIC PAT's `repo` scope is coarse — it grants
|
|
58
|
+
* read/write across every repository the account can reach, so a leaked
|
|
59
|
+
* bug-report token is a leaked key to the whole account. With a fine-grained
|
|
60
|
+
* token scoped as above, the blast radius of a leak is filing bug reports and
|
|
61
|
+
* committing files to one evidence repo, and nothing else. GitHub App
|
|
62
|
+
* installation tokens (short-lived, org-installed, revocable centrally) are the
|
|
63
|
+
* next rung for an organisation that wants one; this adapter does not mint
|
|
64
|
+
* them — hand it the token your app already obtained.
|
|
65
|
+
*
|
|
66
|
+
* ## Secrecy (the two-clause law)
|
|
67
|
+
*
|
|
68
|
+
* The token appears in no message, no error and no log this adapter can
|
|
69
|
+
* produce, and neither does the bundle's content. A failed request is reported
|
|
70
|
+
* as **the status and GitHub's own `message` field** — never the request, never
|
|
71
|
+
* the headers, never the body that carried the token, never a byte of
|
|
72
|
+
* evidence. Transport failures are re-wrapped rather than rethrown, because a
|
|
73
|
+
* `fetch` implementation is free to put the request (headers included) into the
|
|
74
|
+
* error it throws. Nothing here writes to a console. Pinned by a suite that
|
|
75
|
+
* forces every failure path and greps the message, the stack and the JSON
|
|
76
|
+
* projection for the token.
|
|
77
|
+
*
|
|
78
|
+
* @example A server route (the app's own repo, the default target)
|
|
79
|
+
* ```ts
|
|
80
|
+
* const reporter = githubBugReporter({ issueRepo: 'acme/checkout-agent' });
|
|
81
|
+
* app.post('/bug-report', async (req, res) => {
|
|
82
|
+
* const report = exportBugReport(recordings.get(req.body.runId), req.body.fields);
|
|
83
|
+
* res.json(await reporter.file(report));
|
|
84
|
+
* });
|
|
85
|
+
* ```
|
|
86
|
+
*/
|
|
87
|
+
import type { BugReport } from '../../lib/bug-report/index.js';
|
|
88
|
+
export interface GithubBugReporterOptions {
|
|
89
|
+
/** `owner/name` of the repo the ISSUE is filed in. Required. */
|
|
90
|
+
readonly issueRepo: string;
|
|
91
|
+
/**
|
|
92
|
+
* `owner/name` of the repo the evidence ZIP is committed to. Defaults to
|
|
93
|
+
* {@link issueRepo}. Point it at a PRIVATE repo when the issue itself is
|
|
94
|
+
* public — see the twin-target section above.
|
|
95
|
+
*/
|
|
96
|
+
readonly evidenceRepo?: string;
|
|
97
|
+
/**
|
|
98
|
+
* The GitHub token. Falls back to the `GITHUB_TOKEN` environment variable.
|
|
99
|
+
* It needs **Contents: read/write on `evidenceRepo`** and **Issues:
|
|
100
|
+
* read/write on `issueRepo`**; filing an issue on a public repo needs only a
|
|
101
|
+
* valid account token. This is a secret: it is sent as an `Authorization`
|
|
102
|
+
* header and appears in no message this adapter can throw.
|
|
103
|
+
*/
|
|
104
|
+
readonly token?: string;
|
|
105
|
+
/** Directory inside `evidenceRepo` for the bundles. Default `'bug-reports'`. */
|
|
106
|
+
readonly dir?: string;
|
|
107
|
+
/** Labels applied to the issue. Default: none. */
|
|
108
|
+
readonly labels?: readonly string[];
|
|
109
|
+
/** Branch to commit the evidence to. Default: the repo's default branch. */
|
|
110
|
+
readonly branch?: string;
|
|
111
|
+
/** API root. Default `https://api.github.com`; for GitHub Enterprise Server
|
|
112
|
+
* it is `https://github.your-company.com/api/v3`. */
|
|
113
|
+
readonly apiBase?: string;
|
|
114
|
+
/**
|
|
115
|
+
* Commit evidence to a PUBLIC repo deliberately.
|
|
116
|
+
*
|
|
117
|
+
* Off by default: a bundle carries a real run — prompts, tool arguments,
|
|
118
|
+
* retrieved documents — and a public repo publishes it to the internet
|
|
119
|
+
* permanently. Set this only when the evidence is synthetic, or when a human
|
|
120
|
+
* has read the manifest and decided.
|
|
121
|
+
*/
|
|
122
|
+
readonly acknowledgePublicEvidence?: boolean;
|
|
123
|
+
/** Refuse a zip larger than this before uploading. Default 24 MB. */
|
|
124
|
+
readonly maxZipBytes?: number;
|
|
125
|
+
/** Test seam — inject `fetch`. Bypasses the network entirely. */
|
|
126
|
+
readonly _fetch?: typeof fetch;
|
|
127
|
+
}
|
|
128
|
+
/** What a filed report leaves behind. */
|
|
129
|
+
export interface FiledBugReport {
|
|
130
|
+
/** The issue, on `issueRepo`. */
|
|
131
|
+
readonly issueUrl: string;
|
|
132
|
+
/** The committed bundle's blob page, on `evidenceRepo`. */
|
|
133
|
+
readonly zipUrl: string;
|
|
134
|
+
/** Path inside `evidenceRepo`. */
|
|
135
|
+
readonly zipPath: string;
|
|
136
|
+
/** `owner/name` the evidence went to. */
|
|
137
|
+
readonly evidenceRepo: string;
|
|
138
|
+
/**
|
|
139
|
+
* Whether the evidence repo's visibility was actually READ before committing.
|
|
140
|
+
*
|
|
141
|
+
* `false` means the metadata call failed — usually a token that can write
|
|
142
|
+
* contents but not read repository metadata. The commit proceeds (a
|
|
143
|
+
* permissions quirk must not block a bug report) and this field says the
|
|
144
|
+
* guard did not run, so nobody mistakes an unchecked upload for a checked one.
|
|
145
|
+
*/
|
|
146
|
+
readonly checkedVisibility: boolean;
|
|
147
|
+
/** The answer, when the check ran. */
|
|
148
|
+
readonly evidenceRepoPrivate?: boolean;
|
|
149
|
+
}
|
|
150
|
+
/** Files a finished {@link BugReport}. */
|
|
151
|
+
export interface BugReporter {
|
|
152
|
+
file(report: BugReport): Promise<FiledBugReport>;
|
|
153
|
+
}
|
|
154
|
+
export declare function githubBugReporter(options: GithubBugReporterOptions): BugReporter;
|