@birdybeep/agent-core 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs ADDED
@@ -0,0 +1,1098 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ AGENT_CORE_VERSION: () => AGENT_CORE_VERSION,
24
+ AGENT_SESSION_STATUSES: () => AGENT_SESSION_STATUSES,
25
+ APPROVAL_COLLAPSE_WINDOW_MS: () => APPROVAL_COLLAPSE_WINDOW_MS,
26
+ BIRDYBEEP_EVENT_TYPES: () => BIRDYBEEP_EVENT_TYPES,
27
+ BODY_MAX_CHARS: () => BODY_MAX_CHARS,
28
+ CLAIM_RECLAIM_MS: () => CLAIM_RECLAIM_MS,
29
+ DEFAULT_DEDUP_WINDOW_MS: () => DEFAULT_DEDUP_WINDOW_MS,
30
+ DEFAULT_DRAIN_MAX: () => DEFAULT_DRAIN_MAX,
31
+ DEFAULT_SEND_TIMEOUT_MS: () => DEFAULT_SEND_TIMEOUT_MS,
32
+ DEFAULT_TOTAL_BUDGET_MS: () => DEFAULT_TOTAL_BUDGET_MS,
33
+ ERROR_CODES: () => ERROR_CODES,
34
+ ERROR_STATUS: () => ERROR_STATUS,
35
+ FileTokenStore: () => FileTokenStore,
36
+ HARNESS_IDS: () => HARNESS_IDS,
37
+ INTEGRATION_STATUSES: () => INTEGRATION_STATUSES,
38
+ KeychainTokenStore: () => KeychainTokenStore,
39
+ LABEL_MAX_CHARS: () => LABEL_MAX_CHARS,
40
+ LocalEventQueue: () => LocalEventQueue,
41
+ MAX_AGENT_EVENT_BYTES: () => MAX_AGENT_EVENT_BYTES,
42
+ METADATA_MAX_DEPTH: () => METADATA_MAX_DEPTH,
43
+ METADATA_MAX_KEYS: () => METADATA_MAX_KEYS,
44
+ METADATA_VALUE_MAX_CHARS: () => METADATA_VALUE_MAX_CHARS,
45
+ NormalizeError: () => NormalizeError,
46
+ QUEUE_RETENTION_MS: () => QUEUE_RETENTION_MS,
47
+ RecentEventLedger: () => RecentEventLedger,
48
+ STATUS_REPORT_MAX_ITEMS: () => STATUS_REPORT_MAX_ITEMS,
49
+ STATUS_REPORT_MAX_PAYLOAD_BYTES: () => STATUS_REPORT_MAX_PAYLOAD_BYTES,
50
+ TITLE_MAX_CHARS: () => TITLE_MAX_CHARS,
51
+ agentEventsRequestSchema: () => agentEventsRequestSchema,
52
+ approvalCollapseIdentity: () => approvalCollapseIdentity,
53
+ birdyBeepAgentEventSchema: () => birdyBeepAgentEventSchema,
54
+ birdyBeepConfigDir: () => birdyBeepConfigDir,
55
+ birdyBeepDataDir: () => birdyBeepDataDir,
56
+ clearToken: () => clearToken,
57
+ collectMachineSignals: () => collectMachineSignals,
58
+ createSender: () => createSender,
59
+ defaultKeychainBackend: () => defaultKeychainBackend,
60
+ errorCodeSchema: () => errorCodeSchema,
61
+ errorEnvelopeSchema: () => errorEnvelopeSchema,
62
+ eventIdentity: () => eventIdentity,
63
+ eventMetadataSchema: () => eventMetadataSchema,
64
+ eventTypeSchema: () => eventTypeSchema,
65
+ fingerprintFromSignals: () => fingerprintFromSignals,
66
+ getMachineFingerprintHash: () => getMachineFingerprintHash,
67
+ getMachineIdentity: () => getMachineIdentity,
68
+ getMachineLabel: () => getMachineLabel,
69
+ getOS: () => getOS,
70
+ getToken: () => getToken,
71
+ harnessSchema: () => harnessSchema,
72
+ hashPath: () => hashPath,
73
+ integrationStatusItemSchema: () => integrationStatusItemSchema,
74
+ integrationStatusReportSchema: () => integrationStatusReportSchema,
75
+ integrationStatusResponseSchema: () => integrationStatusResponseSchema,
76
+ integrationStatusSchema: () => integrationStatusSchema,
77
+ isWithinMaxAgentEventSize: () => isWithinMaxAgentEventSize,
78
+ isoDateTimeSchema: () => isoDateTimeSchema,
79
+ machineSchema: () => machineSchema,
80
+ macosKeychainBackend: () => macosKeychainBackend,
81
+ normalizeEvent: () => normalizeEvent,
82
+ pairStartRequestSchema: () => pairStartRequestSchema,
83
+ pairStartResponseSchema: () => pairStartResponseSchema,
84
+ pairTokenRequestSchema: () => pairTokenRequestSchema,
85
+ pairTokenResponseSchema: () => pairTokenResponseSchema,
86
+ redactSecrets: () => redactSecrets,
87
+ resolveTokenStore: () => resolveTokenStore,
88
+ runAgentHook: () => runAgentHook,
89
+ scrubAbsolutePaths: () => scrubAbsolutePaths,
90
+ sessionStatusSchema: () => sessionStatusSchema,
91
+ setToken: () => setToken,
92
+ successEnvelopeSchema: () => successEnvelopeSchema,
93
+ truncate: () => truncate,
94
+ unavailableKeychainBackend: () => unavailableKeychainBackend,
95
+ workspaceSchema: () => workspaceSchema
96
+ });
97
+ module.exports = __toCommonJS(index_exports);
98
+
99
+ // src/adapter.ts
100
+ var INTEGRATION_STATUSES = [
101
+ "installed",
102
+ "not_detected",
103
+ "needs_restart",
104
+ "needs_trust",
105
+ "error",
106
+ "revoked",
107
+ "unknown"
108
+ ];
109
+
110
+ // src/api.ts
111
+ var import_zod = require("zod");
112
+ var ERROR_CODES = [
113
+ "validation_failed",
114
+ "unauthorized",
115
+ "forbidden",
116
+ "token_revoked",
117
+ "not_found",
118
+ "payload_too_large",
119
+ "rate_limited",
120
+ "quota_exceeded",
121
+ "internal_error"
122
+ ];
123
+ var errorCodeSchema = import_zod.z.enum(ERROR_CODES);
124
+ var errorEnvelopeSchema = import_zod.z.object({
125
+ error: import_zod.z.object({
126
+ code: errorCodeSchema,
127
+ message: import_zod.z.string(),
128
+ details: import_zod.z.unknown().optional()
129
+ }),
130
+ requestId: import_zod.z.string().optional()
131
+ });
132
+ var successEnvelopeSchema = (data) => import_zod.z.object({ data });
133
+ var ERROR_STATUS = {
134
+ validation_failed: 400,
135
+ unauthorized: 401,
136
+ forbidden: 403,
137
+ token_revoked: 403,
138
+ not_found: 404,
139
+ payload_too_large: 413,
140
+ rate_limited: 429,
141
+ quota_exceeded: 429,
142
+ internal_error: 500
143
+ };
144
+
145
+ // src/dedup.ts
146
+ var import_node_crypto = require("crypto");
147
+ var import_node_fs = require("fs");
148
+ var import_node_path2 = require("path");
149
+
150
+ // src/paths.ts
151
+ var import_node_os = require("os");
152
+ var import_node_path = require("path");
153
+ function baseDir(kind) {
154
+ const home = (0, import_node_os.homedir)();
155
+ if (process.platform === "win32") {
156
+ const local = process.env["LOCALAPPDATA"];
157
+ return local && local.length > 0 ? local : (0, import_node_path.join)(home, "AppData", "Local");
158
+ }
159
+ if (process.platform === "darwin") {
160
+ return (0, import_node_path.join)(home, "Library", "Application Support");
161
+ }
162
+ const xdg = kind === "data" ? process.env["XDG_DATA_HOME"] : process.env["XDG_CONFIG_HOME"];
163
+ if (xdg && xdg.length > 0) return xdg;
164
+ return (0, import_node_path.join)(home, kind === "data" ? (0, import_node_path.join)(".local", "share") : ".config");
165
+ }
166
+ function birdyBeepDataDir() {
167
+ return (0, import_node_path.join)(baseDir("data"), "birdybeep");
168
+ }
169
+ function birdyBeepConfigDir() {
170
+ return (0, import_node_path.join)(baseDir("config"), "birdybeep");
171
+ }
172
+
173
+ // src/dedup.ts
174
+ var DEFAULT_DEDUP_WINDOW_MS = 1e4;
175
+ var APPROVAL_COLLAPSE_WINDOW_MS = 1e3;
176
+ function isLedgerEntry(v) {
177
+ return typeof v === "object" && v !== null && typeof v["id"] === "string" && typeof v["ts"] === "number";
178
+ }
179
+ function contentHash(title, body) {
180
+ return (0, import_node_crypto.createHash)("sha256").update(`${title}
181
+ ${body}`).digest("hex").slice(0, 16);
182
+ }
183
+ function eventIdentity(event) {
184
+ return `${event.harness}:${event.source_session_id}:${event.event_type}:${contentHash(
185
+ event.title,
186
+ event.body
187
+ )}`;
188
+ }
189
+ function approvalCollapseIdentity(event) {
190
+ return `${event.harness}:${event.source_session_id}:approval_required:any`;
191
+ }
192
+ var RecentEventLedger = class {
193
+ path;
194
+ #windowMs;
195
+ #now;
196
+ constructor(options = {}) {
197
+ this.path = options.path ?? (0, import_node_path2.join)(birdyBeepDataDir(), "recent-events.json");
198
+ this.#windowMs = options.windowMs ?? DEFAULT_DEDUP_WINDOW_MS;
199
+ this.#now = options.now ?? (() => Date.now());
200
+ }
201
+ #read() {
202
+ if (!(0, import_node_fs.existsSync)(this.path)) return [];
203
+ try {
204
+ const parsed = JSON.parse((0, import_node_fs.readFileSync)(this.path, "utf8"));
205
+ return Array.isArray(parsed) ? parsed.filter(isLedgerEntry) : [];
206
+ } catch {
207
+ return [];
208
+ }
209
+ }
210
+ #write(entries) {
211
+ const dir = (0, import_node_path2.dirname)(this.path);
212
+ (0, import_node_fs.mkdirSync)(dir, { recursive: true, mode: 448 });
213
+ if (process.platform !== "win32") (0, import_node_fs.chmodSync)(dir, 448);
214
+ const tmp = `${this.path}.tmp`;
215
+ (0, import_node_fs.writeFileSync)(tmp, JSON.stringify(entries), { mode: 384 });
216
+ (0, import_node_fs.renameSync)(tmp, this.path);
217
+ if (process.platform !== "win32") (0, import_node_fs.chmodSync)(this.path, 384);
218
+ }
219
+ /**
220
+ * If `identity` was recorded within the window, return true (it's a duplicate —
221
+ * caller should skip). Otherwise record it (pruning expired entries) and return
222
+ * false. `windowMs` narrows the duplicate check for THIS identity (e.g. the short
223
+ * approval-collapse window) — pruning always uses the ledger's own (max) window so
224
+ * a narrower check never evicts entries other checks still need. Fail-open: any
225
+ * I/O error returns false (allow the send).
226
+ */
227
+ markAndCheck(identity, windowMs = this.#windowMs) {
228
+ try {
229
+ const now = this.#now();
230
+ const pruneCutoff = now - this.#windowMs;
231
+ const dupCutoff = now - Math.min(windowMs, this.#windowMs);
232
+ const fresh = this.#read().filter((e) => e.ts >= pruneCutoff);
233
+ if (fresh.some((e) => e.id === identity && e.ts >= dupCutoff)) return true;
234
+ fresh.push({ id: identity, ts: now });
235
+ this.#write(fresh);
236
+ return false;
237
+ } catch {
238
+ return false;
239
+ }
240
+ }
241
+ clear() {
242
+ try {
243
+ if ((0, import_node_fs.existsSync)(this.path)) (0, import_node_fs.rmSync)(this.path, { force: true });
244
+ } catch {
245
+ }
246
+ }
247
+ /** Whether the ledger file has secure (0600) perms. True on Windows (ACL-based). */
248
+ isSecure() {
249
+ if (process.platform === "win32") return true;
250
+ try {
251
+ return ((0, import_node_fs.statSync)(this.path).mode & 63) === 0;
252
+ } catch {
253
+ return true;
254
+ }
255
+ }
256
+ };
257
+
258
+ // src/event.ts
259
+ var import_zod3 = require("zod");
260
+
261
+ // src/primitives.ts
262
+ var import_zod2 = require("zod");
263
+ var BIRDYBEEP_EVENT_TYPES = [
264
+ "session_started",
265
+ "session_resumed",
266
+ "session_active",
267
+ "needs_input",
268
+ "approval_required",
269
+ "agent_idle",
270
+ "agent_completed",
271
+ "agent_failed",
272
+ "test_failed",
273
+ "tool_started",
274
+ "tool_finished",
275
+ "subagent_started",
276
+ "subagent_completed",
277
+ "custom",
278
+ // "test" (9fh): the `birdybeep test` diagnostic. Notifies by default server-side
279
+ // (unlike "custom", which the §10.5 matrix suppresses unconditionally) and is
280
+ // quota-exempt — the product's DEFAULT_NOTIFY/gauntlet carry the other half.
281
+ "test"
282
+ ];
283
+ var AGENT_SESSION_STATUSES = [
284
+ "starting",
285
+ "running",
286
+ "waiting_for_input",
287
+ "waiting_for_approval",
288
+ "idle",
289
+ "completed",
290
+ "failed",
291
+ "unknown"
292
+ ];
293
+ var HARNESS_IDS = ["claude_code", "codex", "opencode"];
294
+ var eventTypeSchema = import_zod2.z.enum(BIRDYBEEP_EVENT_TYPES);
295
+ var sessionStatusSchema = import_zod2.z.enum(AGENT_SESSION_STATUSES);
296
+ var harnessSchema = import_zod2.z.enum(HARNESS_IDS);
297
+ var isoDateTimeSchema = import_zod2.z.iso.datetime({ offset: true });
298
+ var MAX_AGENT_EVENT_BYTES = 16 * 1024;
299
+ function isWithinMaxAgentEventSize(body) {
300
+ const bytes = typeof body === "number" ? body : body.byteLength;
301
+ return bytes <= MAX_AGENT_EVENT_BYTES;
302
+ }
303
+
304
+ // src/event.ts
305
+ var machineSchema = import_zod3.z.object({
306
+ label: import_zod3.z.string().min(1),
307
+ os: import_zod3.z.string().min(1)
308
+ });
309
+ var workspaceSchema = import_zod3.z.object({
310
+ cwd: import_zod3.z.string().min(1),
311
+ repo_name: import_zod3.z.string().optional(),
312
+ branch: import_zod3.z.string().optional()
313
+ });
314
+ var eventMetadataSchema = import_zod3.z.object({
315
+ tool: import_zod3.z.string().optional(),
316
+ command_summary: import_zod3.z.string().optional()
317
+ }).catchall(import_zod3.z.unknown());
318
+ var birdyBeepAgentEventSchema = import_zod3.z.object({
319
+ event_id: import_zod3.z.string().min(1),
320
+ event_type: eventTypeSchema,
321
+ occurred_at: isoDateTimeSchema,
322
+ harness: harnessSchema,
323
+ harness_version: import_zod3.z.string().optional(),
324
+ source_session_id: import_zod3.z.string().min(1),
325
+ machine: machineSchema,
326
+ workspace: workspaceSchema,
327
+ status: sessionStatusSchema,
328
+ title: import_zod3.z.string(),
329
+ body: import_zod3.z.string(),
330
+ metadata: eventMetadataSchema.optional()
331
+ });
332
+ var agentEventsRequestSchema = birdyBeepAgentEventSchema;
333
+
334
+ // src/fingerprint.ts
335
+ var import_node_crypto2 = require("crypto");
336
+ var import_node_os2 = require("os");
337
+ function primaryMac() {
338
+ const ifaces = (0, import_node_os2.networkInterfaces)();
339
+ for (const list of Object.values(ifaces)) {
340
+ for (const entry of list ?? []) {
341
+ if (!entry.internal && entry.mac && entry.mac !== "00:00:00:00:00:00") return entry.mac;
342
+ }
343
+ }
344
+ return null;
345
+ }
346
+ function collectMachineSignals() {
347
+ return {
348
+ hostname: (0, import_node_os2.hostname)(),
349
+ platform: process.platform,
350
+ arch: process.arch,
351
+ cpuModel: (0, import_node_os2.cpus)()[0]?.model ?? "unknown",
352
+ totalmem: (0, import_node_os2.totalmem)(),
353
+ mac: primaryMac()
354
+ };
355
+ }
356
+ function fingerprintFromSignals(signals) {
357
+ const material = [
358
+ signals.platform,
359
+ signals.arch,
360
+ signals.hostname,
361
+ signals.cpuModel,
362
+ String(signals.totalmem),
363
+ signals.mac ?? ""
364
+ ].join("\0");
365
+ return (0, import_node_crypto2.createHash)("sha256").update(material).digest("hex");
366
+ }
367
+ function getMachineFingerprintHash(signals = collectMachineSignals()) {
368
+ return fingerprintFromSignals(signals);
369
+ }
370
+ function getOS(platform = process.platform) {
371
+ switch (platform) {
372
+ case "darwin":
373
+ return "macos";
374
+ case "win32":
375
+ return "windows";
376
+ case "linux":
377
+ return "linux";
378
+ default:
379
+ return platform;
380
+ }
381
+ }
382
+ function getMachineLabel(rawHostname = (0, import_node_os2.hostname)()) {
383
+ const label = rawHostname.replace(/\.local$/i, "").trim();
384
+ return label.length > 0 ? label : `${getOS()}-machine`;
385
+ }
386
+ function getMachineIdentity() {
387
+ return {
388
+ label: getMachineLabel(),
389
+ os: getOS(),
390
+ fingerprintHash: getMachineFingerprintHash()
391
+ };
392
+ }
393
+
394
+ // src/hook.ts
395
+ async function runAgentHook(adapter, rawInput, options) {
396
+ let event;
397
+ try {
398
+ event = await adapter.normalizeEvent(rawInput);
399
+ } catch {
400
+ return { outcome: "skipped" };
401
+ }
402
+ const ledger = options.ledger ?? new RecentEventLedger();
403
+ const contentDup = ledger.markAndCheck(eventIdentity(event));
404
+ const approvalDup = event.event_type === "approval_required" && ledger.markAndCheck(approvalCollapseIdentity(event), APPROVAL_COLLAPSE_WINDOW_MS);
405
+ if (contentDup || approvalDup) {
406
+ return { outcome: "deduped", eventType: event.event_type };
407
+ }
408
+ const send = await options.sender.send(event);
409
+ return { outcome: send.outcome, eventType: event.event_type, send };
410
+ }
411
+
412
+ // src/integrations.ts
413
+ var import_zod4 = require("zod");
414
+ var integrationStatusSchema = import_zod4.z.enum(INTEGRATION_STATUSES);
415
+ var STATUS_REPORT_MAX_ITEMS = 16;
416
+ var STATUS_REPORT_MAX_PAYLOAD_BYTES = 2048;
417
+ var integrationStatusItemSchema = import_zod4.z.object({
418
+ harness: harnessSchema,
419
+ status: integrationStatusSchema,
420
+ harness_version: import_zod4.z.string().max(64).optional(),
421
+ adapter_version: import_zod4.z.string().max(64).optional(),
422
+ last_status_payload: import_zod4.z.unknown().optional().refine((v) => v === void 0 || JSON.stringify(v).length <= STATUS_REPORT_MAX_PAYLOAD_BYTES, {
423
+ message: "last_status_payload too large"
424
+ })
425
+ });
426
+ var integrationStatusReportSchema = import_zod4.z.object({
427
+ integrations: import_zod4.z.array(integrationStatusItemSchema).min(1).max(STATUS_REPORT_MAX_ITEMS)
428
+ });
429
+ var integrationStatusResponseSchema = import_zod4.z.object({
430
+ integrations: import_zod4.z.array(
431
+ import_zod4.z.object({ harness: harnessSchema, status: integrationStatusSchema }).catchall(import_zod4.z.unknown())
432
+ )
433
+ });
434
+
435
+ // src/normalize.ts
436
+ var import_node_crypto3 = require("crypto");
437
+ var TITLE_MAX_CHARS = 200;
438
+ var BODY_MAX_CHARS = 2e3;
439
+ var METADATA_VALUE_MAX_CHARS = 500;
440
+ var METADATA_MAX_KEYS = 64;
441
+ var METADATA_MAX_DEPTH = 4;
442
+ var LABEL_MAX_CHARS = 120;
443
+ var NormalizeError = class extends Error {
444
+ constructor(message, issues) {
445
+ super(message);
446
+ this.issues = issues;
447
+ this.name = "NormalizeError";
448
+ }
449
+ issues;
450
+ };
451
+ function isString(v) {
452
+ return typeof v === "string";
453
+ }
454
+ function asRecord(v) {
455
+ return typeof v === "object" && v !== null && !Array.isArray(v) ? v : {};
456
+ }
457
+ function hashToken(value) {
458
+ return `h_${(0, import_node_crypto3.createHash)("sha256").update(value).digest("hex").slice(0, 16)}`;
459
+ }
460
+ var ABSOLUTE_PATH_RE = /(?:\/[A-Za-z0-9_.-]+){2,}|[A-Za-z]:(?:[\\/][A-Za-z0-9_. -]+)+/g;
461
+ var SECRET_RES = [
462
+ /\bAKIA[0-9A-Z]{16}\b/g,
463
+ // AWS access key id
464
+ /\bgh[pousr]_[A-Za-z0-9]{20,}\b/g,
465
+ // GitHub tokens
466
+ /\bsk-[A-Za-z0-9_-]{16,}\b/g,
467
+ // OpenAI-style keys
468
+ /\bxox[baprs]-[A-Za-z0-9-]{10,}\b/g,
469
+ // Slack tokens
470
+ /\beyJ[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}\b/g,
471
+ // JWT
472
+ /\b(?:bearer|token|secret|password|passwd|api[_-]?key)\b\s*[:=]\s*\S+/gi
473
+ // key=value secrets
474
+ ];
475
+ function scrubAbsolutePaths(text) {
476
+ return text.replace(ABSOLUTE_PATH_RE, (match) => hashToken(match));
477
+ }
478
+ function redactSecrets(text) {
479
+ let out = text;
480
+ for (const re of SECRET_RES) out = out.replace(re, "[redacted]");
481
+ return out;
482
+ }
483
+ function truncate(text, max) {
484
+ return text.length <= max ? text : `${text.slice(0, Math.max(0, max - 1))}\u2026`;
485
+ }
486
+ function hashPath(path) {
487
+ return hashToken(path);
488
+ }
489
+ function cleanString(text, max) {
490
+ return truncate(redactSecrets(scrubAbsolutePaths(text)), max);
491
+ }
492
+ function sanitizeValue(value, depth) {
493
+ if (isString(value)) return cleanString(value, METADATA_VALUE_MAX_CHARS);
494
+ if (typeof value === "number" || typeof value === "boolean" || value === null) return value;
495
+ if (depth >= METADATA_MAX_DEPTH) return void 0;
496
+ if (Array.isArray(value)) {
497
+ return value.slice(0, METADATA_MAX_KEYS).map((v) => sanitizeValue(v, depth + 1));
498
+ }
499
+ if (typeof value === "object") {
500
+ const out = {};
501
+ let n = 0;
502
+ for (const [k, v] of Object.entries(value)) {
503
+ if (n >= METADATA_MAX_KEYS) break;
504
+ const cleaned = sanitizeValue(v, depth + 1);
505
+ if (cleaned !== void 0) {
506
+ out[cleanString(k, LABEL_MAX_CHARS)] = cleaned;
507
+ n++;
508
+ }
509
+ }
510
+ return out;
511
+ }
512
+ return void 0;
513
+ }
514
+ function serializedBytes(event) {
515
+ return Buffer.byteLength(JSON.stringify(event), "utf8");
516
+ }
517
+ function enforceSize(event) {
518
+ if (serializedBytes(event) <= MAX_AGENT_EVENT_BYTES) return event;
519
+ const steps = [
520
+ { ...event, metadata: void 0 },
521
+ { ...event, metadata: void 0, body: truncate(event.body, 256) },
522
+ {
523
+ ...event,
524
+ metadata: void 0,
525
+ body: truncate(event.body, 256),
526
+ title: truncate(event.title, 120)
527
+ }
528
+ ];
529
+ for (const candidate of steps) {
530
+ if (serializedBytes(candidate) <= MAX_AGENT_EVENT_BYTES) return candidate;
531
+ }
532
+ throw new NormalizeError("event exceeds max payload size even after shrinking");
533
+ }
534
+ function normalizeEvent(input, opts = {}) {
535
+ const rec = asRecord(input);
536
+ const ws = asRecord(rec["workspace"]);
537
+ const machine = asRecord(rec["machine"]);
538
+ const candidate = {
539
+ event_id: isString(rec["event_id"]) && rec["event_id"].length > 0 ? rec["event_id"] : opts.generateId?.() ?? `evt_local_${(0, import_node_crypto3.randomUUID)()}`,
540
+ event_type: rec["event_type"],
541
+ occurred_at: isString(rec["occurred_at"]) && rec["occurred_at"].length > 0 ? rec["occurred_at"] : opts.now?.() ?? (/* @__PURE__ */ new Date()).toISOString(),
542
+ harness: rec["harness"],
543
+ // source_session_id is a key (§10.3) — keep it stable, but never let a raw path through.
544
+ source_session_id: isString(rec["source_session_id"]) ? scrubAbsolutePaths(rec["source_session_id"]) : rec["source_session_id"],
545
+ machine: {
546
+ label: isString(machine["label"]) ? cleanString(machine["label"], LABEL_MAX_CHARS) : machine["label"],
547
+ os: isString(machine["os"]) ? cleanString(machine["os"], LABEL_MAX_CHARS) : machine["os"]
548
+ },
549
+ workspace: {
550
+ // cwd is an absolute path → always hashed (§15: no absolute path leaves the machine).
551
+ cwd: isString(ws["cwd"]) ? hashPath(ws["cwd"]) : ws["cwd"]
552
+ },
553
+ status: rec["status"],
554
+ title: isString(rec["title"]) ? cleanString(rec["title"], TITLE_MAX_CHARS) : rec["title"],
555
+ body: isString(rec["body"]) ? cleanString(rec["body"], BODY_MAX_CHARS) : rec["body"]
556
+ };
557
+ if (isString(rec["harness_version"])) {
558
+ candidate["harness_version"] = cleanString(rec["harness_version"], LABEL_MAX_CHARS);
559
+ }
560
+ const wsOut = candidate["workspace"];
561
+ if (isString(ws["repo_name"])) wsOut["repo_name"] = cleanString(ws["repo_name"], LABEL_MAX_CHARS);
562
+ if (isString(ws["branch"])) wsOut["branch"] = cleanString(ws["branch"], LABEL_MAX_CHARS);
563
+ if (rec["metadata"] !== void 0) {
564
+ candidate["metadata"] = sanitizeValue(rec["metadata"], 0);
565
+ }
566
+ const parsed = birdyBeepAgentEventSchema.safeParse(candidate);
567
+ if (!parsed.success) {
568
+ throw new NormalizeError("normalized event failed schema validation", parsed.error.issues);
569
+ }
570
+ return enforceSize(parsed.data);
571
+ }
572
+
573
+ // src/pairing.ts
574
+ var import_zod5 = require("zod");
575
+ var pairStartRequestSchema = import_zod5.z.object({
576
+ machine_label: import_zod5.z.string().min(1),
577
+ os: import_zod5.z.string().optional().catch(void 0),
578
+ cli_version: import_zod5.z.string().optional().catch(void 0),
579
+ requested_scopes: import_zod5.z.array(import_zod5.z.string()).optional().catch(void 0)
580
+ });
581
+ var pairTokenRequestSchema = import_zod5.z.object({
582
+ device_code: import_zod5.z.string().min(1),
583
+ machine_fingerprint: import_zod5.z.string().optional().catch(void 0)
584
+ });
585
+ var pairStartResponseSchema = import_zod5.z.object({
586
+ device_code: import_zod5.z.string(),
587
+ user_code: import_zod5.z.string(),
588
+ qr_payload: import_zod5.z.string(),
589
+ expires_at: isoDateTimeSchema
590
+ });
591
+ var pairTokenResponseSchema = import_zod5.z.object({
592
+ machine_token: import_zod5.z.string(),
593
+ machine_id: import_zod5.z.string()
594
+ });
595
+
596
+ // src/queue.ts
597
+ var import_node_crypto4 = require("crypto");
598
+ var import_node_fs2 = require("fs");
599
+ var import_node_path3 = require("path");
600
+ var QUEUE_RETENTION_MS = 24 * 60 * 60 * 1e3;
601
+ var DEFAULT_DRAIN_MAX = 50;
602
+ var CLAIM_RECLAIM_MS = 6e4;
603
+ function isRecord(v) {
604
+ return typeof v === "object" && v !== null;
605
+ }
606
+ var LocalEventQueue = class {
607
+ dir;
608
+ #retentionMs;
609
+ #now;
610
+ constructor(options = {}) {
611
+ this.dir = options.dir ?? (0, import_node_path3.join)(birdyBeepDataDir(), "queue");
612
+ this.#retentionMs = options.retentionMs ?? QUEUE_RETENTION_MS;
613
+ this.#now = options.now ?? (() => Date.now());
614
+ }
615
+ /** Ensure the dir exists with 0700 perms; repair a too-permissive existing dir. */
616
+ #ensureDir() {
617
+ (0, import_node_fs2.mkdirSync)(this.dir, { recursive: true, mode: 448 });
618
+ if (process.platform !== "win32") (0, import_node_fs2.chmodSync)(this.dir, 448);
619
+ }
620
+ /** Park a normalized event on disk (atomic write, 0600). Never throws. */
621
+ enqueue(event) {
622
+ try {
623
+ this.#ensureDir();
624
+ const enqueuedAt = this.#now();
625
+ const name = `${enqueuedAt}-${(0, import_node_crypto4.randomUUID)()}.json`;
626
+ const finalPath = (0, import_node_path3.join)(this.dir, name);
627
+ const tmpPath = `${finalPath}.tmp`;
628
+ (0, import_node_fs2.writeFileSync)(tmpPath, JSON.stringify({ enqueuedAt, event }), { mode: 384 });
629
+ (0, import_node_fs2.renameSync)(tmpPath, finalPath);
630
+ if (process.platform !== "win32") (0, import_node_fs2.chmodSync)(finalPath, 384);
631
+ return true;
632
+ } catch {
633
+ return false;
634
+ }
635
+ }
636
+ /**
637
+ * Return orphaned `.claim` files (claims older than {@link CLAIM_RECLAIM_MS}) to the
638
+ * queue by renaming them back to their original `.json` name. The claim timestamp is
639
+ * embedded in the claim FILENAME (rename preserves mtime, so fs times would report
640
+ * the enqueue age, not the claim age — an active drain of an old entry must not be
641
+ * "reclaimed" out from under it). Racing reclaims are safe: rename losers ENOENT.
642
+ * Known window: a drainer SUSPENDED (not dead) >60s can resume after its claim was
643
+ * reclaimed and double-send — accepted, since 60s ≫ the 5s send budget (a claim that
644
+ * old almost always is a dead process) and the backend dedupe absorbs the repeat.
645
+ */
646
+ #reclaimOrphanedClaims(names) {
647
+ const cutoff = this.#now() - CLAIM_RECLAIM_MS;
648
+ for (const name of names) {
649
+ const m = /^(.+\.json)\.(\d+)-[0-9a-f-]+\.claim$/.exec(name);
650
+ if (!m || Number(m[2]) > cutoff) continue;
651
+ try {
652
+ (0, import_node_fs2.renameSync)((0, import_node_path3.join)(this.dir, name), (0, import_node_path3.join)(this.dir, m[1]));
653
+ } catch {
654
+ }
655
+ }
656
+ }
657
+ /** Read all non-expired entries (FIFO by enqueue time); prune expired/corrupt ones. */
658
+ #readFresh() {
659
+ if (!(0, import_node_fs2.existsSync)(this.dir)) return { fresh: [], pruned: 0 };
660
+ if (process.platform !== "win32") {
661
+ try {
662
+ (0, import_node_fs2.chmodSync)(this.dir, 448);
663
+ } catch {
664
+ }
665
+ }
666
+ let pruned = 0;
667
+ const fresh = [];
668
+ const cutoff = this.#now() - this.#retentionMs;
669
+ let names;
670
+ try {
671
+ names = (0, import_node_fs2.readdirSync)(this.dir);
672
+ this.#reclaimOrphanedClaims(names);
673
+ names = (0, import_node_fs2.readdirSync)(this.dir);
674
+ } catch {
675
+ return { fresh: [], pruned: 0 };
676
+ }
677
+ for (const name of names) {
678
+ if (!name.endsWith(".json")) continue;
679
+ const path = (0, import_node_path3.join)(this.dir, name);
680
+ try {
681
+ const parsed = JSON.parse((0, import_node_fs2.readFileSync)(path, "utf8"));
682
+ const enqueuedAt = isRecord(parsed) ? parsed["enqueuedAt"] : void 0;
683
+ const event = isRecord(parsed) ? parsed["event"] : void 0;
684
+ if (typeof enqueuedAt !== "number" || !isRecord(event)) {
685
+ (0, import_node_fs2.rmSync)(path, { force: true });
686
+ pruned++;
687
+ continue;
688
+ }
689
+ if (enqueuedAt < cutoff) {
690
+ (0, import_node_fs2.rmSync)(path, { force: true });
691
+ pruned++;
692
+ continue;
693
+ }
694
+ fresh.push({ path, enqueuedAt, event });
695
+ } catch {
696
+ try {
697
+ (0, import_node_fs2.rmSync)(path, { force: true });
698
+ } catch {
699
+ }
700
+ pruned++;
701
+ }
702
+ }
703
+ fresh.sort((a, b) => a.enqueuedAt - b.enqueuedAt);
704
+ return { fresh, pruned };
705
+ }
706
+ /** Count of fresh (non-expired) queued events. Prunes expired entries as a side effect. */
707
+ size() {
708
+ return this.#readFresh().fresh.length;
709
+ }
710
+ /**
711
+ * Drain up to `max` fresh entries through `send`. Each entry is CLAIMED via an
712
+ * atomic rename before sending, so two concurrent drains never double-send the
713
+ * same event. delivered/drop remove the entry; retry keeps it for next time.
714
+ * Bounded + best-effort: never throws into the caller.
715
+ */
716
+ async drain(send, options = {}) {
717
+ const max = options.max ?? DEFAULT_DRAIN_MAX;
718
+ const result = { delivered: 0, dropped: 0, kept: 0, pruned: 0 };
719
+ let fresh;
720
+ try {
721
+ this.#ensureDir();
722
+ const read = this.#readFresh();
723
+ fresh = read.fresh;
724
+ result.pruned = read.pruned;
725
+ } catch {
726
+ return result;
727
+ }
728
+ for (const entry of fresh.slice(0, max)) {
729
+ if (options.stopWhen?.() === true) {
730
+ result.kept += 1;
731
+ continue;
732
+ }
733
+ const claim = `${entry.path}.${this.#now()}-${(0, import_node_crypto4.randomUUID)()}.claim`;
734
+ try {
735
+ (0, import_node_fs2.renameSync)(entry.path, claim);
736
+ } catch {
737
+ continue;
738
+ }
739
+ let outcome;
740
+ try {
741
+ outcome = await send(entry.event);
742
+ } catch {
743
+ outcome = "retry";
744
+ }
745
+ if (outcome === "retry") {
746
+ try {
747
+ (0, import_node_fs2.renameSync)(claim, entry.path);
748
+ } catch {
749
+ }
750
+ result.kept++;
751
+ } else {
752
+ try {
753
+ (0, import_node_fs2.rmSync)(claim, { force: true });
754
+ } catch {
755
+ }
756
+ if (outcome === "delivered") result.delivered++;
757
+ else result.dropped++;
758
+ }
759
+ }
760
+ return result;
761
+ }
762
+ /** Remove every queued entry (used by `doctor` / debug tooling). Never throws. */
763
+ clear() {
764
+ if (!(0, import_node_fs2.existsSync)(this.dir)) return 0;
765
+ let removed = 0;
766
+ try {
767
+ for (const name of (0, import_node_fs2.readdirSync)(this.dir)) {
768
+ try {
769
+ (0, import_node_fs2.rmSync)((0, import_node_path3.join)(this.dir, name), { force: true });
770
+ removed++;
771
+ } catch {
772
+ }
773
+ }
774
+ } catch {
775
+ }
776
+ return removed;
777
+ }
778
+ /** Whether the queue dir has secure (0700) perms. Returns true on Windows (ACL-based). */
779
+ isSecure() {
780
+ if (process.platform === "win32") return true;
781
+ try {
782
+ return ((0, import_node_fs2.statSync)(this.dir).mode & 63) === 0;
783
+ } catch {
784
+ return false;
785
+ }
786
+ }
787
+ };
788
+
789
+ // src/token-store.ts
790
+ var import_node_child_process = require("child_process");
791
+ var import_node_fs3 = require("fs");
792
+ var import_node_path4 = require("path");
793
+ var import_node_util = require("util");
794
+ var execFileAsync = (0, import_node_util.promisify)(import_node_child_process.execFile);
795
+ var SERVICE = "birdybeep";
796
+ var ACCOUNT = "machine-token";
797
+ var FileTokenStore = class {
798
+ kind = "file";
799
+ path;
800
+ constructor(options = {}) {
801
+ this.path = options.path ?? (0, import_node_path4.join)(birdyBeepDataDir(), "token");
802
+ }
803
+ // Sync internals, Promise-returning to satisfy the TokenStore interface.
804
+ get() {
805
+ if (!(0, import_node_fs3.existsSync)(this.path)) return Promise.resolve(null);
806
+ this.#repairPerms();
807
+ const raw = (0, import_node_fs3.readFileSync)(this.path, "utf8").trim();
808
+ return Promise.resolve(raw.length > 0 ? raw : null);
809
+ }
810
+ set(token) {
811
+ const dir = (0, import_node_path4.dirname)(this.path);
812
+ (0, import_node_fs3.mkdirSync)(dir, { recursive: true, mode: 448 });
813
+ if (process.platform !== "win32") (0, import_node_fs3.chmodSync)(dir, 448);
814
+ const tmp = `${this.path}.tmp`;
815
+ (0, import_node_fs3.writeFileSync)(tmp, token, { mode: 384 });
816
+ (0, import_node_fs3.renameSync)(tmp, this.path);
817
+ if (process.platform !== "win32") (0, import_node_fs3.chmodSync)(this.path, 384);
818
+ return Promise.resolve();
819
+ }
820
+ clear() {
821
+ (0, import_node_fs3.rmSync)(this.path, { force: true });
822
+ return Promise.resolve();
823
+ }
824
+ /** Repair a too-permissive token file (§15.1: strict perms). POSIX only. */
825
+ #repairPerms() {
826
+ if (process.platform === "win32") return;
827
+ try {
828
+ if (((0, import_node_fs3.statSync)(this.path).mode & 63) !== 0) (0, import_node_fs3.chmodSync)(this.path, 384);
829
+ } catch {
830
+ }
831
+ }
832
+ };
833
+ var KeychainTokenStore = class {
834
+ kind = "keychain";
835
+ #backend;
836
+ constructor(backend) {
837
+ this.#backend = backend;
838
+ }
839
+ get() {
840
+ return this.#backend.get(SERVICE, ACCOUNT);
841
+ }
842
+ set(token) {
843
+ return this.#backend.set(SERVICE, ACCOUNT, token);
844
+ }
845
+ clear() {
846
+ return this.#backend.delete(SERVICE, ACCOUNT);
847
+ }
848
+ };
849
+ var unavailableKeychainBackend = {
850
+ available: false,
851
+ get: () => Promise.resolve(null),
852
+ set: () => Promise.reject(new Error("keychain unavailable")),
853
+ delete: () => Promise.resolve()
854
+ };
855
+ function macosKeychainBackend() {
856
+ return {
857
+ available: process.platform === "darwin",
858
+ async get(service, account) {
859
+ try {
860
+ const { stdout } = await execFileAsync("security", [
861
+ "find-generic-password",
862
+ "-s",
863
+ service,
864
+ "-a",
865
+ account,
866
+ "-w"
867
+ ]);
868
+ const value = stdout.replace(/\n$/, "");
869
+ return value.length > 0 ? value : null;
870
+ } catch {
871
+ return null;
872
+ }
873
+ },
874
+ async set(service, account, secret) {
875
+ await execFileAsync("security", [
876
+ "add-generic-password",
877
+ "-U",
878
+ "-s",
879
+ service,
880
+ "-a",
881
+ account,
882
+ "-w",
883
+ secret
884
+ ]);
885
+ },
886
+ async delete(service, account) {
887
+ try {
888
+ await execFileAsync("security", ["delete-generic-password", "-s", service, "-a", account]);
889
+ } catch {
890
+ }
891
+ }
892
+ };
893
+ }
894
+ function defaultKeychainBackend() {
895
+ if (process.platform === "darwin") return macosKeychainBackend();
896
+ return unavailableKeychainBackend;
897
+ }
898
+ function resolveTokenStore(options = {}) {
899
+ const backend = options.backend ?? defaultKeychainBackend();
900
+ if (backend.available) return new KeychainTokenStore(backend);
901
+ return new FileTokenStore(options.filePath !== void 0 ? { path: options.filePath } : {});
902
+ }
903
+ async function setToken(token, options = {}) {
904
+ const store = resolveTokenStore(options);
905
+ await store.set(token);
906
+ return store.kind;
907
+ }
908
+ async function getToken(options = {}) {
909
+ const backend = options.backend ?? defaultKeychainBackend();
910
+ if (backend.available) {
911
+ const fromKeychain = await new KeychainTokenStore(backend).get();
912
+ if (fromKeychain !== null) return fromKeychain;
913
+ }
914
+ const file = new FileTokenStore(options.filePath !== void 0 ? { path: options.filePath } : {});
915
+ return file.get();
916
+ }
917
+ async function clearToken(options = {}) {
918
+ const backend = options.backend ?? defaultKeychainBackend();
919
+ if (backend.available) await new KeychainTokenStore(backend).clear();
920
+ await new FileTokenStore(
921
+ options.filePath !== void 0 ? { path: options.filePath } : {}
922
+ ).clear();
923
+ }
924
+
925
+ // src/sender.ts
926
+ var DEFAULT_SEND_TIMEOUT_MS = 3e3;
927
+ var DEFAULT_TOTAL_BUDGET_MS = 5e3;
928
+ var MIN_DRAIN_ATTEMPT_MS = 250;
929
+ var AGENT_EVENTS_PATH = "/v1/agent-events";
930
+ var EMPTY_DRAIN = { delivered: 0, dropped: 0, kept: 0, pruned: 0 };
931
+ function classify(status, code) {
932
+ if (code === "rate_limited" || code === "internal_error") return "retry";
933
+ if (code !== void 0) return "drop";
934
+ if (status >= 500 || status === 429) return "retry";
935
+ return "drop";
936
+ }
937
+ function parseDecision(body) {
938
+ if (typeof body !== "object" || body === null) return void 0;
939
+ const decision = body["decision"];
940
+ return typeof decision === "string" ? decision : void 0;
941
+ }
942
+ function createSender(config) {
943
+ const baseUrl = config.baseUrl.replace(/\/$/, "");
944
+ const timeoutMs = config.timeoutMs ?? DEFAULT_SEND_TIMEOUT_MS;
945
+ const totalBudgetMs = config.totalBudgetMs ?? DEFAULT_TOTAL_BUDGET_MS;
946
+ const queue = config.queue ?? new LocalEventQueue();
947
+ const fetchImpl = config.fetchImpl ?? fetch;
948
+ const drainMax = config.drainMax ?? DEFAULT_DRAIN_MAX;
949
+ const clock = config.now ?? (() => Date.now());
950
+ async function attempt(event, token, attemptTimeoutMs = timeoutMs) {
951
+ const controller = new AbortController();
952
+ const timer = setTimeout(() => controller.abort(), attemptTimeoutMs);
953
+ try {
954
+ const res = await fetchImpl(`${baseUrl}${AGENT_EVENTS_PATH}`, {
955
+ method: "POST",
956
+ headers: { authorization: `Bearer ${token}`, "content-type": "application/json" },
957
+ body: JSON.stringify(event),
958
+ signal: controller.signal
959
+ });
960
+ if (res.status >= 200 && res.status < 300) {
961
+ const decision = parseDecision(await res.json().catch(() => void 0));
962
+ return { result: "delivered", status: res.status, decision };
963
+ }
964
+ let code;
965
+ try {
966
+ const body = await res.json();
967
+ const parsed = errorEnvelopeSchema.safeParse(body);
968
+ if (parsed.success) code = parsed.data.error.code;
969
+ } catch {
970
+ }
971
+ return { result: classify(res.status, code), status: res.status, code };
972
+ } catch {
973
+ return { result: "retry" };
974
+ } finally {
975
+ clearTimeout(timer);
976
+ }
977
+ }
978
+ function drainQueue(token, deadline) {
979
+ return queue.drain(
980
+ (e) => {
981
+ const remaining = deadline - clock();
982
+ return attempt(e, token, Math.max(1, Math.min(timeoutMs, remaining))).then((a) => a.result);
983
+ },
984
+ { max: drainMax, stopWhen: () => deadline - clock() < MIN_DRAIN_ATTEMPT_MS }
985
+ );
986
+ }
987
+ return {
988
+ async send(event) {
989
+ const deadline = clock() + totalBudgetMs;
990
+ const token = await getToken(config.tokenOptions);
991
+ if (token === null) {
992
+ queue.enqueue(event);
993
+ return { outcome: "queued" };
994
+ }
995
+ const a = await attempt(event, token, Math.min(timeoutMs, totalBudgetMs));
996
+ let outcome;
997
+ if (a.result === "delivered") {
998
+ outcome = "delivered";
999
+ } else if (a.result === "retry") {
1000
+ queue.enqueue(event);
1001
+ outcome = "queued";
1002
+ } else {
1003
+ outcome = "dropped";
1004
+ }
1005
+ const drained = await drainQueue(token, deadline);
1006
+ const result = { outcome, drained };
1007
+ if (a.status !== void 0) result.status = a.status;
1008
+ if (a.code !== void 0) result.code = a.code;
1009
+ if (a.decision !== void 0) result.decision = a.decision;
1010
+ return result;
1011
+ },
1012
+ async drainNow() {
1013
+ const token = await getToken(config.tokenOptions);
1014
+ if (token === null) return EMPTY_DRAIN;
1015
+ return drainQueue(token, clock() + totalBudgetMs);
1016
+ }
1017
+ };
1018
+ }
1019
+
1020
+ // src/index.ts
1021
+ var AGENT_CORE_VERSION = "0.0.0";
1022
+ // Annotate the CommonJS export names for ESM import in node:
1023
+ 0 && (module.exports = {
1024
+ AGENT_CORE_VERSION,
1025
+ AGENT_SESSION_STATUSES,
1026
+ APPROVAL_COLLAPSE_WINDOW_MS,
1027
+ BIRDYBEEP_EVENT_TYPES,
1028
+ BODY_MAX_CHARS,
1029
+ CLAIM_RECLAIM_MS,
1030
+ DEFAULT_DEDUP_WINDOW_MS,
1031
+ DEFAULT_DRAIN_MAX,
1032
+ DEFAULT_SEND_TIMEOUT_MS,
1033
+ DEFAULT_TOTAL_BUDGET_MS,
1034
+ ERROR_CODES,
1035
+ ERROR_STATUS,
1036
+ FileTokenStore,
1037
+ HARNESS_IDS,
1038
+ INTEGRATION_STATUSES,
1039
+ KeychainTokenStore,
1040
+ LABEL_MAX_CHARS,
1041
+ LocalEventQueue,
1042
+ MAX_AGENT_EVENT_BYTES,
1043
+ METADATA_MAX_DEPTH,
1044
+ METADATA_MAX_KEYS,
1045
+ METADATA_VALUE_MAX_CHARS,
1046
+ NormalizeError,
1047
+ QUEUE_RETENTION_MS,
1048
+ RecentEventLedger,
1049
+ STATUS_REPORT_MAX_ITEMS,
1050
+ STATUS_REPORT_MAX_PAYLOAD_BYTES,
1051
+ TITLE_MAX_CHARS,
1052
+ agentEventsRequestSchema,
1053
+ approvalCollapseIdentity,
1054
+ birdyBeepAgentEventSchema,
1055
+ birdyBeepConfigDir,
1056
+ birdyBeepDataDir,
1057
+ clearToken,
1058
+ collectMachineSignals,
1059
+ createSender,
1060
+ defaultKeychainBackend,
1061
+ errorCodeSchema,
1062
+ errorEnvelopeSchema,
1063
+ eventIdentity,
1064
+ eventMetadataSchema,
1065
+ eventTypeSchema,
1066
+ fingerprintFromSignals,
1067
+ getMachineFingerprintHash,
1068
+ getMachineIdentity,
1069
+ getMachineLabel,
1070
+ getOS,
1071
+ getToken,
1072
+ harnessSchema,
1073
+ hashPath,
1074
+ integrationStatusItemSchema,
1075
+ integrationStatusReportSchema,
1076
+ integrationStatusResponseSchema,
1077
+ integrationStatusSchema,
1078
+ isWithinMaxAgentEventSize,
1079
+ isoDateTimeSchema,
1080
+ machineSchema,
1081
+ macosKeychainBackend,
1082
+ normalizeEvent,
1083
+ pairStartRequestSchema,
1084
+ pairStartResponseSchema,
1085
+ pairTokenRequestSchema,
1086
+ pairTokenResponseSchema,
1087
+ redactSecrets,
1088
+ resolveTokenStore,
1089
+ runAgentHook,
1090
+ scrubAbsolutePaths,
1091
+ sessionStatusSchema,
1092
+ setToken,
1093
+ successEnvelopeSchema,
1094
+ truncate,
1095
+ unavailableKeychainBackend,
1096
+ workspaceSchema
1097
+ });
1098
+ //# sourceMappingURL=index.cjs.map