@raindrop-ai/openai-managed-agents 0.0.3 → 0.0.5

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.js CHANGED
@@ -25,7 +25,36 @@ __export(index_exports, {
25
25
  });
26
26
  module.exports = __toCommonJS(index_exports);
27
27
 
28
- // ../core/dist/chunk-BYBMRLZM.js
28
+ // ../core/dist/chunk-ZCB62SIO.js
29
+ var __create = Object.create;
30
+ var __defProp2 = Object.defineProperty;
31
+ var __getOwnPropDesc2 = Object.getOwnPropertyDescriptor;
32
+ var __getOwnPropNames2 = Object.getOwnPropertyNames;
33
+ var __getProtoOf = Object.getPrototypeOf;
34
+ var __hasOwnProp2 = Object.prototype.hasOwnProperty;
35
+ var __commonJS = (cb, mod) => function __require() {
36
+ try {
37
+ return mod || (0, cb[__getOwnPropNames2(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
38
+ } catch (e) {
39
+ throw mod = 0, e;
40
+ }
41
+ };
42
+ var __copyProps2 = (to, from, except, desc) => {
43
+ if (from && typeof from === "object" || typeof from === "function") {
44
+ for (let key of __getOwnPropNames2(from))
45
+ if (!__hasOwnProp2.call(to, key) && key !== except)
46
+ __defProp2(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc2(from, key)) || desc.enumerable });
47
+ }
48
+ return to;
49
+ };
50
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps2(
51
+ // If the importer is in node compatibility mode or this is not an ESM
52
+ // file that has been converted to a CommonJS file using a Babel-
53
+ // compatible transform (i.e. "__esModule" has not been set), then set
54
+ // "default" to the CommonJS "module.exports" for node compatibility.
55
+ isNodeMode || !mod || !mod.__esModule ? __defProp2(target, "default", { value: mod, enumerable: true }) : target,
56
+ mod
57
+ ));
29
58
  var EVAL_CORRELATION_ID_ATTRIBUTE = "raindrop.eval_correlation_id";
30
59
  var PROBE_SCOPE = { correlationId: "" };
