@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.mjs CHANGED
@@ -1,4 +1,33 @@
1
- // ../core/dist/chunk-BYBMRLZM.js
1
+ // ../core/dist/chunk-ZCB62SIO.js
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __commonJS = (cb, mod) => function __require() {
9
+ try {
10
+ return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
11
+ } catch (e) {
12
+ throw mod = 0, e;
13
+ }
14
+ };
15
+ var __copyProps = (to, from, except, desc) => {
16
+ if (from && typeof from === "object" || typeof from === "function") {
17
+ for (let key of __getOwnPropNames(from))
18
+ if (!__hasOwnProp.call(to, key) && key !== except)
19
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
20
+ }
21
+ return to;
22
+ };
23
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
24
+ // If the importer is in node compatibility mode or this is not an ESM
25
+ // file that has been converted to a CommonJS file using a Babel-
26
+ // compatible transform (i.e. "__esModule" has not been set), then set
27
+ // "default" to the CommonJS "module.exports" for node compatibility.
28
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
29
+ mod
30
+ ));
2
31
  var EVAL_CORRELATION_ID_ATTRIBUTE = "raindrop.eval_correlation_id";
3
32
  var PROBE_SCOPE = { correlationId: "" };
4
33
  var EvalScopeStorage = class {
@@ -60,21 +89,246 @@ function evalScopeAttributes() {
60
89
  return { [EVAL_CORRELATION_ID_ATTRIBUTE]: scope.correlationId };
61
90
  }
62
91
 
63
- // ../core/dist/chunk-I6TVJQEC.js
92
+ // ../core/dist/chunk-AX36JKJL.js
93
+ var require_NameRedactor = __commonJS({
94
+ "../redact-pii/dist/built-ins/NameRedactor.js"(exports) {
95
+ Object.defineProperty(exports, "__esModule", { value: true });
96
+ exports.NameRedactor = void 0;
97
+ var greetingRegex = /(^|\.\s+)(dear|hi|hello|greetings|hey|hey there)/gi;
98
+ 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;
99
+ var greetingOrClosing = new RegExp("(((" + greetingRegex.source + ")|(" + closingRegex.source + "\\s*[,.!]*))[\\s-]*)", "gi");
100
+ var genericName = new RegExp("( ?(([A-Z][a-z]{1,30})|([A-Z]\\.))){1,10}([,.]|[,.]?$)", "my");
101
+ var NameRedactor = class {
102
+ constructor(replaceWith = "PERSON_NAME") {
103
+ this.replaceWith = replaceWith;
104
+ }
105
+ redact(textToRedact) {
106
+ var _a;
107
+ greetingOrClosing.lastIndex = 0;
108
+ genericName.lastIndex = 0;
109
+ const pieces = [];
110
+ let copiedThrough = 0;
111
+ let changed = false;
112
+ let greetingOrClosingMatch = greetingOrClosing.exec(textToRedact);
113
+ while (greetingOrClosingMatch !== null) {
114
+ genericName.lastIndex = greetingOrClosing.lastIndex;
115
+ const genericNameMatch = genericName.exec(textToRedact);
116
+ if (genericNameMatch !== null && genericNameMatch.index === greetingOrClosing.lastIndex) {
117
+ const suffix = (_a = genericNameMatch[5]) !== null && _a !== void 0 ? _a : "";
118
+ const end = genericNameMatch.index + genericNameMatch[0].length;
119
+ pieces.push(textToRedact.slice(copiedThrough, genericNameMatch.index), this.replaceWith, suffix);
120
+ copiedThrough = end;
121
+ changed = true;
122
+ greetingOrClosing.lastIndex = end - suffix.length;
123
+ }
124
+ greetingOrClosingMatch = greetingOrClosing.exec(textToRedact);
125
+ }
126
+ if (!changed)
127
+ return textToRedact;
128
+ pieces.push(textToRedact.slice(copiedThrough));
129
+ return pieces.join("");
130
+ }
131
+ };
132
+ exports.NameRedactor = NameRedactor;
133
+ }
134
+ });
135
+ var require_simple_regexp_patterns = __commonJS({
136
+ "../redact-pii/dist/built-ins/simple-regexp-patterns.js"(exports) {
137
+ Object.defineProperty(exports, "__esModule", { value: true });
138
+ exports.url = exports.digits = exports.credentials = exports.password = exports.username = exports.emailAddress = exports.usSocialSecurityNumber = exports.ipAddress = exports.phoneNumber = exports.zipcode = exports.streetAddress = exports.creditCardNumber = void 0;
139
+ var aptRegex = /(apt|bldg|dept|fl|hngr|lot|pier|rm|ste|slip|trlr|unit|#)\.? *[a-z0-9-]+\b/gi;
140
+ var poBoxRegex = /P\.? ?O\.? *Box +\d+/gi;
141
+ var roadRegex = /(street|st|road|rd|avenue|ave|drive|dr|loop|court|ct|circle|cir|lane|ln|boulevard|blvd|way)\.?\b/gi;
142
+ exports.creditCardNumber = /\d{4}[ -]?\d{4}[ -]?\d{4}[ -]?\d{4}|\d{4}[ -]?\d{6}[ -]?\d{4}\d?/g;
143
+ exports.streetAddress = new RegExp("(\\d{1,10}\\s*(\\w{1,30} ){1,2}" + roadRegex.source + "(\\s+" + aptRegex.source + ")?)|(" + poBoxRegex.source + ")", "gi");
144
+ exports.zipcode = /\b\d{5}\b(-\d{4})?\b/g;
145
+ exports.phoneNumber = /(\(?\+?[0-9]{1,2}\)?[-. ]?)?(\(?[0-9]{3}\)?|[0-9]{3})[-. ]?([0-9]{3}[-. ]?[0-9]{4}|\b[A-Z0-9]{7}\b)/g;
146
+ exports.ipAddress = /(\d{1,3}(\.\d{1,3}){3}|[0-9A-F]{4}(:[0-9A-F]{4}){5}(::|(:0000)+))/gi;
147
+ exports.usSocialSecurityNumber = /\b\d{3}[ -.]\d{2}[ -.]\d{4}\b/g;
148
+ exports.emailAddress = /([a-z0-9_\-.+]{1,64})@\w+(\.\w+)*/gi;
149
+ exports.username = /(user( ?name)?|login): \S+/gi;
150
+ exports.password = /(pass(word|phrase)?|secret): \S+/gi;
151
+ exports.credentials = /(login( cred(ential)?s| info(rmation)?)?|cred(ential)?s) ?:\s*\S+\s+\/?\s*\S+/gi;
152
+ exports.digits = /\b\d{4,}\b/g;
153
+ exports.url = /([^\s:/?#]{1,64}):\/\/([^/?#\s]*)([^?#\s]*)(\?([^#\s]*))?(#([^\s]*))?/g;
154
+ }
155
+ });
156
+ var require_utils = __commonJS({
157
+ "../redact-pii/dist/utils.js"(exports) {
158
+ Object.defineProperty(exports, "__esModule", { value: true });
159
+ exports.snakeCase = exports.isSyncRedactor = exports.isSimpleRegexpCustomRedactorConfig = void 0;
160
+ function isSimpleRegexpCustomRedactorConfig(redactor2) {
161
+ return typeof redactor2.regexpPattern !== "undefined";
162
+ }
163
+ exports.isSimpleRegexpCustomRedactorConfig = isSimpleRegexpCustomRedactorConfig;
164
+ function isSyncRedactor(redactor2) {
165
+ return typeof redactor2.redact === "function";
166
+ }
167
+ exports.isSyncRedactor = isSyncRedactor;
168
+ function snakeCase(input = "") {
169
+ var _a;
170
+ 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 : [];
171
+ return words.map((word) => word.toLowerCase()).join("_");
172
+ }
173
+ exports.snakeCase = snakeCase;
174
+ }
175
+ });
176
+ var require_SimpleRegexpRedactor = __commonJS({
177
+ "../redact-pii/dist/built-ins/SimpleRegexpRedactor.js"(exports) {
178
+ Object.defineProperty(exports, "__esModule", { value: true });
179
+ exports.SimpleRegexpRedactor = void 0;
180
+ var utils_1 = require_utils();
181
+ var SimpleRegexpRedactor = class {
182
+ constructor({ replaceWith = (0, utils_1.snakeCase)().toUpperCase(), regexpPattern: regexpMatcher }) {
183
+ this.replaceWith = replaceWith;
184
+ this.regexpMatcher = regexpMatcher;
185
+ }
186
+ redact(textToRedact) {
187
+ return textToRedact.replace(this.regexpMatcher, this.replaceWith);
188
+ }
189
+ };
190
+ exports.SimpleRegexpRedactor = SimpleRegexpRedactor;
191
+ }
192
+ });
193
+ var require_composition = __commonJS({
194
+ "../redact-pii/dist/composition.js"(exports) {
195
+ Object.defineProperty(exports, "__esModule", { value: true });
196
+ exports.composeChildRedactors = void 0;
197
+ var NameRedactor_1 = require_NameRedactor();
198
+ var simpleRegexpBuiltIns = require_simple_regexp_patterns();
199
+ var SimpleRegexpRedactor_1 = require_SimpleRegexpRedactor();
200
+ var utils_1 = require_utils();
201
+ function normalizeCustomRedactorConfig(redactorConfig) {
202
+ return (0, utils_1.isSimpleRegexpCustomRedactorConfig)(redactorConfig) ? new SimpleRegexpRedactor_1.SimpleRegexpRedactor({
203
+ regexpPattern: redactorConfig.regexpPattern,
204
+ replaceWith: redactorConfig.replaceWith
205
+ }) : redactorConfig;
206
+ }
207
+ function composeChildRedactors(opts = {}) {
208
+ var _a, _b, _c, _d;
209
+ const childRedactors = [];
210
+ if (opts.customRedactors && opts.customRedactors.before) {
211
+ opts.customRedactors.before.map(normalizeCustomRedactorConfig).forEach((redactor2) => childRedactors.push(redactor2));
212
+ }
213
+ for (const regexpName of Object.keys(simpleRegexpBuiltIns)) {
214
+ if (!opts.builtInRedactors || !opts.builtInRedactors[regexpName] || opts.builtInRedactors[regexpName].enabled !== false) {
215
+ childRedactors.push(new SimpleRegexpRedactor_1.SimpleRegexpRedactor({
216
+ regexpPattern: simpleRegexpBuiltIns[regexpName],
217
+ 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()
218
+ }));
219
+ }
220
+ }
221
+ if (!opts.builtInRedactors || !opts.builtInRedactors.names || opts.builtInRedactors.names.enabled !== false) {
222
+ 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));
223
+ }
224
+ if (opts.customRedactors && opts.customRedactors.after) {
225
+ opts.customRedactors.after.map(normalizeCustomRedactorConfig).forEach((redactor2) => childRedactors.push(redactor2));
226
+ }
227
+ return childRedactors;
228
+ }
229
+ exports.composeChildRedactors = composeChildRedactors;
230
+ }
231
+ });
232
+ var require_AsyncCompositeRedactor = __commonJS({
233
+ "../redact-pii/dist/AsyncCompositeRedactor.js"(exports) {
234
+ var __awaiter = exports && exports.__awaiter || function(thisArg, _arguments, P, generator) {
235
+ function adopt(value) {
236
+ return value instanceof P ? value : new P(function(resolve2) {
237
+ resolve2(value);
238
+ });
239
+ }
240
+ return new (P || (P = Promise))(function(resolve2, reject) {
241
+ function fulfilled(value) {
242
+ try {
243
+ step(generator.next(value));
244
+ } catch (e) {
245
+ reject(e);
246
+ }
247
+ }
248
+ function rejected(value) {
249
+ try {
250
+ step(generator["throw"](value));
251
+ } catch (e) {
252
+ reject(e);
253
+ }
254
+ }
255
+ function step(result) {
256
+ result.done ? resolve2(result.value) : adopt(result.value).then(fulfilled, rejected);
257
+ }
258
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
259
+ });
260
+ };
261
+ Object.defineProperty(exports, "__esModule", { value: true });
262
+ exports.AsyncCompositeRedactor = void 0;
263
+ var composition_1 = require_composition();
264
+ var utils_1 = require_utils();
265
+ var AsyncCompositeRedactor = class {
266
+ constructor(opts) {
267
+ this.childRedactors = [];
268
+ this.redactAsync = (textToRedact) => __awaiter(this, void 0, void 0, function* () {
269
+ for (const redactor2 of this.childRedactors) {
270
+ if ((0, utils_1.isSyncRedactor)(redactor2)) {
271
+ textToRedact = redactor2.redact(textToRedact);
272
+ } else {
273
+ textToRedact = yield redactor2.redactAsync(textToRedact);
274
+ }
275
+ }
276
+ return textToRedact;
277
+ });
278
+ this.childRedactors = (0, composition_1.composeChildRedactors)(opts);
279
+ }
280
+ };
281
+ exports.AsyncCompositeRedactor = AsyncCompositeRedactor;
282
+ }
283
+ });
284
+ var require_SyncCompositeRedactor = __commonJS({
285
+ "../redact-pii/dist/SyncCompositeRedactor.js"(exports) {
286
+ Object.defineProperty(exports, "__esModule", { value: true });
287
+ exports.SyncCompositeRedactor = void 0;
288
+ var composition_1 = require_composition();
289
+ var SyncCompositeRedactor = class {
290
+ constructor(opts) {
291
+ this.childRedactors = [];
292
+ this.redact = (textToRedact) => {
293
+ for (const redactor2 of this.childRedactors) {
294
+ textToRedact = redactor2.redact(textToRedact);
295
+ }
296
+ return textToRedact;
297
+ };
298
+ this.childRedactors = (0, composition_1.composeChildRedactors)(opts);
299
+ }
300
+ };
301
+ exports.SyncCompositeRedactor = SyncCompositeRedactor;
302
+ }
303
+ });
304
+ var require_dist = __commonJS({
305
+ "../redact-pii/dist/index.js"(exports) {
306
+ Object.defineProperty(exports, "__esModule", { value: true });
307
+ exports.SyncRedactor = exports.AsyncRedactor = void 0;
308
+ var AsyncCompositeRedactor_1 = require_AsyncCompositeRedactor();
309
+ Object.defineProperty(exports, "AsyncRedactor", { enumerable: true, get: function() {
310
+ return AsyncCompositeRedactor_1.AsyncCompositeRedactor;
311
+ } });
312
+ var SyncCompositeRedactor_1 = require_SyncCompositeRedactor();
313
+ Object.defineProperty(exports, "SyncRedactor", { enumerable: true, get: function() {
314
+ return SyncCompositeRedactor_1.SyncCompositeRedactor;
315
+ } });
316
+ }
317
+ });
64
318
  var STATE_KEY = /* @__PURE__ */ Symbol.for("raindrop.tracing.replayTraceRoutingState");
65
319
  function state() {
66
320
  const holder = globalThis;
67
321
  let current = holder[STATE_KEY];
68
322
  if (!current) {
69
- let storage = null;
323
+ let storage2 = null;
70
324
  try {
71
325
  const Ctor = globalThis.RAINDROP_ASYNC_LOCAL_STORAGE;
72
- if (Ctor) storage = new Ctor();
326
+ if (Ctor) storage2 = new Ctor();
73
327
  } catch (e) {
74
- storage = null;
328
+ storage2 = null;
75
329
  }
76
330
  current = {
77
- storage,
331
+ storage: storage2,
78
332
  syncDestination: void 0,
79
333
  spanDestinations: /* @__PURE__ */ new WeakMap()
80
334
  };
@@ -222,12 +476,12 @@ var NO_AMBIENT_ROUTING_BIND_WARNING = "[raindrop] This runtime's AsyncLocalStora
222
476
  var ROUTING_STORAGE_KEY = /* @__PURE__ */ Symbol.for("raindrop.tracing.routingContextStorage");
223
477
  function routingStorage() {
224
478
  const holder = globalThis;
225
- let storage = holder[ROUTING_STORAGE_KEY];
226
- if (!storage) {
227
- storage = new ContextStackStorage(NO_AMBIENT_ROUTING_BIND_WARNING);
228
- holder[ROUTING_STORAGE_KEY] = storage;
479
+ let storage2 = holder[ROUTING_STORAGE_KEY];
480
+ if (!storage2) {
481
+ storage2 = new ContextStackStorage(NO_AMBIENT_ROUTING_BIND_WARNING);
482
+ holder[ROUTING_STORAGE_KEY] = storage2;
229
483
  }
230
- return storage;
484
+ return storage2;
231
485
  }
232
486
  function currentRoutingBinding() {
233
487
  const bound = routingStorage().current();
@@ -513,6 +767,14 @@ function appGitPropertiesFromAttributes(attributes) {
513
767
  }
514
768
  return properties;
515
769
  }
770
+ function normalizeFeatureFlags(input) {
771
+ if (Array.isArray(input)) {
772
+ return Object.fromEntries(input.map((name) => [name, "true"]));
773
+ }
774
+ return Object.fromEntries(
775
+ Object.entries(input).map(([k, v]) => [k, String(v)])
776
+ );
777
+ }
516
778
  function getCrypto() {
517
779
  const c = globalThis.crypto;
518
780
  return c;
@@ -614,11 +876,91 @@ function runWithTracingSuppressed(fn) {
614
876
  return fn();
615
877
  }
616
878
  }
879
+ var MAX_QUOTED_ITEMS = 3;
880
+ function isRecord(value) {
881
+ return typeof value === "object" && value !== null && !Array.isArray(value);
882
+ }
883
+ function itemLabel(item) {
884
+ if (typeof item.event_id === "string" && item.event_id) return `event ${item.event_id}`;
885
+ if (typeof item.index === "number") return `item ${item.index}`;
886
+ return "item";
887
+ }
888
+ function describeRejected(item) {
889
+ const issues = Array.isArray(item.issues) ? item.issues : [];
890
+ const text = issues.filter(isRecord).map((issue) => {
891
+ const path = typeof issue.path === "string" && issue.path ? `${issue.path}: ` : "";
892
+ return `${path}${typeof issue.message === "string" ? issue.message : "invalid"}`;
893
+ }).join("; ");
894
+ return `${itemLabel(item)} (${text || "invalid"})`;
895
+ }
896
+ function describeWarning(item) {
897
+ if (typeof item.message === "string" && item.message) return `${itemLabel(item)}: ${item.message}`;
898
+ const fields = Array.isArray(item.fields) ? item.fields.filter((f) => typeof f === "string") : [];
899
+ return `${itemLabel(item)}: ignored ${fields.join(", ") || "input"}`;
900
+ }
901
+ function summarize(items, describe) {
902
+ const quoted = items.slice(0, MAX_QUOTED_ITEMS).map(describe).join(" | ");
903
+ const more = items.length > MAX_QUOTED_ITEMS ? ` (+${items.length - MAX_QUOTED_ITEMS} more)` : "";
904
+ return `${quoted}${more}`;
905
+ }
906
+ function reportIngestResponse(bodyText, prefix, opName) {
907
+ if (!bodyText) return;
908
+ let body;
909
+ try {
910
+ body = JSON.parse(bodyText);
911
+ } catch (e) {
912
+ return;
913
+ }
914
+ if (!isRecord(body)) return;
915
+ const rejected = Array.isArray(body.rejected) ? body.rejected.filter(isRecord) : [];
916
+ if (rejected.length > 0) {
917
+ rateLimitedLog(
918
+ `${prefix}.ingest_rejected`,
919
+ () => console.warn(
920
+ `${prefix} ${opName}: ingest rejected ${rejected.length} item(s), which were not stored: ${summarize(rejected, describeRejected)}`
921
+ )
922
+ );
923
+ }
924
+ const warnings = Array.isArray(body.warnings) ? body.warnings.filter(isRecord) : [];
925
+ if (warnings.length > 0) {
926
+ rateLimitedLog(
927
+ `${prefix}.ingest_warnings`,
928
+ () => console.warn(
929
+ `${prefix} ${opName}: ingest accepted ${warnings.length} item(s) with changes: ${summarize(warnings, describeWarning)}`
930
+ )
931
+ );
932
+ }
933
+ }
934
+ var DEFAULT_FLUSH_TIMEOUT_MS = 1e4;
617
935
  var DEFAULT_REQUEST_TIMEOUT_MS = 3e4;
618
936
  var MAX_RETRY_DELAY_MS = 3e4;
619
937
  function wait(ms) {
620
938
  return new Promise((resolve2) => setTimeout(resolve2, ms));
621
939
  }
940
+ function waitUnlessAborted(ms, signal) {
941
+ if (!signal) return wait(ms);
942
+ if (signal.aborted) return Promise.resolve();
943
+ return new Promise((resolve2) => {
944
+ const done = () => {
945
+ clearTimeout(timer);
946
+ signal.removeEventListener("abort", done);
947
+ resolve2();
948
+ };
949
+ const timer = setTimeout(done, ms);
950
+ signal.addEventListener("abort", done, { once: true });
951
+ });
952
+ }
953
+ function eitherSignal(a, b) {
954
+ if (!b) return a;
955
+ const any = AbortSignal.any;
956
+ if (typeof any === "function") return any([a, b]);
957
+ const controller = new AbortController();
958
+ const abort = () => controller.abort();
959
+ if (a.aborted || b.aborted) abort();
960
+ a.addEventListener("abort", abort, { once: true });
961
+ b.addEventListener("abort", abort, { once: true });
962
+ return controller.signal;
963
+ }
622
964
  function formatEndpoint(endpoint) {
623
965
  if (!endpoint) return void 0;
624
966
  return endpoint.endsWith("/") ? endpoint : `${endpoint}/`;
@@ -663,6 +1005,26 @@ async function raceWithTimeout(promise, timeoutMs) {
663
1005
  if (timer) clearTimeout(timer);
664
1006
  return settledInTime;
665
1007
  }
1008
+ var InFlightCutOff = class {
1009
+ constructor() {
1010
+ this.controller = new AbortController();
1011
+ }
1012
+ get signal() {
1013
+ return this.controller.signal;
1014
+ }
1015
+ cut() {
1016
+ this.controller.abort();
1017
+ this.controller = new AbortController();
1018
+ }
1019
+ };
1020
+ async function awaitEarlierSends(earlier, deadlineAt, cutOff, isRunning) {
1021
+ if (earlier.length === 0) return { settled: true, stillRunning: 0 };
1022
+ const settled = await raceWithTimeout(Promise.allSettled(earlier), deadlineAt - Date.now());
1023
+ if (settled) return { settled, stillRunning: 0 };
1024
+ const stillRunning = earlier.filter(isRunning).length;
1025
+ if (stillRunning > 0) cutOff.cut();
1026
+ return { settled: stillRunning === 0, stillRunning };
1027
+ }
666
1028
  function parseRetryAfter(headers) {
667
1029
  var _a;
668
1030
  const value = (_a = headers.get("Retry-After")) != null ? _a : headers.get("retry-after");
@@ -686,45 +1048,80 @@ function getRetryDelayMs(attemptNumber, previousError) {
686
1048
  const factor = Math.pow(2, attemptNumber - 2);
687
1049
  return Math.min(base * factor, MAX_RETRY_DELAY_MS);
688
1050
  }
689
- async function withRetry(operation, opName, opts) {
690
- const prefix = opts.sdkName ? `[raindrop-ai/${opts.sdkName}]` : "[raindrop-ai/core]";
691
- let lastError = void 0;
692
- for (let attemptNumber = 1; attemptNumber <= opts.maxAttempts; attemptNumber++) {
693
- if (attemptNumber > 1) {
694
- const delay = getRetryDelayMs(attemptNumber, lastError);
695
- if (opts.debug) {
696
- console.warn(
697
- `${prefix} ${opName} retry ${attemptNumber}/${opts.maxAttempts} in ${delay}ms`
698
- );
699
- }
700
- if (delay > 0) await wait(delay);
701
- } else if (opts.debug) {
702
- console.log(`${prefix} ${opName} attempt ${attemptNumber}/${opts.maxAttempts}`);
703
- }
704
- try {
705
- return await operation();
706
- } catch (err) {
707
- lastError = err;
708
- if (opts.debug) {
709
- const msg = err instanceof Error ? err.message : String(err);
710
- console.warn(
711
- `${prefix} ${opName} attempt ${attemptNumber} failed: ${msg}${attemptNumber === opts.maxAttempts ? " (no more retries)" : ""}`
712
- );
713
- }
714
- if (lastError && typeof lastError === "object" && "retryable" in lastError && !lastError.retryable)
715
- break;
716
- if (attemptNumber === opts.maxAttempts) break;
717
- }
1051
+ var sendPausedUntil = /* @__PURE__ */ new Map();
1052
+ function originOf(url) {
1053
+ try {
1054
+ return new URL(url).origin;
1055
+ } catch (e) {
1056
+ return url;
1057
+ }
1058
+ }
1059
+ function noteSendPause(url, status, retryAfterMs) {
1060
+ var _a;
1061
+ if (status !== 429 && status !== 503 || typeof retryAfterMs !== "number") return;
1062
+ const until = Date.now() + Math.min(Math.max(0, retryAfterMs), MAX_RETRY_DELAY_MS);
1063
+ const origin = originOf(url);
1064
+ sendPausedUntil.set(origin, Math.max((_a = sendPausedUntil.get(origin)) != null ? _a : 0, until));
1065
+ }
1066
+ function pausedUntil(url) {
1067
+ const until = sendPausedUntil.get(originOf(url));
1068
+ if (until === void 0) return void 0;
1069
+ if (until <= Date.now()) {
1070
+ sendPausedUntil.delete(originOf(url));
1071
+ return void 0;
718
1072
  }
719
- throw lastError instanceof Error ? lastError : new Error(String(lastError));
1073
+ return Math.min(until, Date.now() + MAX_RETRY_DELAY_MS);
1074
+ }
1075
+ function prefixFor(opts) {
1076
+ return opts.sdkName ? `[raindrop-ai/${opts.sdkName}]` : "[raindrop-ai/core]";
720
1077
  }
721
1078
  async function postJson(url, body, headers, opts) {
1079
+ var _a;
1080
+ const outcome = await sendJson(url, body, headers, { ...opts, deadlineAt: void 0 });
1081
+ if (outcome.status === "delivered") return;
1082
+ throw (_a = outcome.error) != null ? _a : new Error(`${redactUrlForLog(url)}: not sent (${outcome.status})`);
1083
+ }
1084
+ async function sendJson(url, body, headers, opts) {
722
1085
  var _a;
723
1086
  const opName = `POST ${redactUrlForLog(url)}`;
1087
+ const prefix = prefixFor(opts);
724
1088
  const timeoutMs = (_a = opts.timeoutMs) != null ? _a : DEFAULT_REQUEST_TIMEOUT_MS;
725
- await withRetry(
726
- async () => {
727
- const resp = await runWithTracingSuppressed(
1089
+ const deadlineAt = opts.deadlineAt;
1090
+ const remaining = () => deadlineAt === void 0 ? Infinity : deadlineAt - Date.now();
1091
+ let lastError;
1092
+ const cutOff = opts.cutOff;
1093
+ for (let attempt = 1; attempt <= opts.maxAttempts; attempt++) {
1094
+ if (cutOff == null ? void 0 : cutOff.aborted) return { status: "deferred", error: lastError };
1095
+ if (attempt > 1) {
1096
+ const delay = getRetryDelayMs(attempt, lastError);
1097
+ if (delay >= remaining()) {
1098
+ return { status: "deferred", retryAtMs: Date.now() + delay, error: lastError };
1099
+ }
1100
+ if (opts.debug) {
1101
+ console.warn(`${prefix} ${opName} retry ${attempt}/${opts.maxAttempts} in ${delay}ms`);
1102
+ }
1103
+ if (delay > 0) await waitUnlessAborted(delay, cutOff);
1104
+ if (cutOff == null ? void 0 : cutOff.aborted) return { status: "deferred", error: lastError };
1105
+ } else if (opts.debug) {
1106
+ console.log(`${prefix} ${opName} attempt ${attempt}/${opts.maxAttempts}`);
1107
+ }
1108
+ const pauseEnd = pausedUntil(url);
1109
+ if (pauseEnd !== void 0) {
1110
+ if (deadlineAt !== void 0 && pauseEnd >= deadlineAt) {
1111
+ return { status: "deferred", retryAtMs: pauseEnd, error: lastError };
1112
+ }
1113
+ await waitUnlessAborted(Math.max(0, pauseEnd - Date.now()), cutOff);
1114
+ if (cutOff == null ? void 0 : cutOff.aborted) return { status: "deferred", retryAtMs: pauseEnd, error: lastError };
1115
+ }
1116
+ const left = remaining();
1117
+ if (left <= 0) return { status: "deferred", error: lastError };
1118
+ const attemptTimeoutMs = Math.min(timeoutMs, left);
1119
+ const clampedByDeadline = attemptTimeoutMs < timeoutMs;
1120
+ const timeoutSignal = AbortSignal.timeout(attemptTimeoutMs);
1121
+ const signal = eitherSignal(timeoutSignal, cutOff);
1122
+ let resp;
1123
+ try {
1124
+ resp = await runWithTracingSuppressed(
728
1125
  () => fetch(url, {
729
1126
  method: "POST",
730
1127
  redirect: headers["X-Raindrop-Replay-Id"] !== void 0 ? "error" : "follow",
@@ -733,24 +1130,98 @@ async function postJson(url, body, headers, opts) {
733
1130
  ...headers
734
1131
  },
735
1132
  body: JSON.stringify(body),
736
- signal: AbortSignal.timeout(timeoutMs)
1133
+ signal
737
1134
  })
738
1135
  );
739
- if (!resp.ok) {
740
- const text = await resp.text().catch(() => "");
741
- const err = new Error(
742
- `HTTP ${resp.status} ${resp.statusText}${text ? `: ${text}` : ""}`
743
- );
744
- const retryAfterMs = parseRetryAfter(resp.headers);
745
- if (typeof retryAfterMs === "number") err.retryAfterMs = retryAfterMs;
746
- err.retryable = resp.status === 429 || resp.status >= 500;
747
- throw err;
1136
+ } catch (err) {
1137
+ const error = err instanceof Error ? err : new Error(String(err));
1138
+ if ((cutOff == null ? void 0 : cutOff.aborted) || timeoutSignal.aborted && clampedByDeadline) {
1139
+ return { status: "cut_off", error };
748
1140
  }
749
- },
750
- opName,
751
- opts
1141
+ lastError = error;
1142
+ logAttemptFailure(opts, prefix, opName, attempt, error);
1143
+ continue;
1144
+ }
1145
+ if (!resp.ok) {
1146
+ const text = await resp.text().catch(() => "");
1147
+ const error = new Error(
1148
+ `HTTP ${resp.status} ${resp.statusText}${text ? `: ${text}` : ""}`
1149
+ );
1150
+ const retryAfterMs = parseRetryAfter(resp.headers);
1151
+ if (typeof retryAfterMs === "number") error.retryAfterMs = retryAfterMs;
1152
+ noteSendPause(url, resp.status, retryAfterMs);
1153
+ error.retryable = resp.status === 408 || resp.status === 429 || resp.status >= 500;
1154
+ if (!error.retryable) return { status: "rejected", error };
1155
+ lastError = error;
1156
+ logAttemptFailure(opts, prefix, opName, attempt, error);
1157
+ continue;
1158
+ }
1159
+ try {
1160
+ reportIngestResponse(await resp.text(), prefix, opName);
1161
+ } catch (e) {
1162
+ }
1163
+ return { status: "delivered" };
1164
+ }
1165
+ return { status: "failed", error: lastError != null ? lastError : new Error(`${opName}: no attempts`) };
1166
+ }
1167
+ function logAttemptFailure(opts, prefix, opName, attempt, error) {
1168
+ if (!opts.debug) return;
1169
+ console.warn(
1170
+ `${prefix} ${opName} attempt ${attempt} failed: ${error.message}${attempt === opts.maxAttempts ? " (no more retries)" : ""}`
1171
+ );
1172
+ }
1173
+ function asCompat(result) {
1174
+ return result;
1175
+ }
1176
+ var DELIVERED = Object.freeze({
1177
+ delivered: true,
1178
+ timedOut: false,
1179
+ dropped: 0,
1180
+ queued: 0,
1181
+ inFlight: 0
1182
+ });
1183
+ function fateOf(outcome, shuttingDown) {
1184
+ switch (outcome.status) {
1185
+ case "delivered":
1186
+ case "rejected":
1187
+ return "delivered";
1188
+ case "deferred":
1189
+ return shuttingDown ? "dropped" : "queued";
1190
+ default:
1191
+ return "dropped";
1192
+ }
1193
+ }
1194
+ function summarize2(fates, inFlight, timedOut) {
1195
+ const dropped = fates.filter((fate) => fate === "dropped").length;
1196
+ const queued = fates.filter((fate) => fate === "queued").length;
1197
+ return {
1198
+ delivered: dropped === 0 && queued === 0 && inFlight === 0,
1199
+ timedOut: timedOut || queued > 0 || inFlight > 0,
1200
+ dropped,
1201
+ queued,
1202
+ inFlight
1203
+ };
1204
+ }
1205
+ function combineResults(...results) {
1206
+ return results.reduce(
1207
+ (acc, r) => ({
1208
+ delivered: acc.delivered && r.delivered,
1209
+ timedOut: acc.timedOut || r.timedOut,
1210
+ dropped: acc.dropped + r.dropped,
1211
+ queued: acc.queued + r.queued,
1212
+ inFlight: acc.inFlight + r.inFlight
1213
+ }),
1214
+ { ...DELIVERED }
752
1215
  );
753
1216
  }
1217
+ function describeUndelivered(what, result) {
1218
+ const parts = [
1219
+ result.dropped > 0 ? `${result.dropped} dropped` : "",
1220
+ result.queued > 0 ? `${result.queued} still queued` : "",
1221
+ result.inFlight > 0 ? `${result.inFlight} still sending` : ""
1222
+ ].filter(Boolean);
1223
+ return `${what} returned before everything was delivered: ${parts.join(", ")}`;
1224
+ }
754
1225
  var DEFAULT_MAX_TEXT_FIELD_CHARS = 1e6;
755
1226
  var TRUNCATION_MARKER = "...[truncated by raindrop]";
756
1227
  var BOUNDED_CLONE_MAX_DEPTH = 12;
@@ -1017,7 +1488,245 @@ function normalizeProjectId(raw, opts) {
1017
1488
  function projectIdHeaders(projectId) {
1018
1489
  return projectId ? { [PROJECT_ID_HEADER]: projectId } : {};
1019
1490
  }
1020
- var SHUTDOWN_DEADLINE_MS = 1e4;
1491
+ var import_redact_pii = __toESM(require_dist(), 1);
1492
+ var DEFAULT_PII_REDACTION_OPTIONS = {
1493
+ builtInRedactors: {
1494
+ credentials: { replaceWith: "<REDACTED CREDENTIALS>" },
1495
+ creditCardNumber: { replaceWith: "<REDACTED CREDIT CARD NUMBER>" },
1496
+ // Any run of 4+ digits: too broad.
1497
+ digits: { enabled: false },
1498
+ emailAddress: { replaceWith: "<REDACTED EMAIL>" },
1499
+ ipAddress: { enabled: false },
1500
+ names: { enabled: false },
1501
+ // Matches (pass(word|phrase)|secret): \S+
1502
+ password: { replaceWith: "<REDACTED SECRET>" },
1503
+ phoneNumber: { replaceWith: "<REDACTED PHONE NUMBER>" },
1504
+ streetAddress: { replaceWith: "<REDACTED ADDRESS>" },
1505
+ url: { enabled: false },
1506
+ username: { enabled: false },
1507
+ usSocialSecurityNumber: { replaceWith: "<REDACTED SOCIAL SECURITY NUMBER>" },
1508
+ zipcode: { enabled: false }
1509
+ }
1510
+ };
1511
+ var redactor;
1512
+ function redactPiiText(text) {
1513
+ redactor != null ? redactor : redactor = new import_redact_pii.SyncRedactor(DEFAULT_PII_REDACTION_OPTIONS);
1514
+ return redactor.redact(text);
1515
+ }
1516
+ var KINDS = ["event", "signal", "identify"];
1517
+ var DEFAULT_PII_FIELDS = {
1518
+ event: ["ai_data.input", "ai_data.output"],
1519
+ signal: [],
1520
+ identify: []
1521
+ };
1522
+ var PROTECTED_LOCATIONS = new Set(
1523
+ [
1524
+ ["event_id"],
1525
+ ["user_id"],
1526
+ ["ai_data", "convo_id"],
1527
+ ["event"],
1528
+ ["timestamp"],
1529
+ ["is_pending"],
1530
+ ["signal_name"],
1531
+ ["signal_type"]
1532
+ ].map((keys) => JSON.stringify(keys))
1533
+ );
1534
+ var SIGNAL_PATH_ALIASES = {
1535
+ comment: "properties.comment",
1536
+ after: "properties.after"
1537
+ };
1538
+ function parsePiiFieldPath(path) {
1539
+ if (typeof path !== "string") return null;
1540
+ const segments = [];
1541
+ let i = 0;
1542
+ const n = path.length;
1543
+ let expectSeparator = false;
1544
+ while (i < n) {
1545
+ const ch = path[i];
1546
+ if (ch === "[") {
1547
+ const close = path.indexOf("]", i);
1548
+ if (path[i + 1] === '"') {
1549
+ let j2 = i + 2;
1550
+ let key = "";
1551
+ while (j2 < n && path[j2] !== '"') {
1552
+ if (path[j2] === "\\" && j2 + 1 < n) {
1553
+ key += path[j2 + 1];
1554
+ j2 += 2;
1555
+ } else {
1556
+ key += path[j2];
1557
+ j2 += 1;
1558
+ }
1559
+ }
1560
+ if (j2 >= n || path[j2 + 1] !== "]" || key === "") return null;
1561
+ segments.push({ kind: "key", key });
1562
+ i = j2 + 2;
1563
+ } else {
1564
+ if (close === -1) return null;
1565
+ const inner = path.slice(i + 1, close);
1566
+ if (inner === "") segments.push({ kind: "every" });
1567
+ else if (/^(0|[1-9]\d*)$/.test(inner))
1568
+ segments.push({ kind: "index", index: Number(inner) });
1569
+ else return null;
1570
+ i = close + 1;
1571
+ }
1572
+ expectSeparator = true;
1573
+ continue;
1574
+ }
1575
+ if (ch === ".") {
1576
+ if (!expectSeparator) return null;
1577
+ i += 1;
1578
+ expectSeparator = false;
1579
+ if (i >= n || path[i] === ".") return null;
1580
+ continue;
1581
+ }
1582
+ if (expectSeparator) return null;
1583
+ let j = i;
1584
+ while (j < n && path[j] !== "." && path[j] !== "[") j += 1;
1585
+ const token = path.slice(i, j);
1586
+ if (token === "*") segments.push({ kind: "any_key" });
1587
+ else if (token === "" || /[\]*"\s]/.test(token)) return null;
1588
+ else segments.push({ kind: "key", key: token });
1589
+ i = j;
1590
+ expectSeparator = true;
1591
+ }
1592
+ return segments.length > 0 && expectSeparator ? segments : null;
1593
+ }
1594
+ function mergePiiFieldMaps(...maps) {
1595
+ const out = { event: [], signal: [], identify: [] };
1596
+ for (const kind of KINDS) {
1597
+ const seen = /* @__PURE__ */ new Set();
1598
+ for (const map of maps) {
1599
+ const paths = map == null ? void 0 : map[kind];
1600
+ if (!Array.isArray(paths)) continue;
1601
+ for (const path of paths) {
1602
+ if (typeof path !== "string" || seen.has(path)) continue;
1603
+ seen.add(path);
1604
+ out[kind].push(path);
1605
+ }
1606
+ }
1607
+ }
1608
+ return out;
1609
+ }
1610
+ function isPlainObject(value) {
1611
+ if (value === null || typeof value !== "object" || Array.isArray(value)) return false;
1612
+ const proto = Object.getPrototypeOf(value);
1613
+ return proto === Object.prototype || proto === null;
1614
+ }
1615
+ function isProtected(location) {
1616
+ return PROTECTED_LOCATIONS.has(JSON.stringify(location));
1617
+ }
1618
+ function redactAll(value, location) {
1619
+ if (location !== null && isProtected(location)) return value;
1620
+ if (typeof value === "string") return redactPiiText(value);
1621
+ if (Array.isArray(value)) return value.map((item) => redactAll(item, null));
1622
+ if (isPlainObject(value)) {
1623
+ const out = {};
1624
+ for (const [key, child] of Object.entries(value)) {
1625
+ out[key] = redactAll(child, location === null ? null : [...location, key]);
1626
+ }
1627
+ return out;
1628
+ }
1629
+ return value;
1630
+ }
1631
+ function applyPath(value, segments, location) {
1632
+ if (segments.length === 0) return redactAll(value, location);
1633
+ const [head, ...rest] = segments;
1634
+ if (head.kind === "key" || head.kind === "any_key") {
1635
+ if (!isPlainObject(value)) return value;
1636
+ const keys = head.kind === "key" ? [head.key] : Object.keys(value);
1637
+ let out2;
1638
+ for (const key of keys) {
1639
+ if (!Object.prototype.hasOwnProperty.call(value, key)) continue;
1640
+ const child = value[key];
1641
+ const next = applyPath(child, rest, location === null ? null : [...location, key]);
1642
+ if (next !== child) {
1643
+ out2 != null ? out2 : out2 = { ...value };
1644
+ out2[key] = next;
1645
+ }
1646
+ }
1647
+ return out2 != null ? out2 : value;
1648
+ }
1649
+ if (!Array.isArray(value)) return value;
1650
+ const indices = head.kind === "index" ? head.index < value.length ? [head.index] : [] : value.keys();
1651
+ let out;
1652
+ for (const index of indices) {
1653
+ const child = value[index];
1654
+ const next = applyPath(child, rest, null);
1655
+ if (next !== child) {
1656
+ out != null ? out : out = [...value];
1657
+ out[index] = next;
1658
+ }
1659
+ }
1660
+ return out != null ? out : value;
1661
+ }
1662
+ var PiiFieldRedactor = class {
1663
+ constructor(options = {}) {
1664
+ var _a;
1665
+ this.startupMap = options.startupFieldMap;
1666
+ this.prefix = (_a = options.logPrefix) != null ? _a : "[raindrop-ai/core]";
1667
+ }
1668
+ /** Replace the server-provided layer (CONTRACT 11.2). Additive: it can only add fields. */
1669
+ setServerFieldMap(map) {
1670
+ this.serverMap = map;
1671
+ this.compiled = void 0;
1672
+ }
1673
+ /** The paths in effect for a kind, defaults first. */
1674
+ effectivePaths(kind) {
1675
+ return mergePiiFieldMaps(DEFAULT_PII_FIELDS, this.startupMap, this.serverMap)[kind];
1676
+ }
1677
+ compile() {
1678
+ if (this.compiled) return this.compiled;
1679
+ const out = { event: [], signal: [], identify: [] };
1680
+ for (const kind of KINDS) {
1681
+ for (const path of this.effectivePaths(kind)) {
1682
+ const variants = [path];
1683
+ if (kind === "signal" && SIGNAL_PATH_ALIASES[path])
1684
+ variants.push(SIGNAL_PATH_ALIASES[path]);
1685
+ for (const variant of variants) {
1686
+ const segments = parsePiiFieldPath(variant);
1687
+ if (segments) {
1688
+ out[kind].push(segments);
1689
+ } else {
1690
+ rateLimitedLog(
1691
+ `${this.prefix}.pii_field_path_invalid`,
1692
+ () => console.warn(
1693
+ `${this.prefix} redactPiiFields: ignoring invalid ${kind} path ${JSON.stringify(variant)}`
1694
+ )
1695
+ );
1696
+ }
1697
+ }
1698
+ }
1699
+ }
1700
+ this.compiled = out;
1701
+ return out;
1702
+ }
1703
+ /** The payload with every selected field redacted (a copy where changed). */
1704
+ redact(kind, payload) {
1705
+ let out = payload;
1706
+ for (const segments of this.compile()[kind]) out = applyPath(out, segments, []);
1707
+ return out;
1708
+ }
1709
+ };
1710
+ function detectRuntime() {
1711
+ if (typeof process === "object" && process && typeof process.env === "object" && process.env && typeof process.version === "string") {
1712
+ return "node";
1713
+ }
1714
+ if (typeof window === "object") return "browser";
1715
+ if (typeof WebSocketPair !== "undefined") return "cloudflare-worker";
1716
+ if (typeof EdgeRuntime === "string") return "vercel-edge";
1717
+ if (typeof WorkerGlobalScope !== "undefined" && typeof importScripts === "function") {
1718
+ return "web-worker";
1719
+ }
1720
+ return "unknown";
1721
+ }
1722
+ var PLACEHOLDER_USER_IDS = /* @__PURE__ */ new Set(["null", "undefined"]);
1723
+ function isMissingUserId(value) {
1724
+ if (value === void 0 || value === null) return true;
1725
+ if (typeof value !== "string") return false;
1726
+ const trimmed = value.trim();
1727
+ return trimmed === "" || PLACEHOLDER_USER_IDS.has(trimmed);
1728
+ }
1729
+ var MAX_DEFERRED_SENDS = 1e3;
1021
1730
  var POST_SHUTDOWN_TIMEOUT_MS = 5e3;
1022
1731
  function appGitOwnerId(prefix) {
1023
1732
  try {
@@ -1026,18 +1735,52 @@ function appGitOwnerId(prefix) {
1026
1735
  return `${prefix}:${Date.now().toString(36)}:${Math.random().toString(36).slice(2)}`;
1027
1736
  }
1028
1737
  }
1738
+ function earliestIsoTimestamp(...values) {
1739
+ let earliest;
1740
+ for (const value of values) {
1741
+ if (value === void 0) continue;
1742
+ const date = new Date(value);
1743
+ if (Number.isNaN(date.getTime())) continue;
1744
+ if (earliest === void 0 || date < earliest) earliest = date;
1745
+ }
1746
+ return earliest == null ? void 0 : earliest.toISOString();
1747
+ }
1748
+ function isPlainObject2(value) {
1749
+ if (value === null || typeof value !== "object" || Array.isArray(value)) return false;
1750
+ const proto = Object.getPrototypeOf(value);
1751
+ return proto === Object.prototype || proto === null;
1752
+ }
1753
+ var UNSAFE_MERGE_KEYS = /* @__PURE__ */ new Set(["__proto__", "constructor", "prototype"]);
1754
+ function deepMergeProperties(target, source) {
1755
+ const out = { ...target };
1756
+ for (const [key, value] of Object.entries(source)) {
1757
+ if (value === void 0 || UNSAFE_MERGE_KEYS.has(key)) continue;
1758
+ const existing = out[key];
1759
+ out[key] = isPlainObject2(existing) && isPlainObject2(value) ? deepMergeProperties(existing, value) : value;
1760
+ }
1761
+ return out;
1762
+ }
1029
1763
  function mergePatches(target, source) {
1030
- var _a, _b, _c, _d, _e, _f, _g, _h, _i;
1031
- const out = { ...target, ...source };
1764
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k;
1765
+ const defined2 = Object.fromEntries(
1766
+ Object.entries(source).filter(([, value]) => value !== void 0)
1767
+ );
1768
+ const out = { ...target, ...defined2 };
1032
1769
  if (target.properties || source.properties) {
1033
- out.properties = { ...(_a = target.properties) != null ? _a : {}, ...(_b = source.properties) != null ? _b : {} };
1770
+ out.properties = deepMergeProperties((_a = target.properties) != null ? _a : {}, (_b = source.properties) != null ? _b : {});
1771
+ }
1772
+ if (target.callerProperties || source.callerProperties) {
1773
+ out.callerProperties = deepMergeProperties(
1774
+ (_c = target.callerProperties) != null ? _c : {},
1775
+ (_d = source.callerProperties) != null ? _d : {}
1776
+ );
1034
1777
  }
1035
1778
  if (target.usage || source.usage) {
1036
- out.usage = { ...(_c = target.usage) != null ? _c : {} };
1037
- if (((_d = source.usage) == null ? void 0 : _d.promptTokens) !== void 0) {
1779
+ out.usage = { ...(_e = target.usage) != null ? _e : {} };
1780
+ if (((_f = source.usage) == null ? void 0 : _f.promptTokens) !== void 0) {
1038
1781
  out.usage.promptTokens = source.usage.promptTokens;
1039
1782
  }
1040
- if (((_e = source.usage) == null ? void 0 : _e.completionTokens) !== void 0) {
1783
+ if (((_g = source.usage) == null ? void 0 : _g.completionTokens) !== void 0) {
1041
1784
  out.usage.completionTokens = source.usage.completionTokens;
1042
1785
  }
1043
1786
  }
@@ -1045,10 +1788,10 @@ function mergePatches(target, source) {
1045
1788
  out.error = target.error;
1046
1789
  }
1047
1790
  if (target.featureFlags || source.featureFlags) {
1048
- out.featureFlags = { ...(_f = target.featureFlags) != null ? _f : {}, ...(_g = source.featureFlags) != null ? _g : {} };
1791
+ out.featureFlags = { ...(_h = target.featureFlags) != null ? _h : {}, ...(_i = source.featureFlags) != null ? _i : {} };
1049
1792
  }
1050
1793
  if (target.attachments || source.attachments) {
1051
- out.attachments = [...(_h = target.attachments) != null ? _h : [], ...(_i = source.attachments) != null ? _i : []];
1794
+ out.attachments = [...(_j = target.attachments) != null ? _j : [], ...(_k = source.attachments) != null ? _k : []];
1052
1795
  }
1053
1796
  return out;
1054
1797
  }
@@ -1073,6 +1816,15 @@ function eventTelemetryProperties(patch) {
1073
1816
  }
1074
1817
  return properties;
1075
1818
  }
1819
+ var FINISHED_EVENT_MEMORY = 1e4;
1820
+ var MANUAL_UPDATES = /* @__PURE__ */ new WeakSet();
1821
+ function markManualUpdate(patch) {
1822
+ try {
1823
+ MANUAL_UPDATES.add(patch);
1824
+ } catch (e) {
1825
+ }
1826
+ return patch;
1827
+ }
1076
1828
  var EventShipper = class {
1077
1829
  constructor(opts) {
1078
1830
  this.appGitOwner = appGitOwnerId("event");
@@ -1080,6 +1832,12 @@ var EventShipper = class {
1080
1832
  this.sticky = /* @__PURE__ */ new Map();
1081
1833
  this.timers = /* @__PURE__ */ new Map();
1082
1834
  this.inFlight = /* @__PURE__ */ new Set();
1835
+ this.background = /* @__PURE__ */ new Set();
1836
+ this.backgroundDrops = 0;
1837
+ this.cutOff = new InFlightCutOff();
1838
+ this.finishedEventIds = /* @__PURE__ */ new Set();
1839
+ this.sendChains = /* @__PURE__ */ new Map();
1840
+ this.deferredSends = /* @__PURE__ */ new Set();
1083
1841
  this.hasShutdown = false;
1084
1842
  var _a, _b, _c, _d, _e, _f, _g, _h, _i;
1085
1843
  this.appGitContext = (_a = opts.appGitContext) != null ? _a : createAppGitContext(opts.appGit);
@@ -1093,6 +1851,7 @@ var EventShipper = class {
1093
1851
  this.prefix = `[raindrop-ai/${this.sdkName}]`;
1094
1852
  this.defaultEventName = (_f = opts.defaultEventName) != null ? _f : "ai_generation";
1095
1853
  this.maxTextFieldCharsOpt = opts.maxTextFieldChars;
1854
+ this.flushTimeoutMs = resolveFlushTimeoutMs(opts.flushTimeoutMs);
1096
1855
  this.localDebuggerUrl = (_g = resolveLocalDebuggerBaseUrl(opts.localDebuggerUrl)) != null ? _g : void 0;
1097
1856
  if (this.debug && this.localDebuggerUrl) {
1098
1857
  console.log(`${this.prefix} Local debugger mirroring: ${this.localDebuggerUrl}`);
@@ -1101,15 +1860,29 @@ var EventShipper = class {
1101
1860
  debug: this.debug,
1102
1861
  prefix: this.prefix
1103
1862
  });
1104
- const isNode = typeof process !== "undefined" && typeof process.version === "string";
1863
+ this.redactPii = opts.redactPii === true;
1864
+ this.piiFields = new PiiFieldRedactor({
1865
+ startupFieldMap: opts.redactPiiFields,
1866
+ logPrefix: this.prefix
1867
+ });
1868
+ if (opts.redactPiiFields && !this.redactPii) {
1869
+ rateLimitedLog(
1870
+ `${this.prefix}.pii_fields_without_redaction`,
1871
+ () => console.warn(
1872
+ `${this.prefix} redactPiiFields is set but redactPii is off; nothing is redacted. Set redactPii: true to use it.`
1873
+ )
1874
+ );
1875
+ }
1876
+ this.wizardSession = opts.wizardSession || void 0;
1877
+ const runtime = detectRuntime();
1105
1878
  this.context = {
1106
1879
  library: {
1107
1880
  name: (_h = opts.libraryName) != null ? _h : "@raindrop-ai/core",
1108
1881
  version: (_i = opts.libraryVersion) != null ? _i : "0.0.0"
1109
1882
  },
1110
1883
  metadata: {
1111
- jsRuntime: isNode ? "node" : "web",
1112
- ...isNode ? { nodeVersion: process.version } : {}
1884
+ jsRuntime: runtime,
1885
+ ...runtime === "node" ? { nodeVersion: process.version } : {}
1113
1886
  }
1114
1887
  };
1115
1888
  }
@@ -1126,28 +1899,21 @@ var EventShipper = class {
1126
1899
  return { ...this.authHeaders(), ...projectIdHeaders(this.projectId) };
1127
1900
  }
1128
1901
  /**
1129
- * Build the retry/timeout options for one POST, honoring the shutdown
1130
- * deadline. Returns `null` when the shutdown drain window is exhausted —
1131
- * the caller must drop the payload (with a rate-limited warning) instead
1132
- * of issuing a request that could outlive process exit.
1133
- *
1134
- * Checked fresh on EVERY send, so a shutdown that begins while the flush
1135
- * path is mid-drain takes effect immediately: no further retries, and the
1136
- * per-attempt timeout is clamped to the remaining window. After
1137
- * `shutdown()` returns (deadline cleared, `hasShutdown` still set),
1138
- * sends — late callers, or flush work the deadline abandoned mid-drain —
1139
- * run as a single short attempt rather than regaining the full retry
1140
- * schedule.
1902
+ * Retry/timeout options for one POST. While `shutdown()` drains, sends retry
1903
+ * within its window (deadline = the shutdown deadline); a flush passes its
1904
+ * own deadline. `null` when the shutdown window is spent: the caller drops
1905
+ * the payload with a warning. After `shutdown()` returns, late sends get a
1906
+ * single short attempt rather than the full retry schedule.
1141
1907
  */
1142
- requestOpts() {
1908
+ requestOpts(deadlineAt) {
1143
1909
  if (this.shutdownDeadlineAt !== void 0) {
1144
- const remainingMs = this.shutdownDeadlineAt - Date.now();
1145
- if (remainingMs <= 0) return null;
1910
+ if (this.shutdownDeadlineAt - Date.now() <= 0) return null;
1146
1911
  return {
1147
- maxAttempts: 1,
1912
+ maxAttempts: 3,
1148
1913
  debug: this.debug,
1149
1914
  sdkName: this.sdkName,
1150
- timeoutMs: Math.min(DEFAULT_REQUEST_TIMEOUT_MS, remainingMs)
1915
+ deadlineAt: Math.min(this.shutdownDeadlineAt, deadlineAt != null ? deadlineAt : Infinity),
1916
+ cutOff: this.cutOff.signal
1151
1917
  };
1152
1918
  }
1153
1919
  if (this.hasShutdown) {
@@ -1155,15 +1921,94 @@ var EventShipper = class {
1155
1921
  maxAttempts: 1,
1156
1922
  debug: this.debug,
1157
1923
  sdkName: this.sdkName,
1158
- timeoutMs: POST_SHUTDOWN_TIMEOUT_MS
1924
+ timeoutMs: POST_SHUTDOWN_TIMEOUT_MS,
1925
+ cutOff: this.cutOff.signal
1159
1926
  };
1160
1927
  }
1161
- return { maxAttempts: 3, debug: this.debug, sdkName: this.sdkName };
1928
+ return {
1929
+ maxAttempts: 3,
1930
+ debug: this.debug,
1931
+ sdkName: this.sdkName,
1932
+ deadlineAt,
1933
+ cutOff: this.cutOff.signal
1934
+ };
1935
+ }
1936
+ shuttingDown() {
1937
+ return this.shutdownDeadlineAt !== void 0 || this.hasShutdown;
1162
1938
  }
1163
1939
  async patch(eventId, patch) {
1164
- var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k;
1165
- if (!this.enabled) return;
1166
- if (!eventId || !eventId.trim()) return;
1940
+ await this.patchWithFate(eventId, patch);
1941
+ }
1942
+ /**
1943
+ * Queue an update without waiting for any network send: for integrations,
1944
+ * whose captured call must return as soon as the update is queued (rule C9,
1945
+ * hot path). A finishing update starts its send in the background; `flush()`
1946
+ * and `shutdown()` wait for it within their deadline and count it in their
1947
+ * result. Never throws or rejects.
1948
+ */
1949
+ enqueue(eventId, patch) {
1950
+ let op;
1951
+ try {
1952
+ op = this.patchWithFate(eventId, patch).catch((err) => {
1953
+ this.warnBackgroundFailure(eventId, err);
1954
+ return "dropped";
1955
+ }).then((fate) => {
1956
+ if (fate === "dropped") this.backgroundDrops += 1;
1957
+ return fate;
1958
+ });
1959
+ } catch (err) {
1960
+ this.warnBackgroundFailure(eventId, err);
1961
+ return;
1962
+ }
1963
+ this.background.add(op);
1964
+ void op.finally(() => this.background.delete(op));
1965
+ }
1966
+ /** `finish()` without waiting for the send; see `enqueue`. */
1967
+ enqueueFinish(eventId, patch = {}) {
1968
+ this.enqueue(eventId, { ...patch, isPending: false });
1969
+ }
1970
+ /**
1971
+ * Caller properties win over everything the integration derived (W6). A
1972
+ * clash is intended by the caller, so it is only logged in debug mode.
1973
+ */
1974
+ withCallerProperties(eventId, derived, caller) {
1975
+ if (!caller || Object.keys(caller).length === 0) return derived;
1976
+ if (this.debug) {
1977
+ try {
1978
+ const keys = overriddenKeys(derived, caller);
1979
+ if (keys.length > 0) {
1980
+ console.log(
1981
+ `${this.prefix} event ${eventId}: caller properties override the integration's value for ${keys.map((k) => `"${k}"`).join(", ")}`
1982
+ );
1983
+ }
1984
+ } catch (e) {
1985
+ }
1986
+ }
1987
+ return deepMergeProperties(derived, caller);
1988
+ }
1989
+ warnBackgroundFailure(eventId, err) {
1990
+ rateLimitedLog(
1991
+ `${this.prefix}.background_update`,
1992
+ () => console.warn(`${this.prefix} update for event ${eventId} failed:`, err)
1993
+ );
1994
+ }
1995
+ /**
1996
+ * Id of the last event an integration finished in this process (manual
1997
+ * events API finishes excluded): what integrations expose as `lastEventId`
1998
+ * (wrapper profile W10).
1999
+ */
2000
+ get lastFinishedEventId() {
2001
+ return this.lastFinished;
2002
+ }
2003
+ async patchWithFate(eventId, patch) {
2004
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m;
2005
+ const manual = MANUAL_UPDATES.has(patch);
2006
+ if (!this.enabled) return void 0;
2007
+ if (!eventId || !eventId.trim()) return void 0;
2008
+ if (patch.userId !== void 0 && isMissingUserId(patch.userId)) {
2009
+ const { userId: _placeholder, ...rest } = patch;
2010
+ patch = rest;
2011
+ }
1167
2012
  const capturedSticky = this.sticky.get(eventId);
1168
2013
  const eventAlreadyKnown = capturedSticky !== void 0 || this.buffers.has(eventId);
1169
2014
  const destination = currentReplayTraceDestination();
@@ -1171,7 +2016,7 @@ var EventShipper = class {
1171
2016
  if ((capturedSticky == null ? void 0 : capturedSticky.replayOnly) === true || !eventAlreadyKnown && cloudReplay) {
1172
2017
  if (patch.isPending === false) this.sticky.delete(eventId);
1173
2018
  else this.sticky.set(eventId, { ...capturedSticky, replayOnly: true });
1174
- return;
2019
+ return void 0;
1175
2020
  }
1176
2021
  const maxChars = resolveMaxTextFieldChars(this.maxTextFieldCharsOpt);
1177
2022
  if (typeof patch.input === "string" && patch.input.length > maxChars) {
@@ -1194,11 +2039,12 @@ var EventShipper = class {
1194
2039
  }
1195
2040
  const sticky = (_c = this.sticky.get(eventId)) != null ? _c : {};
1196
2041
  const existing = (_d = this.buffers.get(eventId)) != null ? _d : {};
2042
+ const explicitProperties = patch.callerProperties !== void 0 ? { ...patch.properties, ...patch.callerProperties } : patch.properties;
1197
2043
  patch = {
1198
2044
  ...patch,
1199
2045
  properties: withAppGitProperties(
1200
2046
  patch.properties,
1201
- this.appGitContext.forOperation(eventId, patch.properties, this.appGitOwner)
2047
+ this.appGitContext.forOperation(eventId, explicitProperties, this.appGitOwner)
1202
2048
  )
1203
2049
  };
1204
2050
  const merged = mergePatches(existing, patch);
@@ -1208,47 +2054,165 @@ var EventShipper = class {
1208
2054
  userId: (_h = merged.userId) != null ? _h : sticky.userId,
1209
2055
  convoId: (_i = merged.convoId) != null ? _i : sticky.convoId,
1210
2056
  eventName: (_j = merged.eventName) != null ? _j : sticky.eventName,
1211
- isPending: (_k = merged.isPending) != null ? _k : sticky.isPending
2057
+ isPending: (_k = merged.isPending) != null ? _k : sticky.isPending,
2058
+ startedAt: (_l = earliestIsoTimestamp(sticky.startedAt, patch.timestamp)) != null ? _l : (/* @__PURE__ */ new Date()).toISOString(),
2059
+ resumed: sticky.resumed,
2060
+ hasContent: sticky.hasContent === true || patchHasContent(patch),
2061
+ callerProperties: patch.callerProperties !== void 0 ? deepMergeProperties((_m = sticky.callerProperties) != null ? _m : {}, patch.callerProperties) : sticky.callerProperties
1212
2062
  });
1213
2063
  const t = this.timers.get(eventId);
1214
2064
  if (t) clearTimeout(t);
1215
2065
  if (merged.isPending === false) {
1216
- await this.flushOne(eventId);
2066
+ if (!manual) this.lastFinished = eventId;
2067
+ this.noteFinish(eventId);
2068
+ this.noteEmptyFinish(eventId);
2069
+ const fate = await this.flushOne(eventId);
1217
2070
  if (this.appGitContext.releaseOperation) this.appGitContext.releaseOperation(eventId, this.appGitOwner);
1218
2071
  else this.appGitContext.finishOperation(eventId);
1219
- return;
2072
+ return fate;
1220
2073
  }
1221
2074
  const timeout = setTimeout(() => {
1222
2075
  void this.flushOne(eventId).catch(() => {
1223
2076
  });
1224
2077
  }, this.partialFlushMs);
1225
2078
  this.timers.set(eventId, timeout);
2079
+ return void 0;
2080
+ }
2081
+ /**
2082
+ * A second finish for an event is still sent (ingest finalizes once and
2083
+ * treats it as an update, CONTRACT §10.2), but it usually means several
2084
+ * calls share one event id by mistake, so it is warned about.
2085
+ */
2086
+ noteFinish(eventId) {
2087
+ if (this.finishedEventIds.has(eventId)) {
2088
+ rateLimitedLog(
2089
+ `${this.prefix}.repeated_finish`,
2090
+ () => console.warn(
2091
+ `${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.`
2092
+ )
2093
+ );
2094
+ return;
2095
+ }
2096
+ this.finishedEventIds.add(eventId);
2097
+ if (this.finishedEventIds.size > FINISHED_EVENT_MEMORY) {
2098
+ const oldest = this.finishedEventIds.values().next().value;
2099
+ if (oldest !== void 0) this.finishedEventIds.delete(oldest);
2100
+ }
2101
+ }
2102
+ /**
2103
+ * An event finished with no input, output or attachments is almost always a
2104
+ * caller bug (CONTRACT §14.2, rule C5); it is sent anyway and warned about.
2105
+ * Not for a resumed event: its content may have been sent by another process.
2106
+ */
2107
+ noteEmptyFinish(eventId) {
2108
+ const sticky = this.sticky.get(eventId);
2109
+ if ((sticky == null ? void 0 : sticky.resumed) === true || (sticky == null ? void 0 : sticky.hasContent) === true) return;
2110
+ rateLimitedLog(
2111
+ `${this.prefix}.empty_finish`,
2112
+ () => console.warn(
2113
+ `${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.`
2114
+ )
2115
+ );
2116
+ }
2117
+ /**
2118
+ * Continue an event that was begun elsewhere (another process, a queue
2119
+ * worker, a later request) using only its id. No request is sent; later
2120
+ * `patch` / `finish` calls for this id are sent even without a `userId`,
2121
+ * and ingest keeps the user, conversation and start time it already has.
2122
+ * Pass `userId` / `convoId` / `eventName` if you have them.
2123
+ */
2124
+ resume(eventId, options = {}) {
2125
+ var _a, _b, _c;
2126
+ if (!this.enabled) return;
2127
+ if (!eventId || !eventId.trim()) return;
2128
+ const sticky = (_a = this.sticky.get(eventId)) != null ? _a : {};
2129
+ this.sticky.set(eventId, {
2130
+ ...sticky,
2131
+ userId: isMissingUserId(options.userId) ? sticky.userId : options.userId,
2132
+ convoId: (_b = options.convoId) != null ? _b : sticky.convoId,
2133
+ eventName: (_c = options.eventName) != null ? _c : sticky.eventName,
2134
+ resumed: true
2135
+ });
1226
2136
  }
1227
2137
  async finish(eventId, patch) {
1228
- await this.patch(eventId, { ...patch, isPending: false });
2138
+ const update = { ...patch, isPending: false };
2139
+ if (MANUAL_UPDATES.has(patch)) MANUAL_UPDATES.add(update);
2140
+ await this.patch(eventId, update);
1229
2141
  }
2142
+ /**
2143
+ * Send everything buffered, open (pending) events included, within
2144
+ * `flushTimeoutMs` (CONTRACT 1.2.1). Resolves, never rejects, to whether it
2145
+ * all reached ingest; when it didn't, it also warns. At the deadline,
2146
+ * payloads not yet sent stay queued for a later send and a request cut off
2147
+ * mid-flight is dropped. Sends already running when it was called are
2148
+ * waited for up to the deadline and then cut off.
2149
+ */
1230
2150
  async flush() {
1231
- if (!this.enabled) return;
2151
+ const result = await this.drainAll(Date.now() + this.flushTimeoutMs);
2152
+ if (!result.delivered && this.shutdownDeadlineAt === void 0) {
2153
+ rateLimitedLog(
2154
+ `${this.prefix}.flush_undelivered`,
2155
+ () => console.warn(`${this.prefix} ${describeUndelivered("flush()", result)}`)
2156
+ );
2157
+ }
2158
+ return asCompat(result);
2159
+ }
2160
+ async drainAll(deadlineAt) {
2161
+ if (!this.enabled) return { ...DELIVERED };
2162
+ const deferred = [...this.deferredSends];
2163
+ this.deferredSends.clear();
2164
+ for (const entry of deferred) if (entry.timer) clearTimeout(entry.timer);
1232
2165
  const ids = [...this.buffers.keys()];
1233
- await Promise.all(ids.map((id) => this.flushOne(id)));
1234
- await Promise.all([...this.inFlight].map((p) => p.catch(() => {
1235
- })));
2166
+ const fates = await Promise.all([
2167
+ ...ids.map((id) => this.flushOne(id, deadlineAt)),
2168
+ ...deferred.map((entry) => entry.send(deadlineAt))
2169
+ ]);
2170
+ const background = [...this.background];
2171
+ if (background.length > 0) {
2172
+ await raceWithTimeout(Promise.all(background), deadlineAt - Date.now());
2173
+ }
2174
+ for (; this.backgroundDrops > 0; this.backgroundDrops -= 1) fates.push("dropped");
2175
+ const running = [...this.inFlight];
2176
+ const { settled, stillRunning } = await awaitEarlierSends(
2177
+ running,
2178
+ deadlineAt,
2179
+ this.cutOff,
2180
+ (p) => this.inFlight.has(p)
2181
+ );
2182
+ const unresolved = background.filter((op) => this.background.has(op)).length;
2183
+ return summarize2(
2184
+ fates.filter((fate) => fate !== void 0),
2185
+ Math.max(stillRunning, unresolved),
2186
+ !settled || unresolved > 0
2187
+ );
1236
2188
  }
2189
+ /**
2190
+ * Flush within `flushTimeoutMs` and stop. Sends retry while the window
2191
+ * allows; whatever is left at the deadline is dropped with a warning, so a
2192
+ * dead network can never wedge process exit. Resolves, never rejects.
2193
+ */
1237
2194
  async shutdown() {
1238
2195
  this.hasShutdown = true;
1239
- this.shutdownDeadlineAt = Date.now() + SHUTDOWN_DEADLINE_MS;
2196
+ const deadlineAt = Date.now() + this.flushTimeoutMs;
2197
+ this.shutdownDeadlineAt = deadlineAt;
2198
+ let result = { ...DELIVERED };
1240
2199
  try {
1241
2200
  for (const t of this.timers.values()) clearTimeout(t);
1242
2201
  this.timers.clear();
1243
2202
  for (const [eventId, sticky] of this.sticky) {
1244
2203
  if (sticky.replayOnly) this.sticky.delete(eventId);
1245
2204
  }
1246
- const settled = await raceWithTimeout(this.flush(), SHUTDOWN_DEADLINE_MS);
1247
- if (!settled) this.warnShutdownDrop("in-flight request(s) at shutdown");
2205
+ result = await this.drainAll(deadlineAt);
2206
+ if (!result.delivered) {
2207
+ this.warnShutdownDrop(
2208
+ `${result.dropped + result.queued + result.inFlight} payload(s) not delivered`
2209
+ );
2210
+ }
1248
2211
  } finally {
1249
2212
  this.shutdownDeadlineAt = void 0;
1250
2213
  if (this.ownsAppGitContext) this.appGitContext.dispose();
1251
2214
  }
2215
+ return asCompat(result);
1252
2216
  }
1253
2217
  async trackSignal(signal) {
1254
2218
  var _a, _b;
@@ -1269,23 +2233,9 @@ var EventShipper = class {
1269
2233
  ...signal.after ? { after: signal.after } : {}
1270
2234
  }
1271
2235
  }
1272
- ];
2236
+ ].map((item) => this.redactFields("signal", item));
1273
2237
  if (!this.writeKey) return;
1274
- const url = `${this.baseUrl}signals/track`;
1275
- const opts = this.requestOpts();
1276
- if (!opts) {
1277
- this.warnShutdownDrop("signal");
1278
- return;
1279
- }
1280
- try {
1281
- await postJson(url, body, this.requestHeaders(), opts);
1282
- } catch (err) {
1283
- const msg = err instanceof Error ? err.message : String(err);
1284
- rateLimitedLog(
1285
- `${this.prefix}.send_signal_failed`,
1286
- () => console.warn(`${this.prefix} failed to send signal (dropping): ${msg}`)
1287
- );
1288
- }
2238
+ await this.sendDirect("signal", `${this.baseUrl}signals/track`, body);
1289
2239
  }
1290
2240
  async identify(users) {
1291
2241
  if (!this.enabled) return;
@@ -1300,30 +2250,80 @@ var EventShipper = class {
1300
2250
  return false;
1301
2251
  }
1302
2252
  return true;
1303
- }).map((user) => {
1304
- var _a;
1305
- return {
1306
- user_id: user.userId,
1307
- traits: (_a = user.traits) != null ? _a : {}
1308
- };
1309
- });
2253
+ }).map(
2254
+ (user) => {
2255
+ var _a;
2256
+ return this.redactFields("identify", {
2257
+ user_id: user.userId,
2258
+ traits: (_a = user.traits) != null ? _a : {}
2259
+ });
2260
+ }
2261
+ );
1310
2262
  if (!this.writeKey) return;
1311
2263
  if (body.length === 0) return;
1312
- const url = `${this.baseUrl}users/identify`;
1313
- const opts = this.requestOpts();
2264
+ await this.sendDirect("identify", `${this.baseUrl}users/identify`, body);
2265
+ }
2266
+ /**
2267
+ * Send a signal or identify body. Tracked so `flush()` waits for it. A send
2268
+ * a flush deadline deferred is kept and retried later (outside shutdown).
2269
+ */
2270
+ async sendDirect(kind, url, body, deadlineAt) {
2271
+ const opts = this.requestOpts(deadlineAt);
1314
2272
  if (!opts) {
1315
- this.warnShutdownDrop("identify");
1316
- return;
2273
+ this.warnShutdownDrop(kind);
2274
+ return "dropped";
1317
2275
  }
2276
+ const sending = sendJson(url, body, this.requestHeaders(), opts);
2277
+ const tracked = sending.then(() => void 0);
2278
+ this.inFlight.add(tracked);
2279
+ let outcome;
1318
2280
  try {
1319
- await postJson(url, body, this.requestHeaders(), opts);
1320
- } catch (err) {
1321
- const msg = err instanceof Error ? err.message : String(err);
2281
+ outcome = await sending;
2282
+ } finally {
2283
+ this.inFlight.delete(tracked);
2284
+ }
2285
+ const fate = fateOf(outcome, this.shuttingDown());
2286
+ if (fate === "queued") {
2287
+ this.deferSend(
2288
+ (later) => this.sendDirect(kind, url, body, later),
2289
+ outcome.status === "deferred" ? outcome.retryAtMs : void 0
2290
+ );
2291
+ } else if (fate === "dropped") {
2292
+ this.warnSendDropped(kind, outcome);
2293
+ }
2294
+ return fate;
2295
+ }
2296
+ deferSend(send, retryAtMs) {
2297
+ var _a, _b;
2298
+ if (this.deferredSends.size >= MAX_DEFERRED_SENDS) {
1322
2299
  rateLimitedLog(
1323
- `${this.prefix}.send_identify_failed`,
1324
- () => console.warn(`${this.prefix} failed to send identify (dropping): ${msg}`)
2300
+ `${this.prefix}.deferred_full`,
2301
+ () => console.warn(`${this.prefix} too many sends waiting for a retry; dropping one`)
1325
2302
  );
2303
+ return;
1326
2304
  }
2305
+ const entry = { send };
2306
+ const delay = Math.max(this.partialFlushMs, (retryAtMs != null ? retryAtMs : 0) - Date.now());
2307
+ entry.timer = setTimeout(() => {
2308
+ if (!this.deferredSends.delete(entry)) return;
2309
+ void send().catch(() => {
2310
+ });
2311
+ }, delay);
2312
+ (_b = (_a = entry.timer).unref) == null ? void 0 : _b.call(_a);
2313
+ this.deferredSends.add(entry);
2314
+ }
2315
+ warnSendDropped(what, outcome) {
2316
+ var _a, _b;
2317
+ if (outcome.status === "delivered" || outcome.status === "rejected") return;
2318
+ 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;
2319
+ rateLimitedLog(
2320
+ `${this.prefix}.send_${what}_dropped`,
2321
+ () => console.warn(`${this.prefix} failed to send ${what} (dropping): ${reason}`)
2322
+ );
2323
+ }
2324
+ /** The payload with its PII fields redacted when `redactPii` is on (CONTRACT 11.2). */
2325
+ redactFields(kind, payload) {
2326
+ return this.redactPii ? this.piiFields.redact(kind, payload) : payload;
1327
2327
  }
1328
2328
  warnShutdownDrop(what) {
1329
2329
  rateLimitedLog(
@@ -1331,9 +2331,8 @@ var EventShipper = class {
1331
2331
  () => console.warn(`${this.prefix} shutdown flush deadline exceeded; dropping ${what}`)
1332
2332
  );
1333
2333
  }
1334
- async flushOne(eventId) {
1335
- var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m;
1336
- if (!this.enabled) return;
2334
+ async flushOne(eventId, deadlineAt) {
2335
+ if (!this.enabled) return void 0;
1337
2336
  const timer = this.timers.get(eventId);
1338
2337
  if (timer) {
1339
2338
  clearTimeout(timer);
@@ -1341,30 +2340,91 @@ var EventShipper = class {
1341
2340
  }
1342
2341
  const accumulated = this.buffers.get(eventId);
1343
2342
  this.buffers.delete(eventId);
1344
- if (!accumulated) return;
2343
+ if (!accumulated) return void 0;
2344
+ const previous = this.sendChains.get(eventId);
2345
+ let release = () => {
2346
+ };
2347
+ const current = new Promise((resolve2) => {
2348
+ release = resolve2;
2349
+ });
2350
+ this.sendChains.set(eventId, current);
2351
+ try {
2352
+ if (previous) {
2353
+ const ready = deadlineAt === void 0 ? (await previous, true) : await raceWithTimeout(previous, deadlineAt - Date.now());
2354
+ if (!ready) {
2355
+ return this.requeue(eventId, accumulated, void 0);
2356
+ }
2357
+ }
2358
+ return await this.sendAccumulated(eventId, accumulated, deadlineAt);
2359
+ } finally {
2360
+ release();
2361
+ if (this.sendChains.get(eventId) === current) this.sendChains.delete(eventId);
2362
+ }
2363
+ }
2364
+ /**
2365
+ * Put an unsent update back under any newer one and send it later
2366
+ * (CONTRACT 1.2.1). During shutdown it is dropped instead.
2367
+ */
2368
+ requeue(eventId, accumulated, retryAtMs) {
2369
+ var _a, _b;
2370
+ if (this.shuttingDown()) {
2371
+ this.warnShutdownDrop(`track_partial ${eventId}`);
2372
+ if (accumulated.isPending === false) this.sticky.delete(eventId);
2373
+ return "dropped";
2374
+ }
2375
+ const newer = this.buffers.get(eventId);
2376
+ const merged = newer ? mergePatches(accumulated, newer) : accumulated;
2377
+ merged.isPending = (_a = newer == null ? void 0 : newer.isPending) != null ? _a : accumulated.isPending;
2378
+ this.buffers.set(eventId, merged);
2379
+ const existing = this.timers.get(eventId);
2380
+ if (existing) clearTimeout(existing);
2381
+ const delay = Math.max(this.partialFlushMs, (retryAtMs != null ? retryAtMs : 0) - Date.now());
2382
+ const timeout = setTimeout(() => {
2383
+ void this.flushOne(eventId).catch(() => {
2384
+ });
2385
+ }, delay);
2386
+ (_b = timeout.unref) == null ? void 0 : _b.call(timeout);
2387
+ this.timers.set(eventId, timeout);
2388
+ return "queued";
2389
+ }
2390
+ async sendAccumulated(eventId, accumulated, deadlineAt) {
2391
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p;
1345
2392
  const sticky = (_a = this.sticky.get(eventId)) != null ? _a : {};
1346
2393
  const isPending = (_c = (_b = accumulated.isPending) != null ? _b : sticky.isPending) != null ? _c : true;
1347
- const eventName = (_e = (_d = accumulated.eventName) != null ? _d : sticky.eventName) != null ? _e : this.defaultEventName;
1348
- const userId = (_f = accumulated.userId) != null ? _f : sticky.userId;
1349
- if (!userId) {
1350
- if (this.debug) {
1351
- console.warn(`${this.prefix} skipping track_partial for ${eventId}: missing userId`);
1352
- }
2394
+ const knownEventName = (_d = accumulated.eventName) != null ? _d : sticky.eventName;
2395
+ const eventName = knownEventName != null ? knownEventName : sticky.resumed ? void 0 : this.defaultEventName;
2396
+ const userId = (_e = accumulated.userId) != null ? _e : sticky.userId;
2397
+ if (!userId && !sticky.resumed) {
2398
+ rateLimitedLog(
2399
+ `${this.prefix}.track_partial_missing_user`,
2400
+ () => console.warn(
2401
+ `${this.prefix} skipping track_partial for ${eventId}: missing userId (pass userId, or call resume(eventId) to continue an event begun elsewhere)`
2402
+ )
2403
+ );
1353
2404
  this.sticky.delete(eventId);
1354
- return;
2405
+ return void 0;
2406
+ }
2407
+ if (!userId) {
2408
+ rateLimitedLog(
2409
+ `${this.prefix}.track_partial_resumed_without_user`,
2410
+ () => console.warn(
2411
+ `${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)`
2412
+ )
2413
+ );
1355
2414
  }
1356
- const { wizardSession, ...restProperties } = (_g = accumulated.properties) != null ? _g : {};
2415
+ const { wizardSession, ...restProperties } = (_f = accumulated.properties) != null ? _f : {};
1357
2416
  let telemetryProperties = {};
1358
2417
  try {
1359
2418
  telemetryProperties = eventTelemetryProperties(accumulated);
1360
2419
  } catch (e) {
1361
2420
  }
1362
- const convoId = (_h = accumulated.convoId) != null ? _h : sticky.convoId;
1363
- const payload = {
2421
+ const convoId = (_g = accumulated.convoId) != null ? _g : sticky.convoId;
2422
+ const sessionTag = wizardSession != null ? wizardSession : this.wizardSession;
2423
+ const unredacted = {
1364
2424
  event_id: eventId,
1365
- user_id: userId,
1366
- event: eventName,
1367
- timestamp: (_i = accumulated.timestamp) != null ? _i : (/* @__PURE__ */ new Date()).toISOString(),
2425
+ ...userId ? { user_id: userId } : {},
2426
+ ...eventName ? { event: eventName } : {},
2427
+ timestamp: (_i = (_h = sticky.startedAt) != null ? _h : accumulated.timestamp) != null ? _i : (/* @__PURE__ */ new Date()).toISOString(),
1368
2428
  ai_data: {
1369
2429
  input: accumulated.input,
1370
2430
  output: accumulated.error !== void 0 && accumulated.error !== null ? void 0 : accumulated.output,
@@ -1372,32 +2432,38 @@ var EventShipper = class {
1372
2432
  convo_id: convoId
1373
2433
  },
1374
2434
  properties: {
1375
- ...restProperties,
1376
- ...telemetryProperties,
1377
- ...wizardSession ? { "raindrop.wizardSession": wizardSession } : {},
2435
+ ...this.withCallerProperties(
2436
+ eventId,
2437
+ { ...restProperties, ...telemetryProperties },
2438
+ (_j = sticky.callerProperties) != null ? _j : accumulated.callerProperties
2439
+ ),
2440
+ ...sessionTag ? { "raindrop.wizardSession": sessionTag } : {},
1378
2441
  $context: this.context
1379
2442
  },
1380
2443
  ...accumulated.featureFlags && Object.keys(accumulated.featureFlags).length > 0 ? { feature_flags: accumulated.featureFlags } : {},
1381
2444
  attachments: accumulated.attachments,
1382
2445
  is_pending: isPending
1383
2446
  };
2447
+ const payload = this.redactFields("event", unredacted);
1384
2448
  const url = `${this.baseUrl}events/track_partial`;
1385
2449
  if (this.debug) {
2450
+ const sent = payload;
2451
+ const preview = (v, n) => typeof v === "string" ? v.slice(0, n) : void 0;
1386
2452
  console.log(`${this.prefix} sending track_partial`, {
1387
2453
  eventId,
1388
2454
  eventName,
1389
2455
  userId,
1390
2456
  convoId,
1391
2457
  isPending,
1392
- inputPreview: typeof accumulated.input === "string" ? accumulated.input.slice(0, 120) : void 0,
1393
- outputPreview: typeof accumulated.output === "string" ? accumulated.output.slice(0, 120) : void 0,
1394
- attachments: (_k = (_j = accumulated.attachments) == null ? void 0 : _j.length) != null ? _k : 0,
1395
- attachmentKinds: (_m = (_l = accumulated.attachments) == null ? void 0 : _l.map((a) => ({
2458
+ inputPreview: preview((_k = sent.ai_data) == null ? void 0 : _k.input, 120),
2459
+ outputPreview: preview((_l = sent.ai_data) == null ? void 0 : _l.output, 120),
2460
+ attachments: (_n = (_m = sent.attachments) == null ? void 0 : _m.length) != null ? _n : 0,
2461
+ attachmentKinds: (_p = (_o = sent.attachments) == null ? void 0 : _o.map((a) => ({
1396
2462
  type: a.type,
1397
2463
  role: a.role,
1398
2464
  name: a.name,
1399
- valuePreview: a.value.slice(0, 60)
1400
- }))) != null ? _m : [],
2465
+ valuePreview: preview(a.value, 60)
2466
+ }))) != null ? _p : [],
1401
2467
  endpoint: url
1402
2468
  });
1403
2469
  }
@@ -1410,39 +2476,94 @@ var EventShipper = class {
1410
2476
  if (!this.writeKey) {
1411
2477
  if (!isPending) this.sticky.delete(eventId);
1412
2478
  if (mirror) await mirror;
1413
- return;
2479
+ return void 0;
1414
2480
  }
1415
- const opts = this.requestOpts();
2481
+ const opts = this.requestOpts(deadlineAt);
1416
2482
  if (!opts) {
1417
2483
  this.warnShutdownDrop(`track_partial ${eventId}`);
1418
2484
  if (!isPending) this.sticky.delete(eventId);
1419
2485
  if (mirror) await mirror;
1420
- return;
2486
+ return "dropped";
1421
2487
  }
1422
- const p = postJson(url, payload, this.requestHeaders(), opts);
1423
- const combined = mirror ? Promise.all([mirror, p]).then(() => void 0) : p;
2488
+ const sending = sendJson(url, payload, this.requestHeaders(), opts);
2489
+ const combined = Promise.all([mirror, sending.then(() => void 0)]).then(() => void 0);
1424
2490
  this.inFlight.add(combined);
2491
+ let outcome;
1425
2492
  try {
1426
- try {
1427
- await combined;
1428
- if (this.debug) {
1429
- console.log(`${this.prefix} sent track_partial ${eventId} (${eventName})`);
1430
- }
1431
- } catch (err) {
1432
- const msg = err instanceof Error ? err.message : String(err);
1433
- rateLimitedLog(
1434
- `${this.prefix}.send_track_partial_failed`,
1435
- () => console.warn(`${this.prefix} failed to send track_partial (dropping): ${msg}`)
1436
- );
1437
- }
2493
+ outcome = await sending;
2494
+ if (mirror) await mirror.catch(() => {
2495
+ });
1438
2496
  } finally {
1439
2497
  this.inFlight.delete(combined);
1440
2498
  }
2499
+ const fate = fateOf(outcome, this.shuttingDown());
2500
+ if (fate === "queued") {
2501
+ return this.requeue(
2502
+ eventId,
2503
+ accumulated,
2504
+ outcome.status === "deferred" ? outcome.retryAtMs : void 0
2505
+ );
2506
+ }
2507
+ if (fate === "dropped") this.warnSendDropped("track_partial", outcome);
2508
+ else if (this.debug && outcome.status === "delivered") {
2509
+ console.log(`${this.prefix} sent track_partial ${eventId} (${eventName})`);
2510
+ }
1441
2511
  if (!isPending) {
1442
2512
  this.sticky.delete(eventId);
1443
2513
  }
2514
+ return fate;
1444
2515
  }
1445
2516
  };
2517
+ function overriddenKeys(derived, caller) {
2518
+ const keys = [];
2519
+ for (const [key, value] of Object.entries(caller)) {
2520
+ if (value === void 0 || !(key in derived)) continue;
2521
+ const existing = derived[key];
2522
+ if (isPlainObject2(existing) && isPlainObject2(value)) {
2523
+ if (overriddenKeys(existing, value).length > 0) keys.push(key);
2524
+ } else if (JSON.stringify(existing) !== JSON.stringify(value)) {
2525
+ keys.push(key);
2526
+ }
2527
+ }
2528
+ return keys;
2529
+ }
2530
+ function patchHasContent(patch) {
2531
+ var _a, _b;
2532
+ 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;
2533
+ }
2534
+ function resolveFlushTimeoutMs(value) {
2535
+ return typeof value === "number" && Number.isFinite(value) && value > 0 ? value : DEFAULT_FLUSH_TIMEOUT_MS;
2536
+ }
2537
+ function createEventsApi(shipper) {
2538
+ return {
2539
+ async patch(eventId, patch) {
2540
+ await (shipper == null ? void 0 : shipper.patch(eventId, markManualUpdate(asCallerPatch(patch))));
2541
+ },
2542
+ async finish(eventId, patch = {}) {
2543
+ await (shipper == null ? void 0 : shipper.finish(eventId, markManualUpdate(asCallerPatch(patch))));
2544
+ },
2545
+ async addAttachments(eventId, attachments) {
2546
+ await (shipper == null ? void 0 : shipper.patch(eventId, markManualUpdate({ attachments })));
2547
+ },
2548
+ async setProperties(eventId, properties) {
2549
+ await (shipper == null ? void 0 : shipper.patch(eventId, markManualUpdate({ properties, callerProperties: properties })));
2550
+ },
2551
+ async setFeatureFlags(eventId, featureFlags) {
2552
+ await (shipper == null ? void 0 : shipper.patch(eventId, markManualUpdate({ featureFlags: normalizeFeatureFlags(featureFlags) })));
2553
+ },
2554
+ resume(eventId, options) {
2555
+ shipper == null ? void 0 : shipper.resume(eventId, options);
2556
+ }
2557
+ };
2558
+ }
2559
+ function asCallerPatch(patch) {
2560
+ var _a;
2561
+ if (!patch.properties) return patch;
2562
+ return {
2563
+ ...patch,
2564
+ callerProperties: { ...patch.properties, ...(_a = patch.callerProperties) != null ? _a : {} }
2565
+ };
2566
+ }
1446
2567
  var MODEL_USAGE_ATTRIBUTES = {
1447
2568
  usageSource: "raindrop.usage.source",
1448
2569
  providerName: "gen_ai.provider.name",
@@ -1502,6 +2623,21 @@ function buildRawModelUsageAttributes(usage) {
1502
2623
  attrDouble(MODEL_USAGE_ATTRIBUTES.providerReportedCost, providerReportedCost)
1503
2624
  ].filter((attribute) => attribute !== void 0);
1504
2625
  }
2626
+ var KNOWN_MODEL_PROVIDERS = /* @__PURE__ */ new Set([
2627
+ ...MODEL_PROVIDER_NAMES.map(([, family]) => family),
2628
+ "vercel",
2629
+ // Further vendors Dawn's pricing resolves (apps/dawn/lib/spend).
2630
+ "deepseek",
2631
+ "fireworks-ai",
2632
+ "groq",
2633
+ "inception",
2634
+ "mistral",
2635
+ "moonshotai",
2636
+ "perplexity",
2637
+ "togetherai",
2638
+ "xai",
2639
+ "zai"
2640
+ ]);
1505
2641
  var STRING_ALIASES = {
1506
2642
  [MODEL_USAGE_ATTRIBUTES.requestModel]: ["ai.model.id", "ai.model"]
1507
2643
  };
@@ -1621,12 +2757,41 @@ var REDACTED_PLACEHOLDER = "[REDACTED]";
1621
2757
  function normalizeKeyName(name) {
1622
2758
  return name.toLowerCase().replace(/[-_.]/g, "");
1623
2759
  }
2760
+ var URL_USERINFO = new RegExp(
2761
+ String.raw`\b([a-z][a-z0-9+.-]{0,${DEFAULT_MAX_TEXT_FIELD_CHARS - 1}})(?::\/\/([^\s/?#]{1,${DEFAULT_MAX_TEXT_FIELD_CHARS}})@)?`,
2762
+ "gi"
2763
+ );
2764
+ var URL_QUERY_PARAM = /([?&#]([A-Za-z0-9_.\-]+)=)[^&#?\s"']*/g;
2765
+ var URL_SECRET_QUERY_KEYS = /* @__PURE__ */ new Set([
2766
+ "token",
2767
+ "secret",
2768
+ "sig",
2769
+ "signature",
2770
+ "awsaccesskeyid",
2771
+ "xamzsignature",
2772
+ "xamzcredential",
2773
+ "xamzsecuritytoken",
2774
+ "xgoogsignature"
2775
+ ]);
2776
+ var DEFAULT_SECRET_SET = buildSecretSet(DEFAULT_SECRET_KEY_NAMES);
2777
+ function redactUrlSecrets(text, secretSet = DEFAULT_SECRET_SET, placeholder = REDACTED_PLACEHOLDER) {
2778
+ if (text.length > DEFAULT_MAX_TEXT_FIELD_CHARS) return placeholder;
2779
+ if (!text.includes("://") && !text.includes("=")) return text;
2780
+ return text.replace(
2781
+ URL_USERINFO,
2782
+ (whole, scheme, userinfo) => userinfo === void 0 ? whole : `${scheme}://${placeholder}@`
2783
+ ).replace(URL_QUERY_PARAM, (whole, prefix, key) => {
2784
+ const normalized = normalizeKeyName(key);
2785
+ return secretSet.has(normalized) || URL_SECRET_QUERY_KEYS.has(normalized) ? `${prefix}${placeholder}` : whole;
2786
+ });
2787
+ }
1624
2788
  function redactSecretsInObject(value, options) {
1625
2789
  var _a, _b;
1626
2790
  const normalizedSecretSet = buildSecretSet((_a = options == null ? void 0 : options.secretKeyNames) != null ? _a : DEFAULT_SECRET_KEY_NAMES);
1627
2791
  const placeholder = (_b = options == null ? void 0 : options.placeholder) != null ? _b : REDACTED_PLACEHOLDER;
1628
2792
  const seen = /* @__PURE__ */ new WeakSet();
1629
2793
  const walk = (node) => {
2794
+ if (typeof node === "string") return redactUrlSecrets(node, normalizedSecretSet, placeholder);
1630
2795
  if (node === null || typeof node !== "object") return node;
1631
2796
  if (seen.has(node)) return "[CIRCULAR]";
1632
2797
  seen.add(node);
@@ -1725,6 +2890,7 @@ var TraceShipper = class {
1725
2890
  this.appGitAdded = /* @__PURE__ */ new WeakMap();
1726
2891
  this.queue = [];
1727
2892
  this.inFlight = /* @__PURE__ */ new Set();
2893
+ this.cutOff = new InFlightCutOff();
1728
2894
  this.hasShutdown = false;
1729
2895
  var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j;
1730
2896
  this.appGitContext = (_a = opts.appGitContext) != null ? _a : createAppGitContext(opts.appGit);
@@ -1737,6 +2903,7 @@ var TraceShipper = class {
1737
2903
  this.flushIntervalMs = (_d = opts.flushIntervalMs) != null ? _d : 1e3;
1738
2904
  this.maxBatchSize = (_e = opts.maxBatchSize) != null ? _e : 50;
1739
2905
  this.maxQueueSize = (_f = opts.maxQueueSize) != null ? _f : 5e3;
2906
+ this.flushTimeoutMs = resolveFlushTimeoutMs(opts.flushTimeoutMs);
1740
2907
  this.sdkName = (_g = opts.sdkName) != null ? _g : "core";
1741
2908
  this.prefix = `[raindrop-ai/${this.sdkName}]`;
1742
2909
  this.serviceName = (_h = opts.serviceName) != null ? _h : "raindrop.core";
@@ -2015,25 +3182,51 @@ var TraceShipper = class {
2015
3182
  }
2016
3183
  this.queue.push(redacted);
2017
3184
  if (this.queue.length >= this.maxBatchSize) {
2018
- void this.flush().catch(() => {
3185
+ void this.drain().catch(() => {
2019
3186
  });
2020
3187
  return;
2021
3188
  }
2022
- if (!this.timer) {
2023
- this.timer = setTimeout(() => {
2024
- this.timer = void 0;
2025
- void this.flush().catch(() => {
2026
- });
2027
- }, this.flushIntervalMs);
2028
- }
3189
+ this.scheduleDrain(this.flushIntervalMs);
3190
+ }
3191
+ scheduleDrain(delayMs) {
3192
+ if (this.timer) return;
3193
+ this.timer = setTimeout(() => {
3194
+ this.timer = void 0;
3195
+ void this.drain().catch(() => {
3196
+ });
3197
+ }, delayMs);
2029
3198
  }
3199
+ /**
3200
+ * Send every queued span within `flushTimeoutMs` (CONTRACT 1.2.1). Resolves,
3201
+ * never rejects, to whether they all reached ingest, and warns when they
3202
+ * didn't. Spans not yet sent at the deadline stay queued.
3203
+ */
2030
3204
  async flush() {
2031
- var _a;
2032
- if (!this.enabled) {
2033
- await Promise.all([...this.inFlight].map((p) => p.catch(() => {
2034
- })));
2035
- return;
3205
+ const result = await this.drainWithin(Date.now() + this.flushTimeoutMs);
3206
+ if (!result.delivered && this.shutdownDeadlineAt === void 0) {
3207
+ rateLimitedLog(
3208
+ `${this.prefix}.flush_undelivered`,
3209
+ () => console.warn(`${this.prefix} ${describeUndelivered("flush()", result)}`)
3210
+ );
2036
3211
  }
3212
+ return asCompat(result);
3213
+ }
3214
+ async drainWithin(deadlineAt) {
3215
+ const fates = this.enabled ? await this.drain(deadlineAt) : [];
3216
+ const running = [...this.inFlight];
3217
+ const { settled, stillRunning } = await awaitEarlierSends(
3218
+ running,
3219
+ deadlineAt,
3220
+ this.cutOff,
3221
+ (p) => this.inFlight.has(p)
3222
+ );
3223
+ return summarize2(fates, stillRunning, !settled);
3224
+ }
3225
+ /** Send queued batches; with a deadline, a deferred batch goes back on the queue. */
3226
+ async drain(deadlineAt) {
3227
+ var _a;
3228
+ const fates = [];
3229
+ if (!this.enabled) return fates;
2037
3230
  if (this.timer) {
2038
3231
  clearTimeout(this.timer);
2039
3232
  this.timer = void 0;
@@ -2047,10 +3240,12 @@ var TraceShipper = class {
2047
3240
  if (spans) spans.push(span);
2048
3241
  else groups.set(destination, [span]);
2049
3242
  }
3243
+ const requeue = [];
3244
+ let retryAtMs;
2050
3245
  for (const [destination, spans] of groups) {
2051
3246
  if (!destination && !this.writeKey) continue;
2052
3247
  if ((destination == null ? void 0 : destination.kind) === "workshop") continue;
2053
- const opts = this.requestOpts();
3248
+ const opts = this.requestOpts(deadlineAt);
2054
3249
  if (!opts) {
2055
3250
  rateLimitedLog(
2056
3251
  `${this.prefix}.shutdown_deadline`,
@@ -2058,6 +3253,7 @@ var TraceShipper = class {
2058
3253
  `${this.prefix} shutdown flush deadline exceeded; dropping ${spans.length} spans`
2059
3254
  )
2060
3255
  );
3256
+ fates.push("dropped");
2061
3257
  continue;
2062
3258
  }
2063
3259
  const body = buildExportTraceServiceRequest(spans, this.serviceName, this.serviceVersion);
@@ -2073,35 +3269,56 @@ var TraceShipper = class {
2073
3269
  endpoint: url
2074
3270
  });
2075
3271
  }
2076
- const p = postJson(url, body, headers, opts);
2077
- this.inFlight.add(p);
3272
+ const sending = sendJson(url, body, headers, opts);
3273
+ const tracked = sending.then(() => void 0);
3274
+ this.inFlight.add(tracked);
3275
+ let outcome;
2078
3276
  try {
2079
- try {
2080
- await p;
2081
- if (this.debug) console.log(`${this.prefix} sent ${spans.length} spans`);
2082
- } catch (err) {
2083
- const msg = err instanceof Error ? err.message : String(err);
2084
- rateLimitedLog(
2085
- `${this.prefix}.send_spans_failed`,
2086
- () => console.warn(`${this.prefix} failed to send ${spans.length} spans: ${msg}`)
2087
- );
2088
- }
3277
+ outcome = await sending;
2089
3278
  } finally {
2090
- this.inFlight.delete(p);
3279
+ this.inFlight.delete(tracked);
3280
+ }
3281
+ const fate = fateOf(outcome, this.shutdownDeadlineAt !== void 0 || this.hasShutdown);
3282
+ fates.push(fate);
3283
+ if (fate === "queued") {
3284
+ requeue.push(...spans);
3285
+ if (outcome.status === "deferred" && outcome.retryAtMs !== void 0) {
3286
+ retryAtMs = Math.max(retryAtMs != null ? retryAtMs : 0, outcome.retryAtMs);
3287
+ }
3288
+ } else if (fate === "dropped") {
3289
+ 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;
3290
+ rateLimitedLog(
3291
+ `${this.prefix}.send_spans_failed`,
3292
+ () => console.warn(`${this.prefix} failed to send ${spans.length} spans: ${reason}`)
3293
+ );
3294
+ } else if (this.debug) {
3295
+ console.log(`${this.prefix} sent ${spans.length} spans`);
3296
+ }
3297
+ }
3298
+ if (requeue.length > 0) {
3299
+ this.queue.unshift(...requeue);
3300
+ if (this.queue.length > this.maxQueueSize) {
3301
+ this.queue.splice(this.maxQueueSize);
2091
3302
  }
3303
+ this.scheduleDrain(Math.max(this.flushIntervalMs, (retryAtMs != null ? retryAtMs : 0) - Date.now()));
3304
+ break;
2092
3305
  }
2093
3306
  }
3307
+ return fates;
3308
+ }
3309
+ /** See EventShipper.requestOpts — same deadline semantics. */
3310
+ requestOpts(deadlineAt) {
3311
+ const opts = this.scheduleFor(deadlineAt);
3312
+ return opts && { ...opts, cutOff: this.cutOff.signal };
2094
3313
  }
2095
- /** See EventShipper.requestOpts — same shutdown-budget semantics. */
2096
- requestOpts() {
3314
+ scheduleFor(deadlineAt) {
2097
3315
  if (this.shutdownDeadlineAt !== void 0) {
2098
- const remainingMs = this.shutdownDeadlineAt - Date.now();
2099
- if (remainingMs <= 0) return null;
3316
+ if (this.shutdownDeadlineAt - Date.now() <= 0) return null;
2100
3317
  return {
2101
- maxAttempts: 1,
3318
+ maxAttempts: 3,
2102
3319
  debug: this.debug,
2103
3320
  sdkName: this.sdkName,
2104
- timeoutMs: Math.min(DEFAULT_REQUEST_TIMEOUT_MS, remainingMs)
3321
+ deadlineAt: Math.min(this.shutdownDeadlineAt, deadlineAt != null ? deadlineAt : Infinity)
2105
3322
  };
2106
3323
  }
2107
3324
  if (this.hasShutdown) {
@@ -2112,27 +3329,28 @@ var TraceShipper = class {
2112
3329
  timeoutMs: POST_SHUTDOWN_TIMEOUT_MS
2113
3330
  };
2114
3331
  }
2115
- return { maxAttempts: 3, debug: this.debug, sdkName: this.sdkName };
3332
+ return { maxAttempts: 3, debug: this.debug, sdkName: this.sdkName, deadlineAt };
2116
3333
  }
3334
+ /**
3335
+ * Flush within `flushTimeoutMs` and stop; spans left at the deadline are
3336
+ * dropped with a warning. Resolves, never rejects.
3337
+ */
2117
3338
  async shutdown() {
2118
3339
  this.hasShutdown = true;
2119
- this.shutdownDeadlineAt = Date.now() + SHUTDOWN_DEADLINE_MS;
3340
+ const deadlineAt = Date.now() + this.flushTimeoutMs;
3341
+ this.shutdownDeadlineAt = deadlineAt;
3342
+ let result = { ...DELIVERED };
2120
3343
  try {
2121
3344
  if (this.timer) {
2122
3345
  clearTimeout(this.timer);
2123
3346
  this.timer = void 0;
2124
3347
  }
2125
- const drain = async () => {
2126
- await this.flush();
2127
- await Promise.all([...this.inFlight].map((p) => p.catch(() => {
2128
- })));
2129
- };
2130
- const settled = await raceWithTimeout(drain(), SHUTDOWN_DEADLINE_MS);
2131
- if (!settled) {
3348
+ result = await this.drainWithin(deadlineAt);
3349
+ if (!result.delivered) {
2132
3350
  rateLimitedLog(
2133
3351
  `${this.prefix}.shutdown_deadline`,
2134
3352
  () => console.warn(
2135
- `${this.prefix} shutdown flush deadline exceeded; abandoning in-flight spans`
3353
+ `${this.prefix} shutdown flush deadline exceeded; ${describeUndelivered("shutdown()", result)}`
2136
3354
  )
2137
3355
  );
2138
3356
  }
@@ -2140,10 +3358,74 @@ var TraceShipper = class {
2140
3358
  this.shutdownDeadlineAt = void 0;
2141
3359
  if (this.ownsAppGitContext) this.appGitContext.dispose();
2142
3360
  }
3361
+ return asCompat(result);
2143
3362
  }
2144
3363
  };
3364
+ function eventShipperOptions(client, extras = {}) {
3365
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i;
3366
+ return {
3367
+ ...extras,
3368
+ writeKey: (_a = extras.writeKey) != null ? _a : client.writeKey,
3369
+ endpoint: (_b = extras.endpoint) != null ? _b : client.endpoint,
3370
+ projectId: (_c = extras.projectId) != null ? _c : client.projectId,
3371
+ debug: (_e = (_d = extras.debug) != null ? _d : client.debug) != null ? _e : false,
3372
+ maxTextFieldChars: (_f = extras.maxTextFieldChars) != null ? _f : client.maxTextFieldChars,
3373
+ flushTimeoutMs: (_g = extras.flushTimeoutMs) != null ? _g : client.flushTimeoutMs,
3374
+ redactPii: (_h = extras.redactPii) != null ? _h : client.redactPii,
3375
+ redactPiiFields: (_i = extras.redactPiiFields) != null ? _i : client.redactPiiFields
3376
+ };
3377
+ }
3378
+ function traceShipperOptions(client, extras = {}) {
3379
+ var _a, _b, _c, _d, _e, _f, _g;
3380
+ return {
3381
+ ...extras,
3382
+ writeKey: (_a = extras.writeKey) != null ? _a : client.writeKey,
3383
+ endpoint: (_b = extras.endpoint) != null ? _b : client.endpoint,
3384
+ projectId: (_c = extras.projectId) != null ? _c : client.projectId,
3385
+ debug: (_e = (_d = extras.debug) != null ? _d : client.debug) != null ? _e : false,
3386
+ maxTextFieldChars: (_f = extras.maxTextFieldChars) != null ? _f : client.maxTextFieldChars,
3387
+ flushTimeoutMs: (_g = extras.flushTimeoutMs) != null ? _g : client.flushTimeoutMs
3388
+ };
3389
+ }
3390
+ function createShippers(client, setup = {}) {
3391
+ return {
3392
+ eventShipper: new EventShipper(eventShipperOptions(client, { ...setup.common, ...setup.event })),
3393
+ traceShipper: new TraceShipper(traceShipperOptions(client, { ...setup.common, ...setup.trace }))
3394
+ };
3395
+ }
3396
+ function isFlushResult(value) {
3397
+ 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";
3398
+ }
3399
+ var UNDELIVERED = Object.freeze({
3400
+ delivered: false,
3401
+ timedOut: false,
3402
+ dropped: 0,
3403
+ queued: 0,
3404
+ inFlight: 0
3405
+ });
3406
+ async function settle(call, what) {
3407
+ try {
3408
+ const result = await call();
3409
+ return isFlushResult(result) ? result : DELIVERED;
3410
+ } catch (error) {
3411
+ console.warn(`[raindrop-ai] ${what} failed: ${error instanceof Error ? error.message : String(error)}`);
3412
+ return UNDELIVERED;
3413
+ }
3414
+ }
3415
+ async function flushShippers(...shippers) {
3416
+ const results = await Promise.all(
3417
+ shippers.flatMap((s) => s ? [settle(() => s.flush(), "flush()")] : [])
3418
+ );
3419
+ return asCompat(combineResults(...results));
3420
+ }
3421
+ async function shutdownShippers(...shippers) {
3422
+ const results = await Promise.all(
3423
+ shippers.flatMap((s) => s ? [settle(() => s.shutdown(), "shutdown()")] : [])
3424
+ );
3425
+ return asCompat(combineResults(...results));
3426
+ }
2145
3427
  var PROMPT_TOOLS_ATTRIBUTE = "ai.prompt.tools";
2146
- function isRecord(value) {
3428
+ function isRecord3(value) {
2147
3429
  return typeof value === "object" && value !== null && !Array.isArray(value);
2148
3430
  }
2149
3431
  var JSON_SCHEMA_KEYS = [
@@ -2161,17 +3443,17 @@ var JSON_SCHEMA_KEYS = [
2161
3443
  "required"
2162
3444
  ];
2163
3445
  function isJsonSchema(value) {
2164
- if (!isRecord(value)) return false;
3446
+ if (!isRecord3(value)) return false;
2165
3447
  if (typeof value["~standard"] === "object") return false;
2166
- if (isRecord(value._def) || isRecord(value._zod)) return false;
3448
+ if (isRecord3(value._def) || isRecord3(value._zod)) return false;
2167
3449
  if (Object.keys(value).length === 0) return true;
2168
3450
  return JSON_SCHEMA_KEYS.some((key) => key in value);
2169
3451
  }
2170
3452
  function toJsonSchema(value, options) {
2171
3453
  if (value === void 0 || value === null) return void 0;
2172
3454
  if (isJsonSchema(value)) return value;
2173
- if (isRecord(value) && isJsonSchema(value.jsonSchema)) return value.jsonSchema;
2174
- if (isRecord(value) && isJsonSchema(value.json)) return value.json;
3455
+ if (isRecord3(value) && isJsonSchema(value.jsonSchema)) return value.jsonSchema;
3456
+ if (isRecord3(value) && isJsonSchema(value.json)) return value.json;
2175
3457
  if (options == null ? void 0 : options.schema) {
2176
3458
  try {
2177
3459
  return options.schema(value);
@@ -2194,23 +3476,23 @@ function functionTool(name, description, schema, options) {
2194
3476
  }
2195
3477
  function normalizeToolDeclaration(tool, options, fallbackName) {
2196
3478
  var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j;
2197
- if (!isRecord(tool)) return void 0;
3479
+ if (!isRecord3(tool)) return void 0;
2198
3480
  if (tool.type === "provider-defined" || tool.type === "provider") {
2199
3481
  const name2 = (_b = (_a = pickString(tool.name)) != null ? _a : pickString(tool.id)) != null ? _b : fallbackName;
2200
3482
  if (name2 === void 0) return void 0;
2201
3483
  const declaration = { type: "provider-defined", name: name2 };
2202
3484
  const id = pickString(tool.id);
2203
3485
  if (id !== void 0) declaration.id = id;
2204
- if (isRecord(tool.args)) declaration.args = tool.args;
3486
+ if (isRecord3(tool.args)) declaration.args = tool.args;
2205
3487
  return declaration;
2206
3488
  }
2207
- if (isRecord(tool.toolSpec)) {
3489
+ if (isRecord3(tool.toolSpec)) {
2208
3490
  const spec = tool.toolSpec;
2209
3491
  const name2 = (_c = pickString(spec.name)) != null ? _c : fallbackName;
2210
3492
  if (name2 === void 0) return void 0;
2211
3493
  return functionTool(name2, spec.description, spec.inputSchema, options);
2212
3494
  }
2213
- if (isRecord(tool.function)) {
3495
+ if (isRecord3(tool.function)) {
2214
3496
  const fn = tool.function;
2215
3497
  const name2 = (_d = pickString(fn.name)) != null ? _d : fallbackName;
2216
3498
  if (name2 === void 0) return void 0;
@@ -2236,7 +3518,7 @@ function isGeminiToolGroup(tool) {
2236
3518
  if (Array.isArray(tool.functionDeclarations)) return true;
2237
3519
  const keys = Object.keys(tool);
2238
3520
  if (keys.length === 0 || NOT_A_GEMINI_GROUP_KEYS.some((key) => key in tool)) return false;
2239
- return keys.every((key) => isRecord(tool[key]));
3521
+ return keys.every((key) => isRecord3(tool[key]));
2240
3522
  }
2241
3523
  function expandGeminiTool(tool, options) {
2242
3524
  const declarations = [];
@@ -2246,7 +3528,7 @@ function expandGeminiTool(tool, options) {
2246
3528
  const normalized = normalizeToolDeclaration(declaration, options);
2247
3529
  if (normalized) declarations.push(normalized);
2248
3530
  }
2249
- } else if (isRecord(value)) {
3531
+ } else if (isRecord3(value)) {
2250
3532
  declarations.push({ type: "provider-defined", id: key, name: key, args: value });
2251
3533
  }
2252
3534
  }
@@ -2256,14 +3538,14 @@ function normalizeToolDeclarations(tools, options) {
2256
3538
  if (tools === void 0 || tools === null) return void 0;
2257
3539
  if (Array.isArray(tools)) {
2258
3540
  return tools.flatMap((tool) => {
2259
- if (isRecord(tool) && isGeminiToolGroup(tool)) {
3541
+ if (isRecord3(tool) && isGeminiToolGroup(tool)) {
2260
3542
  return expandGeminiTool(tool, options);
2261
3543
  }
2262
3544
  const declaration = normalizeToolDeclaration(tool, options);
2263
3545
  return declaration ? [declaration] : [];
2264
3546
  });
2265
3547
  }
2266
- if (!isRecord(tools)) return void 0;
3548
+ if (!isRecord3(tools)) return void 0;
2267
3549
  return Object.entries(tools).flatMap(([key, tool]) => {
2268
3550
  const declaration = normalizeToolDeclaration(tool, options, key);
2269
3551
  return declaration ? [declaration] : [];
@@ -2409,39 +3691,6 @@ var resolveLocal = (sourceDirectory, detectBranch, completed) => {
2409
3691
  function createAppGitContext2(options, runtime = {}) {
2410
3692
  return new AppGitContext(options, { ...runtime, resolveLocal });
2411
3693
  }
2412
- function hasDestination(options) {
2413
- var _a;
2414
- if (options.enabled === false) return false;
2415
- return Boolean(((_a = options.writeKey) == null ? void 0 : _a.trim()) || resolveLocalDebuggerBaseUrl(options.localDebuggerUrl));
2416
- }
2417
- var EventShipper2 = class extends EventShipper {
2418
- constructor(options) {
2419
- var _a;
2420
- const ownsAppGitContext = options.appGitContext === void 0;
2421
- const appGitContext = (_a = options.appGitContext) != null ? _a : createAppGitContext2(options.appGit, { allowDiscovery: hasDestination(options) });
2422
- try {
2423
- super({ ...options, appGitContext });
2424
- } catch (error) {
2425
- if (ownsAppGitContext) appGitContext.dispose();
2426
- throw error;
2427
- }
2428
- if (ownsAppGitContext) this.ownAppGitContextForShutdown();
2429
- }
2430
- };
2431
- var TraceShipper2 = class extends TraceShipper {
2432
- constructor(options) {
2433
- var _a;
2434
- const ownsAppGitContext = options.appGitContext === void 0;
2435
- const appGitContext = (_a = options.appGitContext) != null ? _a : createAppGitContext2(options.appGit, { allowDiscovery: hasDestination(options) });
2436
- try {
2437
- super({ ...options, appGitContext });
2438
- } catch (error) {
2439
- if (ownsAppGitContext) appGitContext.dispose();
2440
- throw error;
2441
- }
2442
- this.ownsAppGitContext = ownsAppGitContext;
2443
- }
2444
- };
2445
3694
  globalThis.RAINDROP_ASYNC_LOCAL_STORAGE = AsyncLocalStorage;
2446
3695
  var SUPPRESS_TRACING_KEY = /* @__PURE__ */ Symbol.for(
2447
3696
  "OpenTelemetry SDK Context Key SUPPRESS_TRACING"
@@ -2579,6 +3828,8 @@ var RaindropManagedAgentsHandler = class {
2579
3828
  eventName: (_e = context.eventName) != null ? _e : "ai_generation",
2580
3829
  input: void 0,
2581
3830
  model: void 0,
3831
+ // Caller-supplied properties win over the derived keys below (W6).
3832
+ callerProperties: context.properties,
2582
3833
  properties: {
2583
3834
  ...context.properties,
2584
3835
  "ai.provider": "openai",
@@ -2899,7 +4150,7 @@ function isRecord2(value) {
2899
4150
  // package.json
2900
4151
  var package_default = {
2901
4152
  name: "@raindrop-ai/openai-managed-agents",
2902
- version: "0.0.3",
4153
+ version: "0.0.5",
2903
4154
  description: "Raindrop tracing for the OpenAI Agents API",
2904
4155
  main: "dist/index.js",
2905
4156
  module: "dist/index.mjs",
@@ -2941,11 +4192,11 @@ var package_default = {
2941
4192
  "@raindrop-ai/core": "workspace:*",
2942
4193
  "@types/node": "^20.11.17",
2943
4194
  msw: "^2.12.7",
2944
- tsup: "^8.4.0",
4195
+ tsup: "^9.0.0",
2945
4196
  typescript: "^5.3.3",
2946
- vitest: "^2.1.9",
4197
+ vitest: "^4.1.12",
2947
4198
  openai: "7.15.0",
2948
- zod: "^3.23.8"
4199
+ zod: "^4.6.6"
2949
4200
  },
2950
4201
  tsup: {
2951
4202
  entry: [
@@ -3067,7 +4318,7 @@ function wrapManagedAgents(client, handler, context) {
3067
4318
 
3068
4319
  // src/index.ts
3069
4320
  function createRaindropOpenAIManagedAgents(opts = {}) {
3070
- var _a, _b;
4321
+ var _a;
3071
4322
  const enabled = Boolean((_a = opts.writeKey) == null ? void 0 : _a.trim());
3072
4323
  if (!enabled)
3073
4324
  console.warn(
@@ -3076,44 +4327,33 @@ function createRaindropOpenAIManagedAgents(opts = {}) {
3076
4327
  const appGitContext = createAppGitContext2(enabled ? opts.appGit : false, {
3077
4328
  allowLocalGit: false
3078
4329
  });
3079
- const shipperOptions = {
3080
- writeKey: opts.writeKey,
3081
- endpoint: opts.endpoint,
3082
- projectId: opts.projectId,
3083
- enabled,
3084
- debug: (_b = opts.debug) != null ? _b : false,
3085
- sdkName: "openai-managed-agents",
3086
- libraryName,
3087
- libraryVersion,
3088
- appGitContext,
3089
- maxTextFieldChars: opts.maxTextFieldChars,
3090
- localDebuggerUrl: opts.localWorkshopUrl === false ? null : opts.localWorkshopUrl
3091
- };
3092
- const events = new EventShipper2(shipperOptions);
3093
- const traces = new TraceShipper2({
3094
- ...shipperOptions,
3095
- serviceName: "raindrop.openai-managed-agents",
3096
- serviceVersion: libraryVersion
4330
+ const { eventShipper: events, traceShipper: traces } = createShippers(opts, {
4331
+ common: {
4332
+ enabled,
4333
+ sdkName: "openai-managed-agents",
4334
+ appGitContext,
4335
+ localDebuggerUrl: opts.localWorkshopUrl === false ? null : opts.localWorkshopUrl
4336
+ },
4337
+ event: { libraryName, libraryVersion },
4338
+ trace: { serviceName: "raindrop.openai-managed-agents", serviceVersion: libraryVersion }
3097
4339
  });
3098
4340
  const handler = new RaindropManagedAgentsHandler(events, traces, opts);
3099
4341
  return {
3100
4342
  handler,
3101
4343
  wrap: (client, context) => wrapManagedAgents(client, handler, context),
3102
- events: {
3103
- patch: (id, patch) => events.patch(id, patch),
3104
- finish: (id, patch) => events.finish(id, patch),
3105
- addAttachments: (id, attachments) => events.patch(id, { attachments }),
3106
- setProperties: (id, properties) => events.patch(id, { properties })
4344
+ get lastEventId() {
4345
+ return events == null ? void 0 : events.lastFinishedEventId;
3107
4346
  },
4347
+ events: createEventsApi(events),
3108
4348
  users: { identify: (users) => events.identify(users) },
3109
4349
  signals: { track: (signal) => events.trackSignal(signal) },
3110
- async flush() {
3111
- await Promise.all([events.flush(), traces.flush()]);
4350
+ flush() {
4351
+ return flushShippers(events, traces);
3112
4352
  },
3113
4353
  async shutdown() {
3114
4354
  try {
3115
4355
  handler.shutdown();
3116
- await Promise.all([events.shutdown(), traces.shutdown()]);
4356
+ return await shutdownShippers(events, traces);
3117
4357
  } finally {
3118
4358
  appGitContext.dispose();
3119
4359
  }