31
60
  var EvalScopeStorage = class {
@@ -87,21 +116,246 @@ function evalScopeAttributes() {
87
116
  return { [EVAL_CORRELATION_ID_ATTRIBUTE]: scope.correlationId };
88
117
  }
89
118
 
90
- // ../core/dist/chunk-I6TVJQEC.js
119
+ // ../core/dist/chunk-AX36JKJL.js
120
+ var require_NameRedactor = __commonJS({
121
+ "../redact-pii/dist/built-ins/NameRedactor.js"(exports2) {
122
+ Object.defineProperty(exports2, "__esModule", { value: true });
123
+ exports2.NameRedactor = void 0;
124
+ var greetingRegex = /(^|\.\s+)(dear|hi|hello|greetings|hey|hey there)/gi;
125
+ var closingRegex = /(thx|thanks|thank you|regards|best|[a-z]{1,30}ly|[a-z]{1,30} regards|all the best|happy [a-z]{1,30}ing|take care|have a [a-z]{1,30} (weekend|night|day))/gi;
126
+ var greetingOrClosing = new RegExp("(((" + greetingRegex.source + ")|(" + closingRegex.source + "\\s*[,.!]*))[\\s-]*)", "gi");
127
+ var genericName = new RegExp("( ?(([A-Z][a-z]{1,30})|([A-Z]\\.))){1,10}([,.]|[,.]?$)", "my");
128
+ var NameRedactor = class {
129
+ constructor(replaceWith = "PERSON_NAME") {
130
+ this.replaceWith = replaceWith;
131
+ }
132
+ redact(textToRedact) {
133
+ var _a;
134
+ greetingOrClosing.lastIndex = 0;
135
+ genericName.lastIndex = 0;
136
+ const pieces = [];
137
+ let copiedThrough = 0;
138
+ let changed = false;
139
+ let greetingOrClosingMatch = greetingOrClosing.exec(textToRedact);
140
+ while (greetingOrClosingMatch !== null) {
141
+ genericName.lastIndex = greetingOrClosing.lastIndex;
142
+ const genericNameMatch = genericName.exec(textToRedact);
143
+ if (genericNameMatch !== null && genericNameMatch.index === greetingOrClosing.lastIndex) {
144
+ const suffix = (_a = genericNameMatch[5]) !== null && _a !== void 0 ? _a : "";
145
+ const end = genericNameMatch.index + genericNameMatch[0].length;
146
+ pieces.push(textToRedact.slice(copiedThrough, genericNameMatch.index), this.replaceWith, suffix);
147
+ copiedThrough = end;
148
+ changed = true;
149
+ greetingOrClosing.lastIndex = end - suffix.length;
150
+ }
151
+ greetingOrClosingMatch = greetingOrClosing.exec(textToRedact);
152
+ }
153
+ if (!changed)
154
+ return textToRedact;
155
+ pieces.push(textToRedact.slice(copiedThrough));
156
+ return pieces.join("");
157
+ }
158
+ };
159
+ exports2.NameRedactor = NameRedactor;
160
+ }
161
+ });
162
+ var require_simple_regexp_patterns = __commonJS({
163
+ "../redact-pii/dist/built-ins/simple-regexp-patterns.js"(exports2) {
164
+ Object.defineProperty(exports2, "__esModule", { value: true });
165
+ exports2.url = exports2.digits = exports2.credentials = exports2.password = exports2.username = exports2.emailAddress = exports2.usSocialSecurityNumber = exports2.ipAddress = exports2.phoneNumber = exports2.zipcode = exports2.streetAddress = exports2.creditCardNumber = void 0;
166
+ var aptRegex = /(apt|bldg|dept|fl|hngr|lot|pier|rm|ste|slip|trlr|unit|#)\.? *[a-z0-9-]+\b/gi;
167
+ var poBoxRegex = /P\.? ?O\.? *Box +\d+/gi;
168
+ var roadRegex = /(street|st|road|rd|avenue|ave|drive|dr|loop|court|ct|circle|cir|lane|ln|boulevard|blvd|way)\.?\b/gi;
169
+ exports2.creditCardNumber = /\d{4}[ -]?\d{4}[ -]?\d{4}[ -]?\d{4}|\d{4}[ -]?\d{6}[ -]?\d{4}\d?/g;
170
+ exports2.streetAddress = new RegExp("(\\d{1,10}\\s*(\\w{1,30} ){1,2}" + roadRegex.source + "(\\s+" + aptRegex.source + ")?)|(" + poBoxRegex.source + ")", "gi");
171
+ exports2.zipcode = /\b\d{5}\b(-\d{4})?\b/g;
172
+ exports2.phoneNumber = /(\(?\+?[0-9]{1,2}\)?[-. ]?)?(\(?[0-9]{3}\)?|[0-9]{3})[-. ]?([0-9]{3}[-. ]?[0-9]{4}|\b[A-Z0-9]{7}\b)/g;
173
+ exports2.ipAddress = /(\d{1,3}(\.\d{1,3}){3}|[0-9A-F]{4}(:[0-9A-F]{4}){5}(::|(:0000)+))/gi;
174
+ exports2.usSocialSecurityNumber = /\b\d{3}[ -.]\d{2}[ -.]\d{4}\b/g;
175
+ exports2.emailAddress = /([a-z0-9_\-.+]{1,64})@\w+(\.\w+)*/gi;
176
+ exports2.username = /(user( ?name)?|login): \S+/gi;
177
+ exports2.password = /(pass(word|phrase)?|secret): \S+/gi;
178
+ exports2.credentials = /(login( cred(ential)?s| info(rmation)?)?|cred(ential)?s) ?:\s*\S+\s+\/?\s*\S+/gi;
179
+ exports2.digits = /\b\d{4,}\b/g;
180
+ exports2.url = /([^\s:/?#]{1,64}):\/\/([^/?#\s]*)([^?#\s]*)(\?([^#\s]*))?(#([^\s]*))?/g;
181
+ }
182
+ });
183
+ var require_utils = __commonJS({
184
+ "../redact-pii/dist/utils.js"(exports2) {
185
+ Object.defineProperty(exports2, "__esModule", { value: true });
186
+ exports2.snakeCase = exports2.isSyncRedactor = exports2.isSimpleRegexpCustomRedactorConfig = void 0;
187
+ function isSimpleRegexpCustomRedactorConfig(redactor2) {
188
+ return typeof redactor2.regexpPattern !== "undefined";
189
+ }
190
+ exports2.isSimpleRegexpCustomRedactorConfig = isSimpleRegexpCustomRedactorConfig;
191
+ function isSyncRedactor(redactor2) {
192
+ return typeof redactor2.redact === "function";
193
+ }
194
+ exports2.isSyncRedactor = isSyncRedactor;
195
+ function snakeCase(input = "") {
196
+ var _a;
197
+ const words = (_a = input.match(/[A-Z]{2,}(?=[A-Z][a-z]|[0-9]|\b)|[A-Z]?[a-z]+|[A-Z]+|[0-9]+/g)) !== null && _a !== void 0 ? _a : [];
198
+ return words.map((word) => word.toLowerCase()).join("_");
199
+ }
200
+ exports2.snakeCase = snakeCase;
201
+ }
202
+ });
203
+ var require_SimpleRegexpRedactor = __commonJS({
204
+ "../redact-pii/dist/built-ins/SimpleRegexpRedactor.js"(exports2) {
205
+ Object.defineProperty(exports2, "__esModule", { value: true });
206
+ exports2.SimpleRegexpRedactor = void 0;
207
+ var utils_1 = require_utils();
208
+ var SimpleRegexpRedactor = class {
209
+ constructor({ replaceWith = (0, utils_1.snakeCase)().toUpperCase(), regexpPattern: regexpMatcher }) {
210
+ this.replaceWith = replaceWith;
211
+ this.regexpMatcher = regexpMatcher;
212
+ }
213
+ redact(textToRedact) {
214
+ return textToRedact.replace(this.regexpMatcher, this.replaceWith);
215
+ }
216
+ };
217
+ exports2.SimpleRegexpRedactor = SimpleRegexpRedactor;
218
+ }
219
+ });
220
+ var require_composition = __commonJS({
221
+ "../redact-pii/dist/composition.js"(exports2) {
222
+ Object.defineProperty(exports2, "__esModule", { value: true });
223
+ exports2.composeChildRedactors = void 0;
224
+ var NameRedactor_1 = require_NameRedactor();
225
+ var simpleRegexpBuiltIns = require_simple_regexp_patterns();
226
+ var SimpleRegexpRedactor_1 = require_SimpleRegexpRedactor();
227
+ var utils_1 = require_utils();
228
+ function normalizeCustomRedactorConfig(redactorConfig) {
229
+ return (0, utils_1.isSimpleRegexpCustomRedactorConfig)(redactorConfig) ? new SimpleRegexpRedactor_1.SimpleRegexpRedactor({
230
+ regexpPattern: redactorConfig.regexpPattern,
231
+ replaceWith: redactorConfig.replaceWith
232
+ }) : redactorConfig;
233
+ }
234
+ function composeChildRedactors(opts = {}) {
235
+ var _a, _b, _c, _d;
236
+ const childRedactors = [];
237
+ if (opts.customRedactors && opts.customRedactors.before) {
238
+ opts.customRedactors.before.map(normalizeCustomRedactorConfig).forEach((redactor2) => childRedactors.push(redactor2));
239
+ }
240
+ for (const regexpName of Object.keys(simpleRegexpBuiltIns)) {
241
+ if (!opts.builtInRedactors || !opts.builtInRedactors[regexpName] || opts.builtInRedactors[regexpName].enabled !== false) {
242
+ childRedactors.push(new SimpleRegexpRedactor_1.SimpleRegexpRedactor({
243
+ regexpPattern: simpleRegexpBuiltIns[regexpName],
244
+ replaceWith: ((_b = (_a = opts.builtInRedactors) === null || _a === void 0 ? void 0 : _a[regexpName]) === null || _b === void 0 ? void 0 : _b.replaceWith) || opts.globalReplaceWith || (0, utils_1.snakeCase)(regexpName).toUpperCase()
245
+ }));
246
+ }
247
+ }
248
+ if (!opts.builtInRedactors || !opts.builtInRedactors.names || opts.builtInRedactors.names.enabled !== false) {
249
+ childRedactors.push(new NameRedactor_1.NameRedactor(((_d = (_c = opts.builtInRedactors) === null || _c === void 0 ? void 0 : _c.names) === null || _d === void 0 ? void 0 : _d.replaceWith) || opts.globalReplaceWith));
250
+ }
251
+ if (opts.customRedactors && opts.customRedactors.after) {
252
+ opts.customRedactors.after.map(normalizeCustomRedactorConfig).forEach((redactor2) => childRedactors.push(redactor2));
253
+ }
254
+ return childRedactors;
255
+ }
256
+ exports2.composeChildRedactors = composeChildRedactors;
257
+ }
258
+ });
259
+ var require_AsyncCompositeRedactor = __commonJS({
260
+ "../redact-pii/dist/AsyncCompositeRedactor.js"(exports2) {
261
+ var __awaiter = exports2 && exports2.__awaiter || function(thisArg, _arguments, P, generator) {
262
+ function adopt(value) {
263
+ return value instanceof P ? value : new P(function(resolve2) {
264
+ resolve2(value);
265
+ });
266
+ }
267
+ return new (P || (P = Promise))(function(resolve2, reject) {
268
+ function fulfilled(value) {
269
+ try {
270
+ step(generator.next(value));
271
+ } catch (e) {
272
+ reject(e);
273
+ }
274
+ }
275
+ function rejected(value) {
276
+ try {
277
+ step(generator["throw"](value));
278
+ } catch (e) {
279
+ reject(e);
280
+ }
281
+ }
282
+ function step(result) {
283
+ result.done ? resolve2(result.value) : adopt(result.value).then(fulfilled, rejected);
284
+ }
285
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
286
+ });
287
+ };
288
+ Object.defineProperty(exports2, "__esModule", { value: true });
289
+ exports2.AsyncCompositeRedactor = void 0;
290
+ var composition_1 = require_composition();
291
+ var utils_1 = require_utils();
292
+ var AsyncCompositeRedactor = class {
293
+ constructor(opts) {
294
+ this.childRedactors = [];
295
+ this.redactAsync = (textToRedact) => __awaiter(this, void 0, void 0, function* () {
296
+ for (const redactor2 of this.childRedactors) {
297
+ if ((0, utils_1.isSyncRedactor)(redactor2)) {
298
+ textToRedact = redactor2.redact(textToRedact);
299
+ } else {
300
+ textToRedact = yield redactor2.redactAsync(textToRedact);
301
+ }
302
+ }
303
+ return textToRedact;
304
+ });
305
+ this.childRedactors = (0, composition_1.composeChildRedactors)(opts);
306
+ }
307
+ };
308
+ exports2.AsyncCompositeRedactor = AsyncCompositeRedactor;
309
+ }
310
+ });
311
+ var require_SyncCompositeRedactor = __commonJS({
312
+ "../redact-pii/dist/SyncCompositeRedactor.js"(exports2) {
313
+ Object.defineProperty(exports2, "__esModule", { value: true });
314
+ exports2.SyncCompositeRedactor = void 0;
315
+ var composition_1 = require_composition();
316
+ var SyncCompositeRedactor = class {
317
+ constructor(opts) {
318
+ this.childRedactors = [];
319
+ this.redact = (textToRedact) => {
320
+ for (const redactor2 of this.childRedactors) {
321
+ textToRedact = redactor2.redact(textToRedact);
322
+ }
323
+ return textToRedact;
324
+ };
325
+ this.childRedactors = (0, composition_1.composeChildRedactors)(opts);
326
+ }
327
+ };
328
+ exports2.SyncCompositeRedactor = SyncCompositeRedactor;
329
+ }
330
+ });
331
+ var require_dist = __commonJS({
332
+ "../redact-pii/dist/index.js"(exports2) {
333
+ Object.defineProperty(exports2, "__esModule", { value: true });
334
+ exports2.SyncRedactor = exports2.AsyncRedactor = void 0;
335
+ var AsyncCompositeRedactor_1 = require_AsyncCompositeRedactor();
336
+ Object.defineProperty(exports2, "AsyncRedactor", { enumerable: true, get: function() {
337
+ return AsyncCompositeRedactor_1.AsyncCompositeRedactor;
338
+ } });
339
+ var SyncCompositeRedactor_1 = require_SyncCompositeRedactor();
340
+ Object.defineProperty(exports2, "SyncRedactor", { enumerable: true, get: function() {
341
+ return SyncCompositeRedactor_1.SyncCompositeRedactor;
342
+ } });
343
+ }
344
+ });
91
345
  var STATE_KEY = /* @__PURE__ */ Symbol.for("raindrop.tracing.replayTraceRoutingState");
92
346
  function state() {
93
347
  const holder = globalThis;
94
348
  let current = holder[STATE_KEY];
95
349
  if (!current) {
96
- let storage = null;
350
+ let storage2 = null;
97
351
  try {
98
352
  const Ctor = globalThis.RAINDROP_ASYNC_LOCAL_STORAGE;
99
- if (Ctor) storage = new Ctor();
353
+ if (Ctor) storage2 = new Ctor();
100
354
  } catch (e) {
101
- storage = null;
355
+ storage2 = null;
102
356
  }
103
357
  current = {
104
- storage,
358
+ storage: storage2,
105
359
  syncDestination: void 0,
106
360
  spanDestinations: /* @__PURE__ */ new WeakMap()
107
361
  };
@@ -249,12 +503,12 @@ var NO_AMBIENT_ROUTING_BIND_WARNING = "[raindrop] This runtime's AsyncLocalStora
249
503
  var ROUTING_STORAGE_KEY = /* @__PURE__ */ Symbol.for("raindrop.tracing.routingContextStorage");
250
504
  function routingStorage() {
251
505
  const holder = globalThis;
252
- let storage = holder[ROUTING_STORAGE_KEY];
253
- if (!storage) {
254
- storage = new ContextStackStorage(NO_AMBIENT_ROUTING_BIND_WARNING);
255
- holder[ROUTING_STORAGE_KEY] = storage;
506
+ let storage2 = holder[ROUTING_STORAGE_KEY];
507
+ if (!storage2) {
508
+ storage2 = new ContextStackStorage(NO_AMBIENT_ROUTING_BIND_WARNING);
509
+ holder[ROUTING_STORAGE_KEY] = storage2;
256
510
  }
257
- return storage;
511
+ return storage2;
258
512
  }
259
513
  function currentRoutingBinding() {
260
514
  const bound = routingStorage().current();
@@ -540,6 +794,14 @@ function appGitPropertiesFromAttributes(attributes) {
540
794
  }
541
795
  return properties;
542
796
  }
797
+ function normalizeFeatureFlags(input) {
798
+ if (Array.isArray(input)) {
799
+ return Object.fromEntries(input.map((name) => [name, "true"]));
800
+ }
801
+ return Object.fromEntries(
802
+ Object.entries(input).map(([k, v]) => [k, String(v)])
803
+ );
804
+ }
543
805
  function getCrypto() {
544
806
  const c = globalThis.crypto;
545
807
  return c;
@@ -641,11 +903,91 @@ function runWithTracingSuppressed(fn) {
641
903
  return fn();
642
904
  }
643
905
  }
906
+ var MAX_QUOTED_ITEMS = 3;
907
+ function isRecord(value) {
908
+ return typeof value === "object" && value !== null && !Array.isArray(value);
909
+ }
910
+ function itemLabel(item) {
911
+ if (typeof item.event_id === "string" && item.event_id) return `event ${item.event_id}`;
912
+ if (typeof item.index === "number") return `item ${item.index}`;
913
+ return "item";
914
+ }
915
+ function describeRejected(item) {
916
+ const issues = Array.isArray(item.issues) ? item.issues : [];
917
+ const text = issues.filter(isRecord).map((issue) => {
918
+ const path = typeof issue.path === "string" && issue.path ? `${issue.path}: ` : "";
919
+ return `${path}${typeof issue.message === "string" ? issue.message : "invalid"}`;
920
+ }).join("; ");
921
+ return `${itemLabel(item)} (${text || "invalid"})`;
922
+ }
923
+ function describeWarning(item) {
924
+ if (typeof item.message === "string" && item.message) return `${itemLabel(item)}: ${item.message}`;
925
+ const fields = Array.isArray(item.fields) ? item.fields.filter((f) => typeof f === "string") : [];
926
+ return `${itemLabel(item)}: ignored ${fields.join(", ") || "input"}`;
927
+ }
928
+ function summarize(items, describe) {
929
+ const quoted = items.slice(0, MAX_QUOTED_ITEMS).map(describe).join(" | ");
930
+ const more = items.length > MAX_QUOTED_ITEMS ? ` (+${items.length - MAX_QUOTED_ITEMS} more)` : "";
931
+ return `${quoted}${more}`;
932
+ }
933
+ function reportIngestResponse(bodyText, prefix, opName) {
934
+ if (!bodyText) return;
935
+ let body;
936
+ try {
937
+ body = JSON.parse(bodyText);
938
+ } catch (e) {
939
+ return;
940
+ }
941
+ if (!isRecord(body)) return;
942
+ const rejected = Array.isArray(body.rejected) ? body.rejected.filter(isRecord) : [];
943
+ if (rejected.length > 0) {
944
+ rateLimitedLog(
945
+ `${prefix}.ingest_rejected`,
946
+ () => console.warn(
947
+ `${prefix} ${opName}: ingest rejected ${rejected.length} item(s), which were not stored: ${summarize(rejected, describeRejected)}`
948
+ )
949
+ );
950
+ }
951
+ const warnings = Array.isArray(body.warnings) ? body.warnings.filter(isRecord) : [];
952
+ if (warnings.length > 0) {
953
+ rateLimitedLog(
954
+ `${prefix}.ingest_warnings`,
955
+ () => console.warn(
956
+ `${prefix} ${opName}: ingest accepted ${warnings.length} item(s) with changes: ${summarize(warnings, describeWarning)}`
957
+ )
958
+ );
959
+ }
960
+ }
961
+ var DEFAULT_FLUSH_TIMEOUT_MS = 1e4;
644
962
  var DEFAULT_REQUEST_TIMEOUT_MS = 3e4;
645
963
  var MAX_RETRY_DELAY_MS = 3e4;
646
964
  function wait(ms) {
647
965
  return new Promise((resolve2) => setTimeout(resolve2, ms));
648
966
  }
967
+ function waitUnlessAborted(ms, signal) {
968
+ if (!signal) return wait(ms);
969
+ if (signal.aborted) return Promise.resolve();
970
+ return new Promise((resolve2) => {
971
+ const done = () => {
972
+ clearTimeout(timer);
973
+ signal.removeEventListener("abort", done);
974
+ resolve2();
975
+ };
976
+ const timer = setTimeout(done, ms);
977
+ signal.addEventListener("abort", done, { once: true });
978
+ });
979
+ }
980
+ function eitherSignal(a, b) {
981
+ if (!b) return a;
982
+ const any = AbortSignal.any;
983
+ if (typeof any === "function") return any([a, b]);
984
+ const controller = new AbortController();
985
+ const abort = () => controller.abort();
986
+ if (a.aborted || b.aborted) abort();
987
+ a.addEventListener("abort", abort, { once: true });
988
+ b.addEventListener("abort", abort, { once: true });
989
+ return controller.signal;
990
+ }
649
991
  function formatEndpoint(endpoint) {
650
992
  if (!endpoint) return void 0;
651
993
  return endpoint.endsWith("/") ? endpoint : `${endpoint}/`;
@@ -690,6 +1032,26 @@ async function raceWithTimeout(promise, timeoutMs) {
690
1032
  if (timer) clearTimeout(timer);
691
1033
  return settledInTime;
692
1034
  }
1035
+ var InFlightCutOff = class {
1036
+ constructor() {
1037
+ this.controller = new AbortController();
1038
+ }
1039
+ get signal() {
1040
+ return this.controller.signal;
1041
+ }
1042
+ cut() {
1043
+ this.controller.abort();
1044
+ this.controller = new AbortController();
1045
+ }
1046
+ };
1047
+ async function awaitEarlierSends(earlier, deadlineAt, cutOff, isRunning) {
1048
+ if (earlier.length === 0) return { settled: true, stillRunning: 0 };
1049
+ const settled = await raceWithTimeout(Promise.allSettled(earlier), deadlineAt - Date.now());
1050
+ if (settled) return { settled, stillRunning: 0 };
1051
+ const stillRunning = earlier.filter(isRunning).length;
1052
+ if (stillRunning > 0) cutOff.cut();
1053
+ return { settled: stillRunning === 0, stillRunning };
1054
+ }
693
1055
  function parseRetryAfter(headers) {
694
1056
  var _a;
695
1057
  const value = (_a = headers.get("Retry-After")) != null ? _a : headers.get("retry-after");
@@ -713,45 +1075,80 @@ function getRetryDelayMs(attemptNumber, previousError) {
713
1075
  const factor = Math.pow(2, attemptNumber - 2);
714
1076
  return Math.min(base * factor, MAX_RETRY_DELAY_MS);
715
1077
  }
716
- async function withRetry(operation, opName, opts) {
717
- const prefix = opts.sdkName ? `[raindrop-ai/${opts.sdkName}]` : "[raindrop-ai/core]";
718
- let lastError = void 0;
719
- for (let attemptNumber = 1; attemptNumber <= opts.maxAttempts; attemptNumber++) {
720
- if (attemptNumber > 1) {
721
- const delay = getRetryDelayMs(attemptNumber, lastError);
722
- if (opts.debug) {
723
- console.warn(
724
- `${prefix} ${opName} retry ${attemptNumber}/${opts.maxAttempts} in ${delay}ms`
725
- );
726
- }
727
- if (delay > 0) await wait(delay);
728
- } else if (opts.debug) {
729
- console.log(`${prefix} ${opName} attempt ${attemptNumber}/${opts.maxAttempts}`);
730
- }
731
- try {
732
- return await operation();
733
- } catch (err) {
734
- lastError = err;
735
- if (opts.debug) {
736
- const msg = err instanceof Error ? err.message : String(err);
737
- console.warn(
738
- `${prefix} ${opName} attempt ${attemptNumber} failed: ${msg}${attemptNumber === opts.maxAttempts ? " (no more retries)" : ""}`
739
- );
740
- }
741
- if (lastError && typeof lastError === "object" && "retryable" in lastError && !lastError.retryable)
742
- break;
743
- if (attemptNumber === opts.maxAttempts) break;
744
- }
1078
+ var sendPausedUntil = /* @__PURE__ */ new Map();
1079
+ function originOf(url) {
1080
+ try {
1081
+ return new URL(url).origin;
1082
+ } catch (e) {
1083
+ return url;
745
1084
  }
746
- throw lastError instanceof Error ? lastError : new Error(String(lastError));
1085
+ }
1086
+ function noteSendPause(url, status, retryAfterMs) {
1087
+ var _a;
1088
+ if (status !== 429 && status !== 503 || typeof retryAfterMs !== "number") return;
1089
+ const until = Date.now() + Math.min(Math.max(0, retryAfterMs), MAX_RETRY_DELAY_MS);
1090
+ const origin = originOf(url);
1091
+ sendPausedUntil.set(origin, Math.max((_a = sendPausedUntil.get(origin)) != null ? _a : 0, until));
1092
+ }
1093
+ function pausedUntil(url) {
1094
+ const until = sendPausedUntil.get(originOf(url));
1095
+ if (until === void 0) return void 0;
1096
+ if (until <= Date.now()) {
1097
+ sendPausedUntil.delete(originOf(url));
1098
+ return void 0;
1099
+ }
1100
+ return Math.min(until, Date.now() + MAX_RETRY_DELAY_MS);
1101
+ }
1102
+ function prefixFor(opts) {
1103
+ return opts.sdkName ? `[raindrop-ai/${opts.sdkName}]` : "[raindrop-ai/core]";
747
1104
  }
748
1105
  async function postJson(url, body, headers, opts) {
1106
+ var _a;
1107
+ const outcome = await sendJson(url, body, headers, { ...opts, deadlineAt: void 0 });
1108
+ if (outcome.status === "delivered") return;
1109
+ throw (_a = outcome.error) != null ? _a : new Error(`${redactUrlForLog(url)}: not sent (${outcome.status})`);
1110
+ }
1111
+ async function sendJson(url, body, headers, opts) {
749
1112
  var _a;
750
1113
  const opName = `POST ${redactUrlForLog(url)}`;
1114
+ const prefix = prefixFor(opts);
751
1115
  const timeoutMs = (_a = opts.timeoutMs) != null ? _a : DEFAULT_REQUEST_TIMEOUT_MS;
752
- await withRetry(
753
- async () => {
754
- const resp = await runWithTracingSuppressed(
1116
+ const deadlineAt = opts.deadlineAt;
1117
+ const remaining = () => deadlineAt === void 0 ? Infinity : deadlineAt - Date.now();
1118
+ let lastError;
1119
+ const cutOff = opts.cutOff;
1120
+ for (let attempt = 1; attempt <= opts.maxAttempts; attempt++) {
1121
+ if (cutOff == null ? void 0 : cutOff.aborted) return { status: "deferred", error: lastError };
1122
+ if (attempt > 1) {
1123
+ const delay = getRetryDelayMs(attempt, lastError);
1124
+ if (delay >= remaining()) {
1125
+ return { status: "deferred", retryAtMs: Date.now() + delay, error: lastError };
1126
+ }
1127
+ if (opts.debug) {
1128
+ console.warn(`${prefix} ${opName} retry ${attempt}/${opts.maxAttempts} in ${delay}ms`);
1129
+ }
1130
+ if (delay > 0) await waitUnlessAborted(delay, cutOff);
1131
+ if (cutOff == null ? void 0 : cutOff.aborted) return { status: "deferred", error: lastError };
1132
+ } else if (opts.debug) {
1133
+ console.log(`${prefix} ${opName} attempt ${attempt}/${opts.maxAttempts}`);
1134
+ }
1135
+ const pauseEnd = pausedUntil(url);
1136
+ if (pauseEnd !== void 0) {
1137
+ if (deadlineAt !== void 0 && pauseEnd >= deadlineAt) {
1138
+ return { status: "deferred", retryAtMs: pauseEnd, error: lastError };
1139
+ }
1140
+ await waitUnlessAborted(Math.max(0, pauseEnd - Date.now()), cutOff);
1141
+ if (cutOff == null ? void 0 : cutOff.aborted) return { status: "deferred", retryAtMs: pauseEnd, error: lastError };
1142
+ }
1143
+ const left = remaining();
1144
+ if (left <= 0) return { status: "deferred", error: lastError };
1145
+ const attemptTimeoutMs = Math.min(timeoutMs, left);
1146
+ const clampedByDeadline = attemptTimeoutMs < timeoutMs;
1147
+ const timeoutSignal = AbortSignal.timeout(attemptTimeoutMs);
1148
+ const signal = eitherSignal(timeoutSignal, cutOff);
1149
+ let resp;
1150
+ try {
1151
+ resp = await runWithTracingSuppressed(
755
1152
  () => fetch(url, {
756
1153
  method: "POST",
757
1154
  redirect: headers["X-Raindrop-Replay-Id"] !== void 0 ? "error" : "follow",
@@ -760,24 +1157,98 @@ async function postJson(url, body, headers, opts) {
760
1157
  ...headers
761
1158
  },
762
1159
  body: JSON.stringify(body),
763
- signal: AbortSignal.timeout(timeoutMs)
1160
+ signal
764
1161
  })
765
1162
  );
766
- if (!resp.ok) {
767
- const text = await resp.text().catch(() => "");
768
- const err = new Error(
769
- `HTTP ${resp.status} ${resp.statusText}${text ? `: ${text}` : ""}`
770
- );
771
- const retryAfterMs = parseRetryAfter(resp.headers);
772
- if (typeof retryAfterMs === "number") err.retryAfterMs = retryAfterMs;
773
- err.retryable = resp.status === 429 || resp.status >= 500;
774
- throw err;
1163
+ } catch (err) {
1164
+ const error = err instanceof Error ? err : new Error(String(err));
1165
+ if ((cutOff == null ? void 0 : cutOff.aborted) || timeoutSignal.aborted && clampedByDeadline) {
1166
+ return { status: "cut_off", error };
775
1167
  }
776
- },
777
- opName,
778
- opts
1168
+ lastError = error;
1169
+ logAttemptFailure(opts, prefix, opName, attempt, error);
1170
+ continue;
1171
+ }
1172
+ if (!resp.ok) {
1173
+ const text = await resp.text().catch(() => "");
1174
+ const error = new Error(
1175
+ `HTTP ${resp.status} ${resp.statusText}${text ? `: ${text}` : ""}`
1176
+ );
1177
+ const retryAfterMs = parseRetryAfter(resp.headers);
1178
+ if (typeof retryAfterMs === "number") error.retryAfterMs = retryAfterMs;
1179
+ noteSendPause(url, resp.status, retryAfterMs);
1180
+ error.retryable = resp.status === 408 || resp.status === 429 || resp.status >= 500;
1181
+ if (!error.retryable) return { status: "rejected", error };
1182
+ lastError = error;
1183
+ logAttemptFailure(opts, prefix, opName, attempt, error);
1184
+ continue;
1185
+ }
1186
+ try {
1187
+ reportIngestResponse(await resp.text(), prefix, opName);
1188
+ } catch (e) {
1189
+ }
1190
+ return { status: "delivered" };
1191
+ }
1192
+ return { status: "failed", error: lastError != null ? lastError : new Error(`${opName}: no attempts`) };
1193
+ }
1194
+ function logAttemptFailure(opts, prefix, opName, attempt, error) {
1195
+ if (!opts.debug) return;
1196
+ console.warn(
1197
+ `${prefix} ${opName} attempt ${attempt} failed: ${error.message}${attempt === opts.maxAttempts ? " (no more retries)" : ""}`
1198
+ );
1199
+ }
1200
+ function asCompat(result) {
1201
+ return result;
1202
+ }
1203
+ var DELIVERED = Object.freeze({
1204
+ delivered: true,
1205
+ timedOut: false,
1206
+ dropped: 0,
1207
+ queued: 0,
1208
+ inFlight: 0
1209
+ });
1210
+ function fateOf(outcome, shuttingDown) {
1211
+ switch (outcome.status) {
1212
+ case "delivered":
1213
+ case "rejected":
1214
+ return "delivered";
1215
+ case "deferred":
1216
+ return shuttingDown ? "dropped" : "queued";
1217
+ default:
1218
+ return "dropped";
1219
+ }
1220
+ }
1221
+ function summarize2(fates, inFlight, timedOut) {
1222
+ const dropped = fates.filter((fate) => fate === "dropped").length;
1223
+ const queued = fates.filter((fate) => fate === "queued").length;
1224
+ return {
1225
+ delivered: dropped === 0 && queued === 0 && inFlight === 0,
1226
+ timedOut: timedOut || queued > 0 || inFlight > 0,
1227
+ dropped,
1228
+ queued,
1229
+ inFlight
1230
+ };
1231
+ }
1232
+ function combineResults(...results) {
1233
+ return results.reduce(
1234
+ (acc, r) => ({
1235
+ delivered: acc.delivered && r.delivered,
1236
+ timedOut: acc.timedOut || r.timedOut,
1237
+ dropped: acc.dropped + r.dropped,
1238
+ queued: acc.queued + r.queued,
1239
+ inFlight: acc.inFlight + r.inFlight
1240
+ }),
1241
+ { ...DELIVERED }
779
1242
  );
780
1243
  }
1244
+ function describeUndelivered(what, result) {
1245
+ const parts = [
1246
+ result.dropped > 0 ? `${result.dropped} dropped` : "",
1247
+ result.queued > 0 ? `${result.queued} still queued` : "",
1248
+ result.inFlight > 0 ? `${result.inFlight} still sending` : ""
1249
+ ].filter(Boolean);
1250
+ return `${what} returned before everything was delivered: ${parts.join(", ")}`;
1251
+ }
781
1252
  var DEFAULT_MAX_TEXT_FIELD_CHARS = 1e6;
782
1253
  var TRUNCATION_MARKER = "...[truncated by raindrop]";
783
1254
  var BOUNDED_CLONE_MAX_DEPTH = 12;
@@ -1044,7 +1515,245 @@ function normalizeProjectId(raw, opts) {
1044
1515
  function projectIdHeaders(projectId) {
1045
1516
  return projectId ? { [PROJECT_ID_HEADER]: projectId } : {};
1046
1517
  }
1047
- var SHUTDOWN_DEADLINE_MS = 1e4;
1518
+ var import_redact_pii = __toESM(require_dist(), 1);
1519
+ var DEFAULT_PII_REDACTION_OPTIONS = {
1520
+ builtInRedactors: {
1521
+ credentials: { replaceWith: "<REDACTED CREDENTIALS>" },
1522
+ creditCardNumber: { replaceWith: "<REDACTED CREDIT CARD NUMBER>" },
1523
+ // Any run of 4+ digits: too broad.
1524
+ digits: { enabled: false },
1525
+ emailAddress: { replaceWith: "<REDACTED EMAIL>" },
1526
+ ipAddress: { enabled: false },
1527
+ names: { enabled: false },
1528
+ // Matches (pass(word|phrase)|secret): \S+
1529
+ password: { replaceWith: "<REDACTED SECRET>" },
1530
+ phoneNumber: { replaceWith: "<REDACTED PHONE NUMBER>" },
1531
+ streetAddress: { replaceWith: "<REDACTED ADDRESS>" },
1532
+ url: { enabled: false },
1533
+ username: { enabled: false },
1534
+ usSocialSecurityNumber: { replaceWith: "<REDACTED SOCIAL SECURITY NUMBER>" },
1535
+ zipcode: { enabled: false }
1536
+ }
1537
+ };
1538
+ var redactor;
1539
+ function redactPiiText(text) {
1540
+ redactor != null ? redactor : redactor = new import_redact_pii.SyncRedactor(DEFAULT_PII_REDACTION_OPTIONS);
1541
+ return redactor.redact(text);
1542
+ }
1543
+ var KINDS = ["event", "signal", "identify"];
1544
+ var DEFAULT_PII_FIELDS = {
1545
+ event: ["ai_data.input", "ai_data.output"],
1546
+ signal: [],
1547
+ identify: []
1548
+ };
1549
+ var PROTECTED_LOCATIONS = new Set(
1550
+ [
1551
+ ["event_id"],
1552
+ ["user_id"],
1553
+ ["ai_data", "convo_id"],
1554
+ ["event"],
1555
+ ["timestamp"],
1556
+ ["is_pending"],
1557
+ ["signal_name"],
1558
+ ["signal_type"]
1559
+ ].map((keys) => JSON.stringify(keys))
1560
+ );
1561
+ var SIGNAL_PATH_ALIASES = {
1562
+ comment: "properties.comment",
1563
+ after: "properties.after"
1564
+ };
1565
+ function parsePiiFieldPath(path) {
1566
+ if (typeof path !== "string") return null;
1567
+ const segments = [];
1568
+ let i = 0;
1569
+ const n = path.length;
1570
+ let expectSeparator = false;
1571
+ while (i < n) {
1572
+ const ch = path[i];
1573
+ if (ch === "[") {
1574
+ const close = path.indexOf("]", i);
1575
+ if (path[i + 1] === '"') {
1576
+ let j2 = i + 2;
1577
+ let key = "";
1578
+ while (j2 < n && path[j2] !== '"') {
1579
+ if (path[j2] === "\\" && j2 + 1 < n) {
1580
+ key += path[j2 + 1];
1581
+ j2 += 2;
1582
+ } else {
1583
+ key += path[j2];
1584
+ j2 += 1;
1585
+ }
1586
+ }
1587
+ if (j2 >= n || path[j2 + 1] !== "]" || key === "") return null;
1588
+ segments.push({ kind: "key", key });
1589
+ i = j2 + 2;
1590
+ } else {
1591
+ if (close === -1) return null;
1592
+ const inner = path.slice(i + 1, close);
1593
+ if (inner === "") segments.push({ kind: "every" });
1594
+ else if (/^(0|[1-9]\d*)$/.test(inner))
1595
+ segments.push({ kind: "index", index: Number(inner) });
1596
+ else return null;
1597
+ i = close + 1;
1598
+ }
1599
+ expectSeparator = true;
1600
+ continue;
1601
+ }
1602
+ if (ch === ".") {
1603
+ if (!expectSeparator) return null;
1604
+ i += 1;
1605
+ expectSeparator = false;
1606
+ if (i >= n || path[i] === ".") return null;
1607
+ continue;
1608
+ }
1609
+ if (expectSeparator) return null;
1610
+ let j = i;
1611
+ while (j < n && path[j] !== "." && path[j] !== "[") j += 1;
1612
+ const token = path.slice(i, j);
1613
+ if (token === "*") segments.push({ kind: "any_key" });
1614
+ else if (token === "" || /[\]*"\s]/.test(token)) return null;
1615
+ else segments.push({ kind: "key", key: token });
1616
+ i = j;
1617
+ expectSeparator = true;
1618
+ }
1619
+ return segments.length > 0 && expectSeparator ? segments : null;
1620
+ }
1621
+ function mergePiiFieldMaps(...maps) {
1622
+ const out = { event: [], signal: [], identify: [] };
1623
+ for (const kind of KINDS) {
1624
+ const seen = /* @__PURE__ */ new Set();
1625
+ for (const map of maps) {
1626
+ const paths = map == null ? void 0 : map[kind];
1627
+ if (!Array.isArray(paths)) continue;
1628
+ for (const path of paths) {
1629
+ if (typeof path !== "string" || seen.has(path)) continue;
1630
+ seen.add(path);
1631
+ out[kind].push(path);
1632
+ }
1633
+ }
1634
+ }
1635
+ return out;
1636
+ }
1637
+ function isPlainObject(value) {
1638
+ if (value === null || typeof value !== "object" || Array.isArray(value)) return false;
1639
+ const proto = Object.getPrototypeOf(value);
1640
+ return proto === Object.prototype || proto === null;
1641
+ }
1642
+ function isProtected(location) {
1643
+ return PROTECTED_LOCATIONS.has(JSON.stringify(location));
1644
+ }
1645
+ function redactAll(value, location) {
1646
+ if (location !== null && isProtected(location)) return value;
1647
+ if (typeof value === "string") return redactPiiText(value);
1648
+ if (Array.isArray(value)) return value.map((item) => redactAll(item, null));
1649
+ if (isPlainObject(value)) {
1650
+ const out = {};
1651
+ for (const [key, child] of Object.entries(value)) {
1652
+ out[key] = redactAll(child, location === null ? null : [...location, key]);
1653
+ }
1654
+ return out;
1655
+ }
1656
+ return value;
1657
+ }
1658
+ function applyPath(value, segments, location) {
1659
+ if (segments.length === 0) return redactAll(value, location);
1660
+ const [head, ...rest] = segments;
1661
+ if (head.kind === "key" || head.kind === "any_key") {
1662
+ if (!isPlainObject(value)) return value;
1663
+ const keys = head.kind === "key" ? [head.key] : Object.keys(value);
1664
+ let out2;
1665
+ for (const key of keys) {
1666
+ if (!Object.prototype.hasOwnProperty.call(value, key)) continue;
1667
+ const child = value[key];
1668
+ const next = applyPath(child, rest, location === null ? null : [...location, key]);
1669
+ if (next !== child) {
1670
+ out2 != null ? out2 : out2 = { ...value };
1671
+ out2[key] = next;
1672
+ }
1673
+ }
1674
+ return out2 != null ? out2 : value;
1675
+ }
1676
+ if (!Array.isArray(value)) return value;
1677
+ const indices = head.kind === "index" ? head.index < value.length ? [head.index] : [] : value.keys();
1678
+ let out;
1679
+ for (const index of indices) {
1680
+ const child = value[index];
1681
+ const next = applyPath(child, rest, null);
1682
+ if (next !== child) {
1683
+ out != null ? out : out = [...value];
1684
+ out[index] = next;
1685
+ }
1686
+ }
1687
+ return out != null ? out : value;
1688
+ }
1689
+ var PiiFieldRedactor = class {
1690
+ constructor(options = {}) {
1691
+ var _a;
1692
+ this.startupMap = options.startupFieldMap;
1693
+ this.prefix = (_a = options.logPrefix) != null ? _a : "[raindrop-ai/core]";
1694
+ }
1695
+ /** Replace the server-provided layer (CONTRACT 11.2). Additive: it can only add fields. */
1696
+ setServerFieldMap(map) {
1697
+ this.serverMap = map;
1698
+ this.compiled = void 0;
1699
+ }
1700
+ /** The paths in effect for a kind, defaults first. */
1701
+ effectivePaths(kind) {
1702
+ return mergePiiFieldMaps(DEFAULT_PII_FIELDS, this.startupMap, this.serverMap)[kind];
1703
+ }
1704
+ compile() {
1705
+ if (this.compiled) return this.compiled;
1706
+ const out = { event: [], signal: [], identify: [] };
1707
+ for (const kind of KINDS) {
1708
+ for (const path of this.effectivePaths(kind)) {
1709
+ const variants = [path];
1710
+ if (kind === "signal" && SIGNAL_PATH_ALIASES[path])
1711
+ variants.push(SIGNAL_PATH_ALIASES[path]);
1712
+ for (const variant of variants) {
1713
+ const segments = parsePiiFieldPath(variant);
1714
+ if (segments) {
1715
+ out[kind].push(segments);
1716
+ } else {
1717
+ rateLimitedLog(
1718
+ `${this.prefix}.pii_field_path_invalid`,
1719
+ () => console.warn(
1720
+ `${this.prefix} redactPiiFields: ignoring invalid ${kind} path ${JSON.stringify(variant)}`
1721
+ )
1722
+ );
1723
+ }
1724
+ }
1725
+ }
1726
+ }
1727
+ this.compiled = out;
1728
+ return out;
1729
+ }
1730
+ /** The payload with every selected field redacted (a copy where changed). */
1731
+ redact(kind, payload) {
1732
+ let out = payload;
1733
+ for (const segments of this.compile()[kind]) out = applyPath(out, segments, []);
1734
+ return out;
1735
+ }
1736
+ };
1737
+ function detectRuntime() {
1738
+ if (typeof process === "object" && process && typeof process.env === "object" && process.env && typeof process.version === "string") {
1739
+ return "node";
1740
+ }
1741
+ if (typeof window === "object") return "browser";
1742
+ if (typeof WebSocketPair !== "undefined") return "cloudflare-worker";
1743
+ if (typeof EdgeRuntime === "string") return "vercel-edge";
1744
+ if (typeof WorkerGlobalScope !== "undefined" && typeof importScripts === "function") {
1745
+ return "web-worker";
1746
+ }
1747
+ return "unknown";
1748
+ }
1749
+ var PLACEHOLDER_USER_IDS = /* @__PURE__ */ new Set(["null", "undefined"]);
1750
+ function isMissingUserId(value) {
1751
+ if (value === void 0 || value === null) return true;
1752
+ if (typeof value !== "string") return false;
1753
+ const trimmed = value.trim();
1754
+ return trimmed === "" || PLACEHOLDER_USER_IDS.has(trimmed);
1755
+ }
1756
+ var MAX_DEFERRED_SENDS = 1e3;
1048
1757
  var POST_SHUTDOWN_TIMEOUT_MS = 5e3;
1049
1758
  function appGitOwnerId(prefix) {
1050
1759
  try {
@@ -1053,18 +1762,52 @@ function appGitOwnerId(prefix) {
1053
1762
  return `${prefix}:${Date.now().toString(36)}:${Math.random().toString(36).slice(2)}`;
1054
1763
  }
1055
1764
  }
1765
+ function earliestIsoTimestamp(...values) {
1766
+ let earliest;
1767
+ for (const value of values) {
1768
+ if (value === void 0) continue;
1769
+ const date = new Date(value);
1770
+ if (Number.isNaN(date.getTime())) continue;
1771
+ if (earliest === void 0 || date < earliest) earliest = date;
1772
+ }
1773
+ return earliest == null ? void 0 : earliest.toISOString();
1774
+ }
1775
+ function isPlainObject2(value) {
1776
+ if (value === null || typeof value !== "object" || Array.isArray(value)) return false;
1777
+ const proto = Object.getPrototypeOf(value);
1778
+ return proto === Object.prototype || proto === null;
1779
+ }
1780
+ var UNSAFE_MERGE_KEYS = /* @__PURE__ */ new Set(["__proto__", "constructor", "prototype"]);
1781
+ function deepMergeProperties(target, source) {
1782
+ const out = { ...target };
1783
+ for (const [key, value] of Object.entries(source)) {
1784
+ if (value === void 0 || UNSAFE_MERGE_KEYS.has(key)) continue;
1785
+ const existing = out[key];
1786
+ out[key] = isPlainObject2(existing) && isPlainObject2(value) ? deepMergeProperties(existing, value) : value;
1787
+ }
1788
+ return out;
1789
+ }
1056
1790
  function mergePatches(target, source) {
1057
- var _a, _b, _c, _d, _e, _f, _g, _h, _i;
1058
- const out = { ...target, ...source };
1791
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k;
1792
+ const defined2 = Object.fromEntries(
1793
+ Object.entries(source).filter(([, value]) => value !== void 0)
1794
+ );
1795
+ const out = { ...target, ...defined2 };
1059
1796
  if (target.properties || source.properties) {
1060
- out.properties = { ...(_a = target.properties) != null ? _a : {}, ...(_b = source.properties) != null ? _b : {} };
1797
+ out.properties = deepMergeProperties((_a = target.properties) != null ? _a : {}, (_b = source.properties) != null ? _b : {});
1798
+ }
1799
+ if (target.callerProperties || source.callerProperties) {
1800
+ out.callerProperties = deepMergeProperties(
1801
+ (_c = target.callerProperties) != null ? _c : {},
1802
+ (_d = source.callerProperties) != null ? _d : {}
1803
+ );
1061
1804
  }
1062
1805
  if (target.usage || source.usage) {
1063
- out.usage = { ...(_c = target.usage) != null ? _c : {} };
1064
- if (((_d = source.usage) == null ? void 0 : _d.promptTokens) !== void 0) {
1806
+ out.usage = { ...(_e = target.usage) != null ? _e : {} };
1807
+ if (((_f = source.usage) == null ? void 0 : _f.promptTokens) !== void 0) {
1065
1808
  out.usage.promptTokens = source.usage.promptTokens;
1066
1809
  }
1067
- if (((_e = source.usage) == null ? void 0 : _e.completionTokens) !== void 0) {
1810
+ if (((_g = source.usage) == null ? void 0 : _g.completionTokens) !== void 0) {
1068
1811
  out.usage.completionTokens = source.usage.completionTokens;
1069
1812
  }
1070
1813
  }
@@ -1072,10 +1815,10 @@ function mergePatches(target, source) {
1072
1815
  out.error = target.error;
1073
1816
  }
1074
1817
  if (target.featureFlags || source.featureFlags) {
1075
- out.featureFlags = { ...(_f = target.featureFlags) != null ? _f : {}, ...(_g = source.featureFlags) != null ? _g : {} };
1818
+ out.featureFlags = { ...(_h = target.featureFlags) != null ? _h : {}, ...(_i = source.featureFlags) != null ? _i : {} };
1076
1819
  }
1077
1820
  if (target.attachments || source.attachments) {
1078
- out.attachments = [...(_h = target.attachments) != null ? _h : [], ...(_i = source.attachments) != null ? _i : []];
1821
+ out.attachments = [...(_j = target.attachments) != null ? _j : [], ...(_k = source.attachments) != null ? _k : []];
1079
1822
  }
1080
1823
  return out;
1081
1824
  }
@@ -1100,6 +1843,15 @@ function eventTelemetryProperties(patch) {
1100
1843
  }
1101
1844
  return properties;
1102
1845
  }
1846
+ var FINISHED_EVENT_MEMORY = 1e4;
1847
+ var MANUAL_UPDATES = /* @__PURE__ */ new WeakSet();
1848
+ function markManualUpdate(patch) {
1849
+ try {
1850
+ MANUAL_UPDATES.add(patch);
1851
+ } catch (e) {
1852
+ }
1853
+ return patch;
1854
+ }
1103
1855
  var EventShipper = class {
1104
1856
  constructor(opts) {
1105
1857
  this.appGitOwner = appGitOwnerId("event");
@@ -1107,6 +1859,12 @@ var EventShipper = class {
1107
1859
  this.sticky = /* @__PURE__ */ new Map();
1108
1860
  this.timers = /* @__PURE__ */ new Map();
1109
1861
  this.inFlight = /* @__PURE__ */ new Set();
1862
+ this.background = /* @__PURE__ */ new Set();
1863
+ this.backgroundDrops = 0;
1864
+ this.cutOff = new InFlightCutOff();
1865
+ this.finishedEventIds = /* @__PURE__ */ new Set();
1866
+ this.sendChains = /* @__PURE__ */ new Map();
1867
+ this.deferredSends = /* @__PURE__ */ new Set();
1110
1868
  this.hasShutdown = false;
1111
1869
  var _a, _b, _c, _d, _e, _f, _g, _h, _i;
1112
1870
  this.appGitContext = (_a = opts.appGitContext) != null ? _a : createAppGitContext(opts.appGit);
@@ -1120,6 +1878,7 @@ var EventShipper = class {
1120
1878
  this.prefix = `[raindrop-ai/${this.sdkName}]`;
1121
1879
  this.defaultEventName = (_f = opts.defaultEventName) != null ? _f : "ai_generation";
1122
1880
  this.maxTextFieldCharsOpt = opts.maxTextFieldChars;
1881
+ this.flushTimeoutMs = resolveFlushTimeoutMs(opts.flushTimeoutMs);
1123
1882
  this.localDebuggerUrl = (_g = resolveLocalDebuggerBaseUrl(opts.localDebuggerUrl)) != null ? _g : void 0;
1124
1883
  if (this.debug && this.localDebuggerUrl) {
1125
1884
  console.log(`${this.prefix} Local debugger mirroring: ${this.localDebuggerUrl}`);
@@ -1128,15 +1887,29 @@ var EventShipper = class {
1128
1887
  debug: this.debug,
1129
1888
  prefix: this.prefix
1130
1889
  });
1131
- const isNode = typeof process !== "undefined" && typeof process.version === "string";
1890
+ this.redactPii = opts.redactPii === true;
1891
+ this.piiFields = new PiiFieldRedactor({
1892
+ startupFieldMap: opts.redactPiiFields,
1893
+ logPrefix: this.prefix
1894
+ });
1895
+ if (opts.redactPiiFields && !this.redactPii) {
1896
+ rateLimitedLog(
1897
+ `${this.prefix}.pii_fields_without_redaction`,
1898
+ () => console.warn(
1899
+ `${this.prefix} redactPiiFields is set but redactPii is off; nothing is redacted. Set redactPii: true to use it.`
1900
+ )
1901
+ );
1902
+ }
1903
+ this.wizardSession = opts.wizardSession || void 0;
1904
+ const runtime = detectRuntime();
1132
1905
  this.context = {
1133
1906
  library: {
1134
1907
  name: (_h = opts.libraryName) != null ? _h : "@raindrop-ai/core",
1135
1908
  version: (_i = opts.libraryVersion) != null ? _i : "0.0.0"
1136
1909
  },
1137
1910
  metadata: {
1138
- jsRuntime: isNode ? "node" : "web",
1139
- ...isNode ? { nodeVersion: process.version } : {}
1911
+ jsRuntime: runtime,
1912
+ ...runtime === "node" ? { nodeVersion: process.version } : {}
1140
1913
  }
1141
1914
  };
1142
1915
  }
@@ -1153,28 +1926,21 @@ var EventShipper = class {
1153
1926
  return { ...this.authHeaders(), ...projectIdHeaders(this.projectId) };
1154
1927
  }
1155
1928
  /**
1156
- * Build the retry/timeout options for one POST, honoring the shutdown
1157
- * deadline. Returns `null` when the shutdown drain window is exhausted —
1158
- * the caller must drop the payload (with a rate-limited warning) instead
1159
- * of issuing a request that could outlive process exit.
1160
- *
1161
- * Checked fresh on EVERY send, so a shutdown that begins while the flush
1162
- * path is mid-drain takes effect immediately: no further retries, and the
1163
- * per-attempt timeout is clamped to the remaining window. After
1164
- * `shutdown()` returns (deadline cleared, `hasShutdown` still set),
1165
- * sends — late callers, or flush work the deadline abandoned mid-drain —
1166
- * run as a single short attempt rather than regaining the full retry
1167
- * schedule.
1929
+ * Retry/timeout options for one POST. While `shutdown()` drains, sends retry
1930
+ * within its window (deadline = the shutdown deadline); a flush passes its
1931
+ * own deadline. `null` when the shutdown window is spent: the caller drops
1932
+ * the payload with a warning. After `shutdown()` returns, late sends get a
1933
+ * single short attempt rather than the full retry schedule.
1168
1934
  */
1169
- requestOpts() {
1935
+ requestOpts(deadlineAt) {
1170
1936
  if (this.shutdownDeadlineAt !== void 0) {
1171
- const remainingMs = this.shutdownDeadlineAt - Date.now();
1172
- if (remainingMs <= 0) return null;
1937
+ if (this.shutdownDeadlineAt - Date.now() <= 0) return null;
1173
1938
  return {
1174
- maxAttempts: 1,
1939
+ maxAttempts: 3,
1175
1940
  debug: this.debug,
1176
1941
  sdkName: this.sdkName,
1177
- timeoutMs: Math.min(DEFAULT_REQUEST_TIMEOUT_MS, remainingMs)
1942
+ deadlineAt: Math.min(this.shutdownDeadlineAt, deadlineAt != null ? deadlineAt : Infinity),
1943
+ cutOff: this.cutOff.signal
1178
1944
  };
1179
1945
  }
1180
1946
  if (this.hasShutdown) {
@@ -1182,15 +1948,94 @@ var EventShipper = class {
1182
1948
  maxAttempts: 1,
1183
1949
  debug: this.debug,
1184
1950
  sdkName: this.sdkName,
1185
- timeoutMs: POST_SHUTDOWN_TIMEOUT_MS
1951
+ timeoutMs: POST_SHUTDOWN_TIMEOUT_MS,
1952
+ cutOff: this.cutOff.signal
1186
1953
  };
1187
1954
  }
1188
- return { maxAttempts: 3, debug: this.debug, sdkName: this.sdkName };
1955
+ return {
1956
+ maxAttempts: 3,
1957
+ debug: this.debug,
1958
+ sdkName: this.sdkName,
1959
+ deadlineAt,
1960
+ cutOff: this.cutOff.signal
1961
+ };
1962
+ }
1963
+ shuttingDown() {
1964
+ return this.shutdownDeadlineAt !== void 0 || this.hasShutdown;
1189
1965
  }
1190
1966
  async patch(eventId, patch) {
1191
- var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k;
1192
- if (!this.enabled) return;
1193
- if (!eventId || !eventId.trim()) return;
1967
+ await this.patchWithFate(eventId, patch);
1968
+ }
1969
+ /**
1970
+ * Queue an update without waiting for any network send: for integrations,
1971
+ * whose captured call must return as soon as the update is queued (rule C9,
1972
+ * hot path). A finishing update starts its send in the background; `flush()`
1973
+ * and `shutdown()` wait for it within their deadline and count it in their
1974
+ * result. Never throws or rejects.
1975
+ */
1976
+ enqueue(eventId, patch) {
1977
+ let op;
1978
+ try {
1979
+ op = this.patchWithFate(eventId, patch).catch((err) => {
1980
+ this.warnBackgroundFailure(eventId, err);
1981
+ return "dropped";
1982
+ }).then((fate) => {
1983
+ if (fate === "dropped") this.backgroundDrops += 1;
1984
+ return fate;
1985
+ });
1986
+ } catch (err) {
1987
+ this.warnBackgroundFailure(eventId, err);
1988
+ return;
1989
+ }
1990
+ this.background.add(op);
1991
+ void op.finally(() => this.background.delete(op));
1992
+ }
1993
+ /** `finish()` without waiting for the send; see `enqueue`. */
1994
+ enqueueFinish(eventId, patch = {}) {
1995
+ this.enqueue(eventId, { ...patch, isPending: false });
1996
+ }
1997
+ /**
1998
+ * Caller properties win over everything the integration derived (W6). A
1999
+ * clash is intended by the caller, so it is only logged in debug mode.
2000
+ */
2001
+ withCallerProperties(eventId, derived, caller) {
2002
+ if (!caller || Object.keys(caller).length === 0) return derived;
2003
+ if (this.debug) {
2004
+ try {
2005
+ const keys = overriddenKeys(derived, caller);
2006
+ if (keys.length > 0) {
2007
+ console.log(
2008
+ `${this.prefix} event ${eventId}: caller properties override the integration's value for ${keys.map((k) => `"${k}"`).join(", ")}`
2009
+ );
2010
+ }
2011
+ } catch (e) {
2012
+ }
2013
+ }
2014
+ return deepMergeProperties(derived, caller);
2015
+ }
2016
+ warnBackgroundFailure(eventId, err) {
2017
+ rateLimitedLog(
2018
+ `${this.prefix}.background_update`,
2019
+ () => console.warn(`${this.prefix} update for event ${eventId} failed:`, err)
2020
+ );
2021
+ }
2022
+ /**
2023
+ * Id of the last event an integration finished in this process (manual
2024
+ * events API finishes excluded): what integrations expose as `lastEventId`
2025
+ * (wrapper profile W10).
2026
+ */
2027
+ get lastFinishedEventId() {
2028
+ return this.lastFinished;
2029
+ }
2030
+ async patchWithFate(eventId, patch) {
2031
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m;
2032
+ const manual = MANUAL_UPDATES.has(patch);
2033
+ if (!this.enabled) return void 0;
2034
+ if (!eventId || !eventId.trim()) return void 0;
2035
+ if (patch.userId !== void 0 && isMissingUserId(patch.userId)) {
2036
+ const { userId: _placeholder, ...rest } = patch;
2037
+ patch = rest;
2038
+ }
1194
2039
  const capturedSticky = this.sticky.get(eventId);
1195
2040
  const eventAlreadyKnown = capturedSticky !== void 0 || this.buffers.has(eventId);
1196
2041
  const destination = currentReplayTraceDestination();
@@ -1198,7 +2043,7 @@ var EventShipper = class {
1198
2043
  if ((capturedSticky == null ? void 0 : capturedSticky.replayOnly) === true || !eventAlreadyKnown && cloudReplay) {
1199
2044
  if (patch.isPending === false) this.sticky.delete(eventId);
1200
2045
  else this.sticky.set(eventId, { ...capturedSticky, replayOnly: true });
1201
- return;
2046
+ return void 0;
1202
2047
  }
1203
2048
  const maxChars = resolveMaxTextFieldChars(this.maxTextFieldCharsOpt);
1204
2049
  if (typeof patch.input === "string" && patch.input.length > maxChars) {
@@ -1221,11 +2066,12 @@ var EventShipper = class {
1221
2066
  }
1222
2067
  const sticky = (_c = this.sticky.get(eventId)) != null ? _c : {};
1223
2068
  const existing = (_d = this.buffers.get(eventId)) != null ? _d : {};
2069
+ const explicitProperties = patch.callerProperties !== void 0 ? { ...patch.properties, ...patch.callerProperties } : patch.properties;
1224
2070
  patch = {
1225
2071
  ...patch,
1226
2072
  properties: withAppGitProperties(
1227
2073
  patch.properties,
1228
- this.appGitContext.forOperation(eventId, patch.properties, this.appGitOwner)
2074
+ this.appGitContext.forOperation(eventId, explicitProperties, this.appGitOwner)
1229
2075
  )
1230
2076
  };
1231
2077
  const merged = mergePatches(existing, patch);
@@ -1235,47 +2081,165 @@ var EventShipper = class {
1235
2081
  userId: (_h = merged.userId) != null ? _h : sticky.userId,
1236
2082
  convoId: (_i = merged.convoId) != null ? _i : sticky.convoId,
1237
2083
  eventName: (_j = merged.eventName) != null ? _j : sticky.eventName,
1238
- isPending: (_k = merged.isPending) != null ? _k : sticky.isPending
2084
+ isPending: (_k = merged.isPending) != null ? _k : sticky.isPending,
2085
+ startedAt: (_l = earliestIsoTimestamp(sticky.startedAt, patch.timestamp)) != null ? _l : (/* @__PURE__ */ new Date()).toISOString(),
2086
+ resumed: sticky.resumed,
2087
+ hasContent: sticky.hasContent === true || patchHasContent(patch),
2088
+ callerProperties: patch.callerProperties !== void 0 ? deepMergeProperties((_m = sticky.callerProperties) != null ? _m : {}, patch.callerProperties) : sticky.callerProperties
1239
2089
  });
1240
2090
  const t = this.timers.get(eventId);
1241
2091
  if (t) clearTimeout(t);
1242
2092
  if (merged.isPending === false) {
1243
- await this.flushOne(eventId);
2093
+ if (!manual) this.lastFinished = eventId;
2094
+ this.noteFinish(eventId);
2095
+ this.noteEmptyFinish(eventId);
2096
+ const fate = await this.flushOne(eventId);
1244
2097
  if (this.appGitContext.releaseOperation) this.appGitContext.releaseOperation(eventId, this.appGitOwner);
1245
2098
  else this.appGitContext.finishOperation(eventId);
1246
- return;
2099
+ return fate;
1247
2100
  }
1248
2101
  const timeout = setTimeout(() => {
1249
2102
  void this.flushOne(eventId).catch(() => {
1250
2103
  });
1251
2104
  }, this.partialFlushMs);
1252
2105
  this.timers.set(eventId, timeout);
2106
+ return void 0;
2107
+ }
2108
+ /**
2109
+ * A second finish for an event is still sent (ingest finalizes once and
2110
+ * treats it as an update, CONTRACT §10.2), but it usually means several
2111
+ * calls share one event id by mistake, so it is warned about.
2112
+ */
2113
+ noteFinish(eventId) {
2114
+ if (this.finishedEventIds.has(eventId)) {
2115
+ rateLimitedLog(
2116
+ `${this.prefix}.repeated_finish`,
2117
+ () => console.warn(
2118
+ `${this.prefix} event ${eventId} was already finished in this process; sending the update anyway (ingest finalizes an event once). Use a separate event id per interaction, or keep the event pending until its last call.`
2119
+ )
2120
+ );
2121
+ return;
2122
+ }
2123
+ this.finishedEventIds.add(eventId);
2124
+ if (this.finishedEventIds.size > FINISHED_EVENT_MEMORY) {
2125
+ const oldest = this.finishedEventIds.values().next().value;
2126
+ if (oldest !== void 0) this.finishedEventIds.delete(oldest);
2127
+ }
2128
+ }
2129
+ /**
2130
+ * An event finished with no input, output or attachments is almost always a
2131
+ * caller bug (CONTRACT §14.2, rule C5); it is sent anyway and warned about.
2132
+ * Not for a resumed event: its content may have been sent by another process.
2133
+ */
2134
+ noteEmptyFinish(eventId) {
2135
+ const sticky = this.sticky.get(eventId);
2136
+ if ((sticky == null ? void 0 : sticky.resumed) === true || (sticky == null ? void 0 : sticky.hasContent) === true) return;
2137
+ rateLimitedLog(
2138
+ `${this.prefix}.empty_finish`,
2139
+ () => console.warn(
2140
+ `${this.prefix} event ${eventId} finished with no input, output or attachments; sending it anyway. Pass the interaction's input at begin or its output at finish.`
2141
+ )
2142
+ );
2143
+ }
2144
+ /**
2145
+ * Continue an event that was begun elsewhere (another process, a queue
2146
+ * worker, a later request) using only its id. No request is sent; later
2147
+ * `patch` / `finish` calls for this id are sent even without a `userId`,
2148
+ * and ingest keeps the user, conversation and start time it already has.
2149
+ * Pass `userId` / `convoId` / `eventName` if you have them.
2150
+ */
2151
+ resume(eventId, options = {}) {
2152
+ var _a, _b, _c;
2153
+ if (!this.enabled) return;
2154
+ if (!eventId || !eventId.trim()) return;
2155
+ const sticky = (_a = this.sticky.get(eventId)) != null ? _a : {};
2156
+ this.sticky.set(eventId, {
2157
+ ...sticky,
2158
+ userId: isMissingUserId(options.userId) ? sticky.userId : options.userId,
2159
+ convoId: (_b = options.convoId) != null ? _b : sticky.convoId,
2160
+ eventName: (_c = options.eventName) != null ? _c : sticky.eventName,
2161
+ resumed: true
2162
+ });
1253
2163
  }
1254
2164
  async finish(eventId, patch) {
1255
- await this.patch(eventId, { ...patch, isPending: false });
2165
+ const update = { ...patch, isPending: false };
2166
+ if (MANUAL_UPDATES.has(patch)) MANUAL_UPDATES.add(update);
2167
+ await this.patch(eventId, update);
1256
2168
  }
2169
+ /**
2170
+ * Send everything buffered, open (pending) events included, within
2171
+ * `flushTimeoutMs` (CONTRACT 1.2.1). Resolves, never rejects, to whether it
2172
+ * all reached ingest; when it didn't, it also warns. At the deadline,
2173
+ * payloads not yet sent stay queued for a later send and a request cut off
2174
+ * mid-flight is dropped. Sends already running when it was called are
2175
+ * waited for up to the deadline and then cut off.
2176
+ */
1257
2177
  async flush() {
1258
- if (!this.enabled) return;
2178
+ const result = await this.drainAll(Date.now() + this.flushTimeoutMs);
2179
+ if (!result.delivered && this.shutdownDeadlineAt === void 0) {
2180
+ rateLimitedLog(
2181
+ `${this.prefix}.flush_undelivered`,
2182
+ () => console.warn(`${this.prefix} ${describeUndelivered("flush()", result)}`)
2183
+ );
2184
+ }
2185
+ return asCompat(result);
2186
+ }
2187
+ async drainAll(deadlineAt) {
2188
+ if (!this.enabled) return { ...DELIVERED };
2189
+ const deferred = [...this.deferredSends];
2190
+ this.deferredSends.clear();
2191
+ for (const entry of deferred) if (entry.timer) clearTimeout(entry.timer);
1259
2192
  const ids = [...this.buffers.keys()];
1260
- await Promise.all(ids.map((id) => this.flushOne(id)));
1261
- await Promise.all([...this.inFlight].map((p) => p.catch(() => {
1262
- })));
2193
+ const fates = await Promise.all([
2194
+ ...ids.map((id) => this.flushOne(id, deadlineAt)),
2195
+ ...deferred.map((entry) => entry.send(deadlineAt))
2196
+ ]);
2197
+ const background = [...this.background];
2198
+ if (background.length > 0) {
2199
+ await raceWithTimeout(Promise.all(background), deadlineAt - Date.now());
2200
+ }
2201
+ for (; this.backgroundDrops > 0; this.backgroundDrops -= 1) fates.push("dropped");
2202
+ const running = [...this.inFlight];
2203
+ const { settled, stillRunning } = await awaitEarlierSends(
2204
+ running,
2205
+ deadlineAt,
2206
+ this.cutOff,
2207
+ (p) => this.inFlight.has(p)
2208
+ );
2209
+ const unresolved = background.filter((op) => this.background.has(op)).length;
2210
+ return summarize2(
2211
+ fates.filter((fate) => fate !== void 0),
2212
+ Math.max(stillRunning, unresolved),
2213
+ !settled || unresolved > 0
2214
+ );
1263
2215
  }
2216
+ /**
2217
+ * Flush within `flushTimeoutMs` and stop. Sends retry while the window
2218
+ * allows; whatever is left at the deadline is dropped with a warning, so a
2219
+ * dead network can never wedge process exit. Resolves, never rejects.
2220
+ */
1264
2221
  async shutdown() {
1265
2222
  this.hasShutdown = true;
1266
- this.shutdownDeadlineAt = Date.now() + SHUTDOWN_DEADLINE_MS;
2223
+ const deadlineAt = Date.now() + this.flushTimeoutMs;
2224
+ this.shutdownDeadlineAt = deadlineAt;
2225
+ let result = { ...DELIVERED };
1267
2226
  try {
1268
2227
  for (const t of this.timers.values()) clearTimeout(t);
1269
2228
  this.timers.clear();
1270
2229
  for (const [eventId, sticky] of this.sticky) {
1271
2230
  if (sticky.replayOnly) this.sticky.delete(eventId);
1272
2231
  }
1273
- const settled = await raceWithTimeout(this.flush(), SHUTDOWN_DEADLINE_MS);
1274
- if (!settled) this.warnShutdownDrop("in-flight request(s) at shutdown");
2232
+ result = await this.drainAll(deadlineAt);
2233
+ if (!result.delivered) {
2234
+ this.warnShutdownDrop(
2235
+ `${result.dropped + result.queued + result.inFlight} payload(s) not delivered`
2236
+ );
2237
+ }
1275
2238
  } finally {
1276
2239
  this.shutdownDeadlineAt = void 0;
1277
2240
  if (this.ownsAppGitContext) this.appGitContext.dispose();
1278
2241
  }
2242
+ return asCompat(result);
1279
2243
  }
1280
2244
  async trackSignal(signal) {
1281
2245
  var _a, _b;
@@ -1296,23 +2260,9 @@ var EventShipper = class {
1296
2260
  ...signal.after ? { after: signal.after } : {}
1297
2261
  }
1298
2262
  }
1299
- ];
2263
+ ].map((item) => this.redactFields("signal", item));
1300
2264
  if (!this.writeKey) return;
1301
- const url = `${this.baseUrl}signals/track`;
1302
- const opts = this.requestOpts();
1303
- if (!opts) {
1304
- this.warnShutdownDrop("signal");
1305
- return;
1306
- }
1307
- try {
1308
- await postJson(url, body, this.requestHeaders(), opts);
1309
- } catch (err) {
1310
- const msg = err instanceof Error ? err.message : String(err);
1311
- rateLimitedLog(
1312
- `${this.prefix}.send_signal_failed`,
1313
- () => console.warn(`${this.prefix} failed to send signal (dropping): ${msg}`)
1314
- );
1315
- }
2265
+ await this.sendDirect("signal", `${this.baseUrl}signals/track`, body);
1316
2266
  }
1317
2267
  async identify(users) {
1318
2268
  if (!this.enabled) return;
@@ -1327,30 +2277,80 @@ var EventShipper = class {
1327
2277
  return false;
1328
2278
  }
1329
2279
  return true;
1330
- }).map((user) => {
1331
- var _a;
1332
- return {
1333
- user_id: user.userId,
1334
- traits: (_a = user.traits) != null ? _a : {}
1335
- };
1336
- });
2280
+ }).map(
2281
+ (user) => {
2282
+ var _a;
2283
+ return this.redactFields("identify", {
2284
+ user_id: user.userId,
2285
+ traits: (_a = user.traits) != null ? _a : {}
2286
+ });
2287
+ }
2288
+ );
1337
2289
  if (!this.writeKey) return;
1338
2290
  if (body.length === 0) return;
1339
- const url = `${this.baseUrl}users/identify`;
1340
- const opts = this.requestOpts();
2291
+ await this.sendDirect("identify", `${this.baseUrl}users/identify`, body);
2292
+ }
2293
+ /**
2294
+ * Send a signal or identify body. Tracked so `flush()` waits for it. A send
2295
+ * a flush deadline deferred is kept and retried later (outside shutdown).
2296
+ */
2297
+ async sendDirect(kind, url, body, deadlineAt) {
2298
+ const opts = this.requestOpts(deadlineAt);
1341
2299
  if (!opts) {
1342
- this.warnShutdownDrop("identify");
1343
- return;
2300
+ this.warnShutdownDrop(kind);
2301
+ return "dropped";
1344
2302
  }
2303
+ const sending = sendJson(url, body, this.requestHeaders(), opts);
2304
+ const tracked = sending.then(() => void 0);
2305
+ this.inFlight.add(tracked);
2306
+ let outcome;
1345
2307
  try {
1346
- await postJson(url, body, this.requestHeaders(), opts);
1347
- } catch (err) {
1348
- const msg = err instanceof Error ? err.message : String(err);
2308
+ outcome = await sending;
2309
+ } finally {
2310
+ this.inFlight.delete(tracked);
2311
+ }
2312
+ const fate = fateOf(outcome, this.shuttingDown());
2313
+ if (fate === "queued") {
2314
+ this.deferSend(
2315
+ (later) => this.sendDirect(kind, url, body, later),
2316
+ outcome.status === "deferred" ? outcome.retryAtMs : void 0
2317
+ );
2318
+ } else if (fate === "dropped") {
2319
+ this.warnSendDropped(kind, outcome);
2320
+ }
2321
+ return fate;
2322
+ }
2323
+ deferSend(send, retryAtMs) {
2324
+ var _a, _b;
2325
+ if (this.deferredSends.size >= MAX_DEFERRED_SENDS) {
1349
2326
  rateLimitedLog(
1350
- `${this.prefix}.send_identify_failed`,
1351
- () => console.warn(`${this.prefix} failed to send identify (dropping): ${msg}`)
2327
+ `${this.prefix}.deferred_full`,
2328
+ () => console.warn(`${this.prefix} too many sends waiting for a retry; dropping one`)
1352
2329
  );
2330
+ return;
1353
2331
  }
2332
+ const entry = { send };
2333
+ const delay = Math.max(this.partialFlushMs, (retryAtMs != null ? retryAtMs : 0) - Date.now());
2334
+ entry.timer = setTimeout(() => {
2335
+ if (!this.deferredSends.delete(entry)) return;
2336
+ void send().catch(() => {
2337
+ });
2338
+ }, delay);
2339
+ (_b = (_a = entry.timer).unref) == null ? void 0 : _b.call(_a);
2340
+ this.deferredSends.add(entry);
2341
+ }
2342
+ warnSendDropped(what, outcome) {
2343
+ var _a, _b;
2344
+ if (outcome.status === "delivered" || outcome.status === "rejected") return;
2345
+ const reason = outcome.status === "cut_off" ? "cut off at the flush deadline (not resent: it may have reached ingest)" : outcome.status === "deferred" ? "not sent before the shutdown deadline" : (_b = (_a = outcome.error) == null ? void 0 : _a.message) != null ? _b : outcome.status;
2346
+ rateLimitedLog(
2347
+ `${this.prefix}.send_${what}_dropped`,
2348
+ () => console.warn(`${this.prefix} failed to send ${what} (dropping): ${reason}`)
2349
+ );
2350
+ }
2351
+ /** The payload with its PII fields redacted when `redactPii` is on (CONTRACT 11.2). */
2352
+ redactFields(kind, payload) {
2353
+ return this.redactPii ? this.piiFields.redact(kind, payload) : payload;
1354
2354
  }
1355
2355
  warnShutdownDrop(what) {
1356
2356
  rateLimitedLog(
@@ -1358,9 +2358,8 @@ var EventShipper = class {
1358
2358
  () => console.warn(`${this.prefix} shutdown flush deadline exceeded; dropping ${what}`)
1359
2359
  );
1360
2360
  }
1361
- async flushOne(eventId) {
1362
- var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m;
1363
- if (!this.enabled) return;
2361
+ async flushOne(eventId, deadlineAt) {
2362
+ if (!this.enabled) return void 0;
1364
2363
  const timer = this.timers.get(eventId);
1365
2364
  if (timer) {
1366
2365
  clearTimeout(timer);
@@ -1368,30 +2367,91 @@ var EventShipper = class {
1368
2367
  }
1369
2368
  const accumulated = this.buffers.get(eventId);
1370
2369
  this.buffers.delete(eventId);
1371
- if (!accumulated) return;
2370
+ if (!accumulated) return void 0;
2371
+ const previous = this.sendChains.get(eventId);
2372
+ let release = () => {
2373
+ };
2374
+ const current = new Promise((resolve2) => {
2375
+ release = resolve2;
2376
+ });
2377
+ this.sendChains.set(eventId, current);
2378
+ try {
2379
+ if (previous) {
2380
+ const ready = deadlineAt === void 0 ? (await previous, true) : await raceWithTimeout(previous, deadlineAt - Date.now());
2381
+ if (!ready) {
2382
+ return this.requeue(eventId, accumulated, void 0);
2383
+ }
2384
+ }
2385
+ return await this.sendAccumulated(eventId, accumulated, deadlineAt);
2386
+ } finally {
2387
+ release();
2388
+ if (this.sendChains.get(eventId) === current) this.sendChains.delete(eventId);
2389
+ }
2390
+ }
2391
+ /**
2392
+ * Put an unsent update back under any newer one and send it later
2393
+ * (CONTRACT 1.2.1). During shutdown it is dropped instead.
2394
+ */
2395
+ requeue(eventId, accumulated, retryAtMs) {
2396
+ var _a, _b;
2397
+ if (this.shuttingDown()) {
2398
+ this.warnShutdownDrop(`track_partial ${eventId}`);
2399
+ if (accumulated.isPending === false) this.sticky.delete(eventId);
2400
+ return "dropped";
2401
+ }
2402
+ const newer = this.buffers.get(eventId);
2403
+ const merged = newer ? mergePatches(accumulated, newer) : accumulated;
2404
+ merged.isPending = (_a = newer == null ? void 0 : newer.isPending) != null ? _a : accumulated.isPending;
2405
+ this.buffers.set(eventId, merged);
2406
+ const existing = this.timers.get(eventId);
2407
+ if (existing) clearTimeout(existing);
2408
+ const delay = Math.max(this.partialFlushMs, (retryAtMs != null ? retryAtMs : 0) - Date.now());
2409
+ const timeout = setTimeout(() => {
2410
+ void this.flushOne(eventId).catch(() => {
2411
+ });
2412
+ }, delay);
2413
+ (_b = timeout.unref) == null ? void 0 : _b.call(timeout);
2414
+ this.timers.set(eventId, timeout);
2415
+ return "queued";
2416
+ }
2417
+ async sendAccumulated(eventId, accumulated, deadlineAt) {
2418
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p;
1372
2419
  const sticky = (_a = this.sticky.get(eventId)) != null ? _a : {};
1373
2420
  const isPending = (_c = (_b = accumulated.isPending) != null ? _b : sticky.isPending) != null ? _c : true;
1374
- const eventName = (_e = (_d = accumulated.eventName) != null ? _d : sticky.eventName) != null ? _e : this.defaultEventName;
1375
- const userId = (_f = accumulated.userId) != null ? _f : sticky.userId;
1376
- if (!userId) {
1377
- if (this.debug) {
1378
- console.warn(`${this.prefix} skipping track_partial for ${eventId}: missing userId`);
1379
- }
2421
+ const knownEventName = (_d = accumulated.eventName) != null ? _d : sticky.eventName;
2422
+ const eventName = knownEventName != null ? knownEventName : sticky.resumed ? void 0 : this.defaultEventName;
2423
+ const userId = (_e = accumulated.userId) != null ? _e : sticky.userId;
2424
+ if (!userId && !sticky.resumed) {
2425
+ rateLimitedLog(
2426
+ `${this.prefix}.track_partial_missing_user`,
2427
+ () => console.warn(
2428
+ `${this.prefix} skipping track_partial for ${eventId}: missing userId (pass userId, or call resume(eventId) to continue an event begun elsewhere)`
2429
+ )
2430
+ );
1380
2431
  this.sticky.delete(eventId);
1381
- return;
2432
+ return void 0;
1382
2433
  }
1383
- const { wizardSession, ...restProperties } = (_g = accumulated.properties) != null ? _g : {};
2434
+ if (!userId) {
2435
+ rateLimitedLog(
2436
+ `${this.prefix}.track_partial_resumed_without_user`,
2437
+ () => console.warn(
2438
+ `${this.prefix} sending track_partial for ${eventId} without a userId; ingest will use the user stored for this event (pass userId to resume() to set it explicitly)`
2439
+ )
2440
+ );
2441
+ }
2442
+ const { wizardSession, ...restProperties } = (_f = accumulated.properties) != null ? _f : {};
1384
2443
  let telemetryProperties = {};
1385
2444
  try {
1386
2445
  telemetryProperties = eventTelemetryProperties(accumulated);
1387
2446
  } catch (e) {
1388
2447
  }
1389
- const convoId = (_h = accumulated.convoId) != null ? _h : sticky.convoId;
1390
- const payload = {
2448
+ const convoId = (_g = accumulated.convoId) != null ? _g : sticky.convoId;
2449
+ const sessionTag = wizardSession != null ? wizardSession : this.wizardSession;
2450
+ const unredacted = {
1391
2451
  event_id: eventId,
1392
- user_id: userId,
1393
- event: eventName,
1394
- timestamp: (_i = accumulated.timestamp) != null ? _i : (/* @__PURE__ */ new Date()).toISOString(),
2452
+ ...userId ? { user_id: userId } : {},
2453
+ ...eventName ? { event: eventName } : {},
2454
+ timestamp: (_i = (_h = sticky.startedAt) != null ? _h : accumulated.timestamp) != null ? _i : (/* @__PURE__ */ new Date()).toISOString(),
1395
2455
  ai_data: {
1396
2456
  input: accumulated.input,
1397
2457
  output: accumulated.error !== void 0 && accumulated.error !== null ? void 0 : accumulated.output,
@@ -1399,32 +2459,38 @@ var EventShipper = class {
1399
2459
  convo_id: convoId
1400
2460
  },
1401
2461
  properties: {
1402
- ...restProperties,
1403
- ...telemetryProperties,
1404
- ...wizardSession ? { "raindrop.wizardSession": wizardSession } : {},
2462
+ ...this.withCallerProperties(
2463
+ eventId,
2464
+ { ...restProperties, ...telemetryProperties },
2465
+ (_j = sticky.callerProperties) != null ? _j : accumulated.callerProperties
2466
+ ),
2467
+ ...sessionTag ? { "raindrop.wizardSession": sessionTag } : {},
1405
2468
  $context: this.context
1406
2469
  },
1407
2470
  ...accumulated.featureFlags && Object.keys(accumulated.featureFlags).length > 0 ? { feature_flags: accumulated.featureFlags } : {},
1408
2471
  attachments: accumulated.attachments,
1409
2472
  is_pending: isPending
1410
2473
  };
2474
+ const payload = this.redactFields("event", unredacted);
1411
2475
  const url = `${this.baseUrl}events/track_partial`;
1412
2476
  if (this.debug) {
2477
+ const sent = payload;
2478
+ const preview = (v, n) => typeof v === "string" ? v.slice(0, n) : void 0;
1413
2479
  console.log(`${this.prefix} sending track_partial`, {
1414
2480
  eventId,
1415
2481
  eventName,
1416
2482
  userId,
1417
2483
  convoId,
1418
2484
  isPending,
1419
- inputPreview: typeof accumulated.input === "string" ? accumulated.input.slice(0, 120) : void 0,
1420
- outputPreview: typeof accumulated.output === "string" ? accumulated.output.slice(0, 120) : void 0,
1421
- attachments: (_k = (_j = accumulated.attachments) == null ? void 0 : _j.length) != null ? _k : 0,
1422
- attachmentKinds: (_m = (_l = accumulated.attachments) == null ? void 0 : _l.map((a) => ({
2485
+ inputPreview: preview((_k = sent.ai_data) == null ? void 0 : _k.input, 120),
2486
+ outputPreview: preview((_l = sent.ai_data) == null ? void 0 : _l.output, 120),
2487
+ attachments: (_n = (_m = sent.attachments) == null ? void 0 : _m.length) != null ? _n : 0,
2488
+ attachmentKinds: (_p = (_o = sent.attachments) == null ? void 0 : _o.map((a) => ({
1423
2489
  type: a.type,
1424
2490
  role: a.role,
1425
2491
  name: a.name,
1426
- valuePreview: a.value.slice(0, 60)
1427
- }))) != null ? _m : [],
2492
+ valuePreview: preview(a.value, 60)
2493
+ }))) != null ? _p : [],
1428
2494
  endpoint: url
1429
2495
  });
1430
2496
  }
@@ -1437,39 +2503,94 @@ var EventShipper = class {
1437
2503
  if (!this.writeKey) {
1438
2504
  if (!isPending) this.sticky.delete(eventId);
1439
2505
  if (mirror) await mirror;
1440
- return;
2506
+ return void 0;
1441
2507
  }
1442
- const opts = this.requestOpts();
2508
+ const opts = this.requestOpts(deadlineAt);
1443
2509
  if (!opts) {
1444
2510
  this.warnShutdownDrop(`track_partial ${eventId}`);
1445
2511
  if (!isPending) this.sticky.delete(eventId);
1446
2512
  if (mirror) await mirror;
1447
- return;
2513
+ return "dropped";
1448
2514
  }
1449
- const p = postJson(url, payload, this.requestHeaders(), opts);
1450
- const combined = mirror ? Promise.all([mirror, p]).then(() => void 0) : p;
2515
+ const sending = sendJson(url, payload, this.requestHeaders(), opts);
2516
+ const combined = Promise.all([mirror, sending.then(() => void 0)]).then(() => void 0);
1451
2517
  this.inFlight.add(combined);
2518
+ let outcome;
1452
2519
  try {
1453
- try {
1454
- await combined;
1455
- if (this.debug) {
1456
- console.log(`${this.prefix} sent track_partial ${eventId} (${eventName})`);
1457
- }
1458
- } catch (err) {
1459
- const msg = err instanceof Error ? err.message : String(err);
1460
- rateLimitedLog(
1461
- `${this.prefix}.send_track_partial_failed`,
1462
- () => console.warn(`${this.prefix} failed to send track_partial (dropping): ${msg}`)
1463
- );
1464
- }
2520
+ outcome = await sending;
2521
+ if (mirror) await mirror.catch(() => {
2522
+ });
1465
2523
  } finally {
1466
2524
  this.inFlight.delete(combined);
1467
2525
  }
2526
+ const fate = fateOf(outcome, this.shuttingDown());
2527
+ if (fate === "queued") {
2528
+ return this.requeue(
2529
+ eventId,
2530
+ accumulated,
2531
+ outcome.status === "deferred" ? outcome.retryAtMs : void 0
2532
+ );
2533
+ }
2534
+ if (fate === "dropped") this.warnSendDropped("track_partial", outcome);
2535
+ else if (this.debug && outcome.status === "delivered") {
2536
+ console.log(`${this.prefix} sent track_partial ${eventId} (${eventName})`);
2537
+ }
1468
2538
  if (!isPending) {
1469
2539
  this.sticky.delete(eventId);
1470
2540
  }
2541
+ return fate;
1471
2542
  }
1472
2543
  };
2544
+ function overriddenKeys(derived, caller) {
2545
+ const keys = [];
2546
+ for (const [key, value] of Object.entries(caller)) {
2547
+ if (value === void 0 || !(key in derived)) continue;
2548
+ const existing = derived[key];
2549
+ if (isPlainObject2(existing) && isPlainObject2(value)) {
2550
+ if (overriddenKeys(existing, value).length > 0) keys.push(key);
2551
+ } else if (JSON.stringify(existing) !== JSON.stringify(value)) {
2552
+ keys.push(key);
2553
+ }
2554
+ }
2555
+ return keys;
2556
+ }
2557
+ function patchHasContent(patch) {
2558
+ var _a, _b;
2559
+ return typeof patch.input === "string" && patch.input.length > 0 || typeof patch.output === "string" && patch.output.length > 0 || ((_b = (_a = patch.attachments) == null ? void 0 : _a.length) != null ? _b : 0) > 0;
2560
+ }
2561
+ function resolveFlushTimeoutMs(value) {
2562
+ return typeof value === "number" && Number.isFinite(value) && value > 0 ? value : DEFAULT_FLUSH_TIMEOUT_MS;
2563
+ }
2564
+ function createEventsApi(shipper) {
2565
+ return {
2566
+ async patch(eventId, patch) {
2567
+ await (shipper == null ? void 0 : shipper.patch(eventId, markManualUpdate(asCallerPatch(patch))));
2568
+ },
2569
+ async finish(eventId, patch = {}) {
2570
+ await (shipper == null ? void 0 : shipper.finish(eventId, markManualUpdate(asCallerPatch(patch))));
2571
+ },
2572
+ async addAttachments(eventId, attachments) {
2573
+ await (shipper == null ? void 0 : shipper.patch(eventId, markManualUpdate({ attachments })));
2574
+ },
2575
+ async setProperties(eventId, properties) {
2576
+ await (shipper == null ? void 0 : shipper.patch(eventId, markManualUpdate({ properties, callerProperties: properties })));
2577
+ },
2578
+ async setFeatureFlags(eventId, featureFlags) {
2579
+ await (shipper == null ? void 0 : shipper.patch(eventId, markManualUpdate({ featureFlags: normalizeFeatureFlags(featureFlags) })));
2580
+ },
2581
+ resume(eventId, options) {
2582
+ shipper == null ? void 0 : shipper.resume(eventId, options);
2583
+ }
2584
+ };
2585
+ }
2586
+ function asCallerPatch(patch) {
2587
+ var _a;
2588
+ if (!patch.properties) return patch;
2589
+ return {
2590
+ ...patch,
2591
+ callerProperties: { ...patch.properties, ...(_a = patch.callerProperties) != null ? _a : {} }
2592
+ };
2593
+ }
1473
2594
  var MODEL_USAGE_ATTRIBUTES = {
1474
2595
  usageSource: "raindrop.usage.source",
1475
2596
  providerName: "gen_ai.provider.name",
@@ -1529,6 +2650,21 @@ function buildRawModelUsageAttributes(usage) {
1529
2650
  attrDouble(MODEL_USAGE_ATTRIBUTES.providerReportedCost, providerReportedCost)
1530
2651
  ].filter((attribute) => attribute !== void 0);
1531
2652
  }
2653
+ var KNOWN_MODEL_PROVIDERS = /* @__PURE__ */ new Set([
2654
+ ...MODEL_PROVIDER_NAMES.map(([, family]) => family),
2655
+ "vercel",
2656
+ // Further vendors Dawn's pricing resolves (apps/dawn/lib/spend).
2657
+ "deepseek",
2658
+ "fireworks-ai",
2659
+ "groq",
2660
+ "inception",
2661
+ "mistral",
2662
+ "moonshotai",
2663
+ "perplexity",
2664
+ "togetherai",
2665
+ "xai",
2666
+ "zai"
2667
+ ]);
1532
2668
  var STRING_ALIASES = {
1533
2669
  [MODEL_USAGE_ATTRIBUTES.requestModel]: ["ai.model.id", "ai.model"]
1534
2670
  };
@@ -1648,12 +2784,41 @@ var REDACTED_PLACEHOLDER = "[REDACTED]";
1648
2784
  function normalizeKeyName(name) {
1649
2785
  return name.toLowerCase().replace(/[-_.]/g, "");
1650
2786
  }
2787
+ var URL_USERINFO = new RegExp(
2788
+ String.raw`\b([a-z][a-z0-9+.-]{0,${DEFAULT_MAX_TEXT_FIELD_CHARS - 1}})(?::\/\/([^\s/?#]{1,${DEFAULT_MAX_TEXT_FIELD_CHARS}})@)?`,
2789
+ "gi"
2790
+ );
2791
+ var URL_QUERY_PARAM = /([?&#]([A-Za-z0-9_.\-]+)=)[^&#?\s"']*/g;
2792
+ var URL_SECRET_QUERY_KEYS = /* @__PURE__ */ new Set([
2793
+ "token",
2794
+ "secret",
2795
+ "sig",
2796
+ "signature",
2797
+ "awsaccesskeyid",
2798
+ "xamzsignature",
2799
+ "xamzcredential",
2800
+ "xamzsecuritytoken",
2801
+ "xgoogsignature"
2802
+ ]);
2803
+ var DEFAULT_SECRET_SET = buildSecretSet(DEFAULT_SECRET_KEY_NAMES);
2804
+ function redactUrlSecrets(text, secretSet = DEFAULT_SECRET_SET, placeholder = REDACTED_PLACEHOLDER) {
2805
+ if (text.length > DEFAULT_MAX_TEXT_FIELD_CHARS) return placeholder;
2806
+ if (!text.includes("://") && !text.includes("=")) return text;
2807
+ return text.replace(
2808
+ URL_USERINFO,
2809
+ (whole, scheme, userinfo) => userinfo === void 0 ? whole : `${scheme}://${placeholder}@`
2810
+ ).replace(URL_QUERY_PARAM, (whole, prefix, key) => {
2811
+ const normalized = normalizeKeyName(key);
2812
+ return secretSet.has(normalized) || URL_SECRET_QUERY_KEYS.has(normalized) ? `${prefix}${placeholder}` : whole;
2813
+ });
2814
+ }
1651
2815
  function redactSecretsInObject(value, options) {
1652
2816
  var _a, _b;
1653
2817
  const normalizedSecretSet = buildSecretSet((_a = options == null ? void 0 : options.secretKeyNames) != null ? _a : DEFAULT_SECRET_KEY_NAMES);
1654
2818
  const placeholder = (_b = options == null ? void 0 : options.placeholder) != null ? _b : REDACTED_PLACEHOLDER;
1655
2819
  const seen = /* @__PURE__ */ new WeakSet();
1656
2820
  const walk = (node) => {
2821
+ if (typeof node === "string") return redactUrlSecrets(node, normalizedSecretSet, placeholder);
1657
2822
  if (node === null || typeof node !== "object") return node;
1658
2823
  if (seen.has(node)) return "[CIRCULAR]";
1659
2824
  seen.add(node);
@@ -1752,6 +2917,7 @@ var TraceShipper = class {
1752
2917
  this.appGitAdded = /* @__PURE__ */ new WeakMap();
1753
2918
  this.queue = [];
1754
2919
  this.inFlight = /* @__PURE__ */ new Set();
2920
+ this.cutOff = new InFlightCutOff();
1755
2921
  this.hasShutdown = false;
1756
2922
  var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j;
1757
2923
  this.appGitContext = (_a = opts.appGitContext) != null ? _a : createAppGitContext(opts.appGit);
@@ -1764,6 +2930,7 @@ var TraceShipper = class {
1764
2930
  this.flushIntervalMs = (_d = opts.flushIntervalMs) != null ? _d : 1e3;
1765
2931
  this.maxBatchSize = (_e = opts.maxBatchSize) != null ? _e : 50;
1766
2932
  this.maxQueueSize = (_f = opts.maxQueueSize) != null ? _f : 5e3;
2933
+ this.flushTimeoutMs = resolveFlushTimeoutMs(opts.flushTimeoutMs);
1767
2934
  this.sdkName = (_g = opts.sdkName) != null ? _g : "core";
1768
2935
  this.prefix = `[raindrop-ai/${this.sdkName}]`;
1769
2936
  this.serviceName = (_h = opts.serviceName) != null ? _h : "raindrop.core";
@@ -2042,25 +3209,51 @@ var TraceShipper = class {
2042
3209
  }
2043
3210
  this.queue.push(redacted);
2044
3211
  if (this.queue.length >= this.maxBatchSize) {
2045
- void this.flush().catch(() => {
3212
+ void this.drain().catch(() => {
2046
3213
  });
2047
3214
  return;
2048
3215
  }
2049
- if (!this.timer) {
2050
- this.timer = setTimeout(() => {
2051
- this.timer = void 0;
2052
- void this.flush().catch(() => {
2053
- });
2054
- }, this.flushIntervalMs);
2055
- }
3216
+ this.scheduleDrain(this.flushIntervalMs);
3217
+ }
3218
+ scheduleDrain(delayMs) {
3219
+ if (this.timer) return;
3220
+ this.timer = setTimeout(() => {
3221
+ this.timer = void 0;
3222
+ void this.drain().catch(() => {
3223
+ });
3224
+ }, delayMs);
2056
3225
  }
3226
+ /**
3227
+ * Send every queued span within `flushTimeoutMs` (CONTRACT 1.2.1). Resolves,
3228
+ * never rejects, to whether they all reached ingest, and warns when they
3229
+ * didn't. Spans not yet sent at the deadline stay queued.
3230
+ */
2057
3231
  async flush() {
2058
- var _a;
2059
- if (!this.enabled) {
2060
- await Promise.all([...this.inFlight].map((p) => p.catch(() => {
2061
- })));
2062
- return;
3232
+ const result = await this.drainWithin(Date.now() + this.flushTimeoutMs);
3233
+ if (!result.delivered && this.shutdownDeadlineAt === void 0) {
3234
+ rateLimitedLog(
3235
+ `${this.prefix}.flush_undelivered`,
3236
+ () => console.warn(`${this.prefix} ${describeUndelivered("flush()", result)}`)
3237
+ );
2063
3238
  }
3239
+ return asCompat(result);
3240
+ }
3241
+ async drainWithin(deadlineAt) {
3242
+ const fates = this.enabled ? await this.drain(deadlineAt) : [];
3243
+ const running = [...this.inFlight];
3244
+ const { settled, stillRunning } = await awaitEarlierSends(
3245
+ running,
3246
+ deadlineAt,
3247
+ this.cutOff,
3248
+ (p) => this.inFlight.has(p)
3249
+ );
3250
+ return summarize2(fates, stillRunning, !settled);
3251
+ }
3252
+ /** Send queued batches; with a deadline, a deferred batch goes back on the queue. */
3253
+ async drain(deadlineAt) {
3254
+ var _a;
3255
+ const fates = [];
3256
+ if (!this.enabled) return fates;
2064
3257
  if (this.timer) {
2065
3258
  clearTimeout(this.timer);
2066
3259
  this.timer = void 0;
@@ -2074,10 +3267,12 @@ var TraceShipper = class {
2074
3267
  if (spans) spans.push(span);
2075
3268
  else groups.set(destination, [span]);
2076
3269
  }
3270
+ const requeue = [];
3271
+ let retryAtMs;
2077
3272
  for (const [destination, spans] of groups) {
2078
3273
  if (!destination && !this.writeKey) continue;
2079
3274
  if ((destination == null ? void 0 : destination.kind) === "workshop") continue;
2080
- const opts = this.requestOpts();
3275
+ const opts = this.requestOpts(deadlineAt);
2081
3276
  if (!opts) {
2082
3277
  rateLimitedLog(
2083
3278
  `${this.prefix}.shutdown_deadline`,
@@ -2085,6 +3280,7 @@ var TraceShipper = class {
2085
3280
  `${this.prefix} shutdown flush deadline exceeded; dropping ${spans.length} spans`
2086
3281
  )
2087
3282
  );
3283
+ fates.push("dropped");
2088
3284
  continue;
2089
3285
  }
2090
3286
  const body = buildExportTraceServiceRequest(spans, this.serviceName, this.serviceVersion);
@@ -2100,35 +3296,56 @@ var TraceShipper = class {
2100
3296
  endpoint: url
2101
3297
  });
2102
3298
  }
2103
- const p = postJson(url, body, headers, opts);
2104
- this.inFlight.add(p);
3299
+ const sending = sendJson(url, body, headers, opts);
3300
+ const tracked = sending.then(() => void 0);
3301
+ this.inFlight.add(tracked);
3302
+ let outcome;
2105
3303
  try {
2106
- try {
2107
- await p;
2108
- if (this.debug) console.log(`${this.prefix} sent ${spans.length} spans`);
2109
- } catch (err) {
2110
- const msg = err instanceof Error ? err.message : String(err);
2111
- rateLimitedLog(
2112
- `${this.prefix}.send_spans_failed`,
2113
- () => console.warn(`${this.prefix} failed to send ${spans.length} spans: ${msg}`)
2114
- );
2115
- }
3304
+ outcome = await sending;
2116
3305
  } finally {
2117
- this.inFlight.delete(p);
3306
+ this.inFlight.delete(tracked);
3307
+ }
3308
+ const fate = fateOf(outcome, this.shutdownDeadlineAt !== void 0 || this.hasShutdown);
3309
+ fates.push(fate);
3310
+ if (fate === "queued") {
3311
+ requeue.push(...spans);
3312
+ if (outcome.status === "deferred" && outcome.retryAtMs !== void 0) {
3313
+ retryAtMs = Math.max(retryAtMs != null ? retryAtMs : 0, outcome.retryAtMs);
3314
+ }
3315
+ } else if (fate === "dropped") {
3316
+ const reason = outcome.status === "cut_off" ? "cut off at the flush deadline (not resent: it may have reached ingest)" : outcome.status === "deferred" ? "not sent before the shutdown deadline" : "error" in outcome && outcome.error ? outcome.error.message : outcome.status;
3317
+ rateLimitedLog(
3318
+ `${this.prefix}.send_spans_failed`,
3319
+ () => console.warn(`${this.prefix} failed to send ${spans.length} spans: ${reason}`)
3320
+ );
3321
+ } else if (this.debug) {
3322
+ console.log(`${this.prefix} sent ${spans.length} spans`);
2118
3323
  }
2119
3324
  }
3325
+ if (requeue.length > 0) {
3326
+ this.queue.unshift(...requeue);
3327
+ if (this.queue.length > this.maxQueueSize) {
3328
+ this.queue.splice(this.maxQueueSize);
3329
+ }
3330
+ this.scheduleDrain(Math.max(this.flushIntervalMs, (retryAtMs != null ? retryAtMs : 0) - Date.now()));
3331
+ break;
3332
+ }
2120
3333
  }
3334
+ return fates;
2121
3335
  }
2122
- /** See EventShipper.requestOpts — same shutdown-budget semantics. */
2123
- requestOpts() {
3336
+ /** See EventShipper.requestOpts — same deadline semantics. */
3337
+ requestOpts(deadlineAt) {
3338
+ const opts = this.scheduleFor(deadlineAt);
3339
+ return opts && { ...opts, cutOff: this.cutOff.signal };
3340
+ }
3341
+ scheduleFor(deadlineAt) {
2124
3342
  if (this.shutdownDeadlineAt !== void 0) {
2125
- const remainingMs = this.shutdownDeadlineAt - Date.now();
2126
- if (remainingMs <= 0) return null;
3343
+ if (this.shutdownDeadlineAt - Date.now() <= 0) return null;
2127
3344
  return {
2128
- maxAttempts: 1,
3345
+ maxAttempts: 3,
2129
3346
  debug: this.debug,
2130
3347
  sdkName: this.sdkName,
2131
- timeoutMs: Math.min(DEFAULT_REQUEST_TIMEOUT_MS, remainingMs)
3348
+ deadlineAt: Math.min(this.shutdownDeadlineAt, deadlineAt != null ? deadlineAt : Infinity)
2132
3349
  };
2133
3350
  }
2134
3351
  if (this.hasShutdown) {
@@ -2139,27 +3356,28 @@ var TraceShipper = class {
2139
3356
  timeoutMs: POST_SHUTDOWN_TIMEOUT_MS
2140
3357
  };
2141
3358
  }
2142
- return { maxAttempts: 3, debug: this.debug, sdkName: this.sdkName };
3359
+ return { maxAttempts: 3, debug: this.debug, sdkName: this.sdkName, deadlineAt };
2143
3360
  }
3361
+ /**
3362
+ * Flush within `flushTimeoutMs` and stop; spans left at the deadline are
3363
+ * dropped with a warning. Resolves, never rejects.
3364
+ */
2144
3365
  async shutdown() {
2145
3366
  this.hasShutdown = true;
2146
- this.shutdownDeadlineAt = Date.now() + SHUTDOWN_DEADLINE_MS;
3367
+ const deadlineAt = Date.now() + this.flushTimeoutMs;
3368
+ this.shutdownDeadlineAt = deadlineAt;
3369
+ let result = { ...DELIVERED };
2147
3370
  try {
2148
3371
  if (this.timer) {
2149
3372
  clearTimeout(this.timer);
2150
3373
  this.timer = void 0;
2151
3374
  }
2152
- const drain = async () => {
2153
- await this.flush();
2154
- await Promise.all([...this.inFlight].map((p) => p.catch(() => {
2155
- })));
2156
- };
2157
- const settled = await raceWithTimeout(drain(), SHUTDOWN_DEADLINE_MS);
2158
- if (!settled) {
3375
+ result = await this.drainWithin(deadlineAt);
3376
+ if (!result.delivered) {
2159
3377
  rateLimitedLog(
2160
3378
  `${this.prefix}.shutdown_deadline`,
2161
3379
  () => console.warn(
2162
- `${this.prefix} shutdown flush deadline exceeded; abandoning in-flight spans`
3380
+ `${this.prefix} shutdown flush deadline exceeded; ${describeUndelivered("shutdown()", result)}`
2163
3381
  )
2164
3382
  );
2165
3383
  }
@@ -2167,10 +3385,74 @@ var TraceShipper = class {
2167
3385
  this.shutdownDeadlineAt = void 0;
2168
3386
  if (this.ownsAppGitContext) this.appGitContext.dispose();
2169
3387
  }
3388
+ return asCompat(result);
2170
3389
  }
2171
3390
  };
3391
+ function eventShipperOptions(client, extras = {}) {
3392
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i;
3393
+ return {
3394
+ ...extras,
3395
+ writeKey: (_a = extras.writeKey) != null ? _a : client.writeKey,
3396
+ endpoint: (_b = extras.endpoint) != null ? _b : client.endpoint,
3397
+ projectId: (_c = extras.projectId) != null ? _c : client.projectId,
3398
+ debug: (_e = (_d = extras.debug) != null ? _d : client.debug) != null ? _e : false,
3399
+ maxTextFieldChars: (_f = extras.maxTextFieldChars) != null ? _f : client.maxTextFieldChars,
3400
+ flushTimeoutMs: (_g = extras.flushTimeoutMs) != null ? _g : client.flushTimeoutMs,
3401
+ redactPii: (_h = extras.redactPii) != null ? _h : client.redactPii,
3402
+ redactPiiFields: (_i = extras.redactPiiFields) != null ? _i : client.redactPiiFields
3403
+ };
3404
+ }
3405
+ function traceShipperOptions(client, extras = {}) {
3406
+ var _a, _b, _c, _d, _e, _f, _g;
3407
+ return {
3408
+ ...extras,
3409
+ writeKey: (_a = extras.writeKey) != null ? _a : client.writeKey,
3410
+ endpoint: (_b = extras.endpoint) != null ? _b : client.endpoint,
3411
+ projectId: (_c = extras.projectId) != null ? _c : client.projectId,
3412
+ debug: (_e = (_d = extras.debug) != null ? _d : client.debug) != null ? _e : false,
3413
+ maxTextFieldChars: (_f = extras.maxTextFieldChars) != null ? _f : client.maxTextFieldChars,
3414
+ flushTimeoutMs: (_g = extras.flushTimeoutMs) != null ? _g : client.flushTimeoutMs
3415
+ };
3416
+ }
3417
+ function createShippers(client, setup = {}) {
3418
+ return {
3419
+ eventShipper: new EventShipper(eventShipperOptions(client, { ...setup.common, ...setup.event })),
3420
+ traceShipper: new TraceShipper(traceShipperOptions(client, { ...setup.common, ...setup.trace }))
3421
+ };
3422
+ }
3423
+ function isFlushResult(value) {
3424
+ return typeof value === "object" && value !== null && "delivered" in value && typeof value.delivered === "boolean" && "dropped" in value && typeof value.dropped === "number" && "queued" in value && typeof value.queued === "number" && "inFlight" in value && typeof value.inFlight === "number" && "timedOut" in value && typeof value.timedOut === "boolean";
3425
+ }
3426
+ var UNDELIVERED = Object.freeze({
3427
+ delivered: false,
3428
+ timedOut: false,
3429
+ dropped: 0,
3430
+ queued: 0,
3431
+ inFlight: 0
3432
+ });
3433
+ async function settle(call, what) {
3434
+ try {
3435
+ const result = await call();
3436
+ return isFlushResult(result) ? result : DELIVERED;
3437
+ } catch (error) {
3438
+ console.warn(`[raindrop-ai] ${what} failed: ${error instanceof Error ? error.message : String(error)}`);
3439
+ return UNDELIVERED;
3440
+ }
3441
+ }
3442
+ async function flushShippers(...shippers) {
3443
+ const results = await Promise.all(
3444
+ shippers.flatMap((s) => s ? [settle(() => s.flush(), "flush()")] : [])
3445
+ );
3446
+ return asCompat(combineResults(...results));
3447
+ }
3448
+ async function shutdownShippers(...shippers) {
3449
+ const results = await Promise.all(
3450
+ shippers.flatMap((s) => s ? [settle(() => s.shutdown(), "shutdown()")] : [])
3451
+ );
3452
+ return asCompat(combineResults(...results));
3453
+ }
2172
3454
  var PROMPT_TOOLS_ATTRIBUTE = "ai.prompt.tools";
2173
- function isRecord(value) {
3455
+ function isRecord3(value) {
2174
3456
  return typeof value === "object" && value !== null && !Array.isArray(value);
2175
3457
  }
2176
3458
  var JSON_SCHEMA_KEYS = [
@@ -2188,17 +3470,17 @@ var JSON_SCHEMA_KEYS = [
2188
3470
  "required"
2189
3471
  ];
2190
3472
  function isJsonSchema(value) {
2191
- if (!isRecord(value)) return false;
3473
+ if (!isRecord3(value)) return false;
2192
3474
  if (typeof value["~standard"] === "object") return false;
2193
- if (isRecord(value._def) || isRecord(value._zod)) return false;
3475
+ if (isRecord3(value._def) || isRecord3(value._zod)) return false;
2194
3476
  if (Object.keys(value).length === 0) return true;
2195
3477
  return JSON_SCHEMA_KEYS.some((key) => key in value);
2196
3478
  }
2197
3479
  function toJsonSchema(value, options) {
2198
3480
  if (value === void 0 || value === null) return void 0;
2199
3481
  if (isJsonSchema(value)) return value;
2200
- if (isRecord(value) && isJsonSchema(value.jsonSchema)) return value.jsonSchema;
2201
- if (isRecord(value) && isJsonSchema(value.json)) return value.json;
3482
+ if (isRecord3(value) && isJsonSchema(value.jsonSchema)) return value.jsonSchema;
3483
+ if (isRecord3(value) && isJsonSchema(value.json)) return value.json;
2202
3484
  if (options == null ? void 0 : options.schema) {
2203
3485
  try {
2204
3486
  return options.schema(value);
@@ -2221,23 +3503,23 @@ function functionTool(name, description, schema, options) {
2221
3503
  }
2222
3504
  function normalizeToolDeclaration(tool, options, fallbackName) {
2223
3505
  var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j;
2224
- if (!isRecord(tool)) return void 0;
3506
+ if (!isRecord3(tool)) return void 0;
2225
3507
  if (tool.type === "provider-defined" || tool.type === "provider") {
2226
3508
  const name2 = (_b = (_a = pickString(tool.name)) != null ? _a : pickString(tool.id)) != null ? _b : fallbackName;
2227
3509
  if (name2 === void 0) return void 0;
2228
3510
  const declaration = { type: "provider-defined", name: name2 };
2229
3511
  const id = pickString(tool.id);
2230
3512
  if (id !== void 0) declaration.id = id;
2231
- if (isRecord(tool.args)) declaration.args = tool.args;
3513
+ if (isRecord3(tool.args)) declaration.args = tool.args;
2232
3514
  return declaration;
2233
3515
  }
2234
- if (isRecord(tool.toolSpec)) {
3516
+ if (isRecord3(tool.toolSpec)) {
2235
3517
  const spec = tool.toolSpec;
2236
3518
  const name2 = (_c = pickString(spec.name)) != null ? _c : fallbackName;
2237
3519
  if (name2 === void 0) return void 0;
2238
3520
  return functionTool(name2, spec.description, spec.inputSchema, options);
2239
3521
  }
2240
- if (isRecord(tool.function)) {
3522
+ if (isRecord3(tool.function)) {
2241
3523
  const fn = tool.function;
2242
3524
  const name2 = (_d = pickString(fn.name)) != null ? _d : fallbackName;
2243
3525
  if (name2 === void 0) return void 0;
@@ -2263,7 +3545,7 @@ function isGeminiToolGroup(tool) {
2263
3545
  if (Array.isArray(tool.functionDeclarations)) return true;
2264
3546
  const keys = Object.keys(tool);
2265
3547
  if (keys.length === 0 || NOT_A_GEMINI_GROUP_KEYS.some((key) => key in tool)) return false;
2266
- return keys.every((key) => isRecord(tool[key]));
3548
+ return keys.every((key) => isRecord3(tool[key]));
2267
3549
  }
2268
3550
  function expandGeminiTool(tool, options) {
2269
3551
  const declarations = [];
@@ -2273,7 +3555,7 @@ function expandGeminiTool(tool, options) {
2273
3555
  const normalized = normalizeToolDeclaration(declaration, options);
2274
3556
  if (normalized) declarations.push(normalized);
2275
3557
  }
2276
- } else if (isRecord(value)) {
3558
+ } else if (isRecord3(value)) {
2277
3559
  declarations.push({ type: "provider-defined", id: key, name: key, args: value });
2278
3560
  }
2279
3561
  }
@@ -2283,14 +3565,14 @@ function normalizeToolDeclarations(tools, options) {
2283
3565
  if (tools === void 0 || tools === null) return void 0;
2284
3566
  if (Array.isArray(tools)) {
2285
3567
  return tools.flatMap((tool) => {
2286
- if (isRecord(tool) && isGeminiToolGroup(tool)) {
3568
+ if (isRecord3(tool) && isGeminiToolGroup(tool)) {
2287
3569
  return expandGeminiTool(tool, options);
2288
3570
  }
2289
3571
  const declaration = normalizeToolDeclaration(tool, options);
2290
3572
  return declaration ? [declaration] : [];
2291
3573
  });
2292
3574
  }
2293
- if (!isRecord(tools)) return void 0;
3575
+ if (!isRecord3(tools)) return void 0;
2294
3576
  return Object.entries(tools).flatMap(([key, tool]) => {
2295
3577
  const declaration = normalizeToolDeclaration(tool, options, key);
2296
3578
  return declaration ? [declaration] : [];
@@ -2436,39 +3718,6 @@ var resolveLocal = (sourceDirectory, detectBranch, completed) => {
2436
3718
  function createAppGitContext2(options, runtime = {}) {
2437
3719
  return new AppGitContext(options, { ...runtime, resolveLocal });
2438
3720
  }
2439
- function hasDestination(options) {
2440
- var _a;
2441
- if (options.enabled === false) return false;
2442
- return Boolean(((_a = options.writeKey) == null ? void 0 : _a.trim()) || resolveLocalDebuggerBaseUrl(options.localDebuggerUrl));
2443
- }
2444
- var EventShipper2 = class extends EventShipper {
2445
- constructor(options) {
2446
- var _a;
2447
- const ownsAppGitContext = options.appGitContext === void 0;
2448
- const appGitContext = (_a = options.appGitContext) != null ? _a : createAppGitContext2(options.appGit, { allowDiscovery: hasDestination(options) });
2449
- try {
2450
- super({ ...options, appGitContext });
2451
- } catch (error) {
2452
- if (ownsAppGitContext) appGitContext.dispose();
2453
- throw error;
2454
- }
2455
- if (ownsAppGitContext) this.ownAppGitContextForShutdown();
2456
- }
2457
- };
2458
- var TraceShipper2 = class extends TraceShipper {
2459
- constructor(options) {
2460
- var _a;
2461
- const ownsAppGitContext = options.appGitContext === void 0;
2462
- const appGitContext = (_a = options.appGitContext) != null ? _a : createAppGitContext2(options.appGit, { allowDiscovery: hasDestination(options) });
2463
- try {
2464
- super({ ...options, appGitContext });
2465
- } catch (error) {
2466
- if (ownsAppGitContext) appGitContext.dispose();
2467
- throw error;
2468
- }
2469
- this.ownsAppGitContext = ownsAppGitContext;
2470
- }
2471
- };
2472
3721
  globalThis.RAINDROP_ASYNC_LOCAL_STORAGE = import_async_hooks.AsyncLocalStorage;
2473
3722
  var SUPPRESS_TRACING_KEY = /* @__PURE__ */ Symbol.for(
2474
3723
  "OpenTelemetry SDK Context Key SUPPRESS_TRACING"
@@ -2606,6 +3855,8 @@ var RaindropManagedAgentsHandler = class {
2606
3855
  eventName: (_e = context.eventName) != null ? _e : "ai_generation",
2607
3856
  input: void 0,
2608
3857
  model: void 0,
3858
+ // Caller-supplied properties win over the derived keys below (W6).
3859
+ callerProperties: context.properties,
2609
3860
  properties: {
2610
3861
  ...context.properties,
2611
3862
  "ai.provider": "openai",
@@ -2926,7 +4177,7 @@ function isRecord2(value) {
2926
4177
  // package.json
2927
4178
  var package_default = {
2928
4179
  name: "@raindrop-ai/openai-managed-agents",
2929
- version: "0.0.3",
4180
+ version: "0.0.5",
2930
4181
  description: "Raindrop tracing for the OpenAI Agents API",
2931
4182
  main: "dist/index.js",
2932
4183
  module: "dist/index.mjs",
@@ -2968,11 +4219,11 @@ var package_default = {
2968
4219
  "@raindrop-ai/core": "workspace:*",
2969
4220
  "@types/node": "^20.11.17",
2970
4221
  msw: "^2.12.7",
2971
- tsup: "^8.4.0",
4222
+ tsup: "^9.0.0",
2972
4223
  typescript: "^5.3.3",
2973
- vitest: "^2.1.9",
4224
+ vitest: "^4.1.12",
2974
4225
  openai: "7.15.0",
2975
- zod: "^3.23.8"
4226
+ zod: "^4.6.6"
2976
4227
  },
2977
4228
  tsup: {
2978
4229
  entry: [
@@ -3094,7 +4345,7 @@ function wrapManagedAgents(client, handler, context) {
3094
4345
 
3095
4346
  // src/index.ts
3096
4347
  function createRaindropOpenAIManagedAgents(opts = {}) {
3097
- var _a, _b;
4348
+ var _a;
3098
4349
  const enabled = Boolean((_a = opts.writeKey) == null ? void 0 : _a.trim());
3099
4350
  if (!enabled)
3100
4351
  console.warn(
@@ -3103,44 +4354,33 @@ function createRaindropOpenAIManagedAgents(opts = {}) {
3103
4354
  const appGitContext = createAppGitContext2(enabled ? opts.appGit : false, {
3104
4355
  allowLocalGit: false
3105
4356
  });
3106
- const shipperOptions = {
3107
- writeKey: opts.writeKey,
3108
- endpoint: opts.endpoint,
3109
- projectId: opts.projectId,
3110
- enabled,
3111
- debug: (_b = opts.debug) != null ? _b : false,
3112
- sdkName: "openai-managed-agents",
3113
- libraryName,
3114
- libraryVersion,
3115
- appGitContext,
3116
- maxTextFieldChars: opts.maxTextFieldChars,
3117
- localDebuggerUrl: opts.localWorkshopUrl === false ? null : opts.localWorkshopUrl
3118
- };
3119
- const events = new EventShipper2(shipperOptions);
3120
- const traces = new TraceShipper2({
3121
- ...shipperOptions,
3122
- serviceName: "raindrop.openai-managed-agents",
3123
- serviceVersion: libraryVersion
4357
+ const { eventShipper: events, traceShipper: traces } = createShippers(opts, {
4358
+ common: {
4359
+ enabled,
4360
+ sdkName: "openai-managed-agents",
4361
+ appGitContext,
4362
+ localDebuggerUrl: opts.localWorkshopUrl === false ? null : opts.localWorkshopUrl
4363
+ },
4364
+ event: { libraryName, libraryVersion },
4365
+ trace: { serviceName: "raindrop.openai-managed-agents", serviceVersion: libraryVersion }
3124
4366
  });
3125
4367
  const handler = new RaindropManagedAgentsHandler(events, traces, opts);
3126
4368
  return {
3127
4369
  handler,
3128
4370
  wrap: (client, context) => wrapManagedAgents(client, handler, context),
3129
- events: {
3130
- patch: (id, patch) => events.patch(id, patch),
3131
- finish: (id, patch) => events.finish(id, patch),
3132
- addAttachments: (id, attachments) => events.patch(id, { attachments }),
3133
- setProperties: (id, properties) => events.patch(id, { properties })
4371
+ get lastEventId() {
4372
+ return events == null ? void 0 : events.lastFinishedEventId;
3134
4373
  },
4374
+ events: createEventsApi(events),
3135
4375
  users: { identify: (users) => events.identify(users) },
3136
4376
  signals: { track: (signal) => events.trackSignal(signal) },
3137
- async flush() {
3138
- await Promise.all([events.flush(), traces.flush()]);
4377
+ flush() {
4378
+ return flushShippers(events, traces);
3139
4379
  },
3140
4380
  async shutdown() {
3141
4381
  try {
3142
4382
  handler.shutdown();
3143
- await Promise.all([events.shutdown(), traces.shutdown()]);
4383
+ return await shutdownShippers(events, traces);
3144
4384
  } finally {
3145
4385
  appGitContext.dispose();
3146
4386
  }