@raindrop-ai/openai-managed-agents 0.0.4 → 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/README.md CHANGED
@@ -120,6 +120,18 @@ responsibility.
120
120
  `users.identify`, and `signals.track` expose the shared core enrichment API.
121
121
  `flush()` ships current buffers; `shutdown()` also closes unfinished captures.
122
122
 
123
+ ## Shared client options
124
+
125
+ `createRaindropOpenAIManagedAgents(...)` also takes the options every Raindrop JS integration shares:
126
+
127
+ | Option | Type | Default | Description |
128
+ |---|---|---|---|
129
+ | `redactPii` | `boolean` | `false` | Redact PII (emails, phone numbers, card numbers, addresses, secrets) from event input/output before it leaves the process |
130
+ | `redactPiiFields` | `PiiFieldMap` | - | More wire field paths to redact when `redactPii` is on, e.g. `{ event: ["properties.customer.email"] }`; add `properties["error.message"]` to also redact error messages |
131
+ | `flushTimeoutMs` | `number` | `10000` | Deadline for `flush()` / `shutdown()`. Both resolve to a result (`delivered`, `dropped`, `queued`, ...) and never reject |
132
+
133
+ `flush()` and `shutdown()` resolve to `{ delivered, timedOut, dropped, queued, inFlight }` and never reject; ignoring the result is fine.
134
+
123
135
  ## Tool catalog capture (`ai.prompt.tools`)
124
136
 
125
137
  **Agent configuration, not exact per-call capture.** Model calls run inside
@@ -141,6 +153,20 @@ is enabled.
141
153
  | --- | --- |
142
154
  | `tools` (on `createRaindropOpenAIManagedAgents({ tools })` or `wrap(client, { tools })`) | Overrides the tool catalog recorded as `ai.prompt.tools` on every model span. Use when the integration cannot see the tools the model was given. Replaces the agent definition entirely (`tools: []` records an empty catalog) and carries no `source` attribute. |
143
155
 
156
+ ## Your properties win
157
+
158
+ Properties you pass (as defaults, per call, or through `events.setProperties` / `events.patch` / `events.finish`) are merged into the event. If one of your keys has the same name as a key the integration sets itself, such as `ai.usage.*`, `ai.provider`, `error.type` / `error.message`, or the integration's own prefixed keys, your value is kept, whatever order the updates are sent in. With `debug` on, the integration logs each key you override.
159
+
160
+ ## Event ids
161
+
162
+ Set each turn's id with the `eventId: (turnId) => string` option; the event and every span of the turn carry it. After a turn finishes, `raindrop.lastEventId` holds its id, for attaching a signal or feedback:
163
+
164
+ ```ts
165
+ await raindrop.signals.track({ eventId: raindrop.lastEventId!, name: "thumbs_up", type: "feedback", sentiment: "POSITIVE" });
166
+ ```
167
+
168
+ Finishes you make through `raindrop.events.*` don't change `lastEventId`. It holds the last turn finished anywhere in the process, so on a client shared by concurrent turns, use the id you passed instead.
169
+
144
170
  ## Limits
145
171
 
146
172
  The service does not expose individual inference requests. Captures represent
package/dist/index.d.mts CHANGED
@@ -98,6 +98,49 @@ type AppGitContext = Pick<AppGitContextImpl, "snapshot" | "snapshotForReplay" |
98
98
  releaseOperation?: (eventId: string, owner: AppGitOperationOwner) => void;
99
99
  };
100
100
  declare const AppGitContext: typeof AppGitContextImpl;
101
+
102
+ type FeatureFlagValue = string | number | boolean;
103
+ type FeatureFlags = string[] | Record<string, FeatureFlagValue>;
104
+
105
+ /**
106
+ * What `flush()` / `shutdown()` resolve to (CONTRACT 1.2.1). Safe to ignore:
107
+ * they never reject, and ignoring the result changes nothing.
108
+ *
109
+ * `delivered` is true when everything enqueued before the call was handed to
110
+ * ingest. A payload ingest rejected permanently (a non-429, non-408 4xx)
111
+ * counts as delivered: ingest saw it and refused it.
112
+ */
113
+ type FlushResult = {
114
+ delivered: boolean;
115
+ /** The call returned at its deadline with work outstanding. */
116
+ timedOut: boolean;
117
+ /** Payloads given up on: cut off mid-flight at the deadline, retries ran out, or left at shutdown. */
118
+ dropped: number;
119
+ /** Payloads still queued for a later send (never sent, or waiting on a retry or Retry-After pause). */
120
+ queued: number;
121
+ /**
122
+ * Sends started before the call and still running at its deadline. They are
123
+ * cut off then: a request in flight is dropped (not resent), and one waiting
124
+ * to retry goes back to its queue.
125
+ */
126
+ inFlight: number;
127
+ };
128
+ /**
129
+ * `FlushResult` as the declared return of public `flush()`/`shutdown()`
130
+ * methods. The `void &` keeps `Promise<FlushResultCompat>` assignable to
131
+ * `Promise<void>`, so code that typed these methods as returning
132
+ * `Promise<void>` (interfaces, callbacks) keeps compiling, while callers can
133
+ * still read the result's fields.
134
+ */
135
+ type FlushResultCompat = void & FlushResult;
136
+
137
+ /** Payload kinds a field map can name: event items on `events/track` and
138
+ * `events/track_partial`, `signals/track` items and `users/identify` items. */
139
+ type PiiPayloadKind = "event" | "signal" | "identify";
140
+ /** Extra fields to redact, per payload kind (CONTRACT 11.2). */
141
+ type PiiFieldMap = {
142
+ [K in PiiPayloadKind]?: readonly string[];
143
+ };
101
144
  type Attachment = {
102
145
  type: string;
103
146
  role: string;
@@ -122,6 +165,14 @@ type Patch = {
122
165
  usage?: EventUsage;
123
166
  error?: unknown;
124
167
  properties?: Record<string, unknown>;
168
+ /**
169
+ * Properties the integrator supplied (setup defaults, per-call context, the
170
+ * manual events API), as opposed to keys an integration derives itself. On a
171
+ * key clash these win over `properties` and over the keys derived from
172
+ * `usage` / `error`, whatever order the updates are sent in (wrapper profile
173
+ * W6). Sent inside the event's `properties`; this is not a wire field.
174
+ */
175
+ callerProperties?: Record<string, unknown>;
125
176
  featureFlags?: Record<string, string>;
126
177
  attachments?: Attachment[];
127
178
  isPending?: boolean;
@@ -138,6 +189,9 @@ type SignalInput = {
138
189
  comment?: string;
139
190
  after?: string;
140
191
  };
192
+ type ResumeOptions = Pick<Patch, "userId" | "convoId" | "eventName">;
193
+ /** Fields `finish` accepts. */
194
+ type FinishFields = Pick<Patch, "output" | "model" | "usage" | "error" | "properties" | "callerProperties" | "featureFlags" | "userId">;
141
195
  type EventShipperOptions = {
142
196
  appGit?: AppGitOptions | false;
143
197
  /** Share with the trace shipper so one operation uses one source snapshot. */
@@ -171,6 +225,27 @@ type EventShipperOptions = {
171
225
  * Defaults to 1,000,000 (matching the Python SDK).
172
226
  */
173
227
  maxTextFieldChars?: number;
228
+ /**
229
+ * Redact PII (emails, phone numbers, card numbers, addresses, secrets) from
230
+ * event input and output before they leave the process. Off by default.
231
+ */
232
+ redactPii?: boolean;
233
+ /**
234
+ * More fields to redact when `redactPii` is on, per payload kind, as wire
235
+ * field paths (`{ event: ["properties.customer.email"], signal: ["comment"],
236
+ * identify: ["traits.email"] }`). Adds to the default `ai_data.input` and
237
+ * `ai_data.output`; never removes them (CONTRACT 11.2, see `pii-fields.ts`).
238
+ */
239
+ redactPiiFields?: PiiFieldMap;
240
+ /**
241
+ * Deadline in ms for `flush()` and `shutdown()`, retries and Retry-After
242
+ * waits included (default 10,000; CONTRACT 1.2.1). At the deadline a flush
243
+ * returns: payloads not yet sent stay queued for later, a request cut off
244
+ * mid-flight is not resent, and `shutdown()` drops what is left.
245
+ */
246
+ flushTimeoutMs?: number;
247
+ /** Tags every event with `raindrop.wizardSession` (set by the setup wizard). */
248
+ wizardSession?: string;
174
249
  };
175
250
  declare class EventShipper {
176
251
  private appGitContext;
@@ -190,7 +265,26 @@ declare class EventShipper {
190
265
  private sticky;
191
266
  private timers;
192
267
  private inFlight;
268
+ /** Updates queued with `enqueue` whose send has not settled yet. */
269
+ private background;
270
+ /** Background updates dropped since the last flush()/shutdown() result. */
271
+ private backgroundDrops;
272
+ /** Cuts off sends still running at a flush or shutdown deadline. */
273
+ private readonly cutOff;
193
274
  private maxTextFieldCharsOpt;
275
+ private redactPii;
276
+ private piiFields;
277
+ private wizardSession;
278
+ /**
279
+ * Events finished in this process, oldest first, capped at
280
+ * FINISHED_EVENT_MEMORY, so a repeated finish can be warned about.
281
+ */
282
+ private finishedEventIds;
283
+ /** The latest send per event; the next send for that event waits for it. */
284
+ private sendChains;
285
+ private flushTimeoutMs;
286
+ /** Signal/identify sends a flush deadline deferred; retried later. */
287
+ private deferredSends;
194
288
  /**
195
289
  * Epoch ms deadline while `shutdown()` is draining; undefined otherwise.
196
290
  * Checked before every POST issued during the final flush.
@@ -211,29 +305,120 @@ declare class EventShipper {
211
305
  private authHeaders;
212
306
  private requestHeaders;
213
307
  /**
214
- * Build the retry/timeout options for one POST, honoring the shutdown
215
- * deadline. Returns `null` when the shutdown drain window is exhausted —
216
- * the caller must drop the payload (with a rate-limited warning) instead
217
- * of issuing a request that could outlive process exit.
218
- *
219
- * Checked fresh on EVERY send, so a shutdown that begins while the flush
220
- * path is mid-drain takes effect immediately: no further retries, and the
221
- * per-attempt timeout is clamped to the remaining window. After
222
- * `shutdown()` returns (deadline cleared, `hasShutdown` still set),
223
- * sends — late callers, or flush work the deadline abandoned mid-drain —
224
- * run as a single short attempt rather than regaining the full retry
225
- * schedule.
308
+ * Retry/timeout options for one POST. While `shutdown()` drains, sends retry
309
+ * within its window (deadline = the shutdown deadline); a flush passes its
310
+ * own deadline. `null` when the shutdown window is spent: the caller drops
311
+ * the payload with a warning. After `shutdown()` returns, late sends get a
312
+ * single short attempt rather than the full retry schedule.
226
313
  */
227
314
  private requestOpts;
315
+ private shuttingDown;
228
316
  patch(eventId: string, patch: Patch): Promise<void>;
229
- finish(eventId: string, patch: Pick<Patch, "output" | "model" | "usage" | "error" | "properties" | "featureFlags" | "userId">): Promise<void>;
230
- flush(): Promise<void>;
231
- shutdown(): Promise<void>;
317
+ /**
318
+ * Queue an update without waiting for any network send: for integrations,
319
+ * whose captured call must return as soon as the update is queued (rule C9,
320
+ * hot path). A finishing update starts its send in the background; `flush()`
321
+ * and `shutdown()` wait for it within their deadline and count it in their
322
+ * result. Never throws or rejects.
323
+ */
324
+ enqueue(eventId: string, patch: Patch): void;
325
+ /** `finish()` without waiting for the send; see `enqueue`. */
326
+ enqueueFinish(eventId: string, patch?: FinishFields): void;
327
+ /**
328
+ * Caller properties win over everything the integration derived (W6). A
329
+ * clash is intended by the caller, so it is only logged in debug mode.
330
+ */
331
+ private withCallerProperties;
332
+ private warnBackgroundFailure;
333
+ /**
334
+ * Id of the last event an integration finished in this process (manual
335
+ * events API finishes excluded): what integrations expose as `lastEventId`
336
+ * (wrapper profile W10).
337
+ */
338
+ get lastFinishedEventId(): string | undefined;
339
+ private lastFinished;
340
+ private patchWithFate;
341
+ /**
342
+ * A second finish for an event is still sent (ingest finalizes once and
343
+ * treats it as an update, CONTRACT §10.2), but it usually means several
344
+ * calls share one event id by mistake, so it is warned about.
345
+ */
346
+ private noteFinish;
347
+ /**
348
+ * An event finished with no input, output or attachments is almost always a
349
+ * caller bug (CONTRACT §14.2, rule C5); it is sent anyway and warned about.
350
+ * Not for a resumed event: its content may have been sent by another process.
351
+ */
352
+ private noteEmptyFinish;
353
+ /**
354
+ * Continue an event that was begun elsewhere (another process, a queue
355
+ * worker, a later request) using only its id. No request is sent; later
356
+ * `patch` / `finish` calls for this id are sent even without a `userId`,
357
+ * and ingest keeps the user, conversation and start time it already has.
358
+ * Pass `userId` / `convoId` / `eventName` if you have them.
359
+ */
360
+ resume(eventId: string, options?: ResumeOptions): void;
361
+ finish(eventId: string, patch: FinishFields): Promise<void>;
362
+ /**
363
+ * Send everything buffered, open (pending) events included, within
364
+ * `flushTimeoutMs` (CONTRACT 1.2.1). Resolves, never rejects, to whether it
365
+ * all reached ingest; when it didn't, it also warns. At the deadline,
366
+ * payloads not yet sent stay queued for a later send and a request cut off
367
+ * mid-flight is dropped. Sends already running when it was called are
368
+ * waited for up to the deadline and then cut off.
369
+ */
370
+ flush(): Promise<FlushResultCompat>;
371
+ private drainAll;
372
+ /**
373
+ * Flush within `flushTimeoutMs` and stop. Sends retry while the window
374
+ * allows; whatever is left at the deadline is dropped with a warning, so a
375
+ * dead network can never wedge process exit. Resolves, never rejects.
376
+ */
377
+ shutdown(): Promise<FlushResultCompat>;
232
378
  trackSignal(signal: SignalInput): Promise<void>;
233
379
  identify(users: IdentifyInput | IdentifyInput[]): Promise<void>;
380
+ /**
381
+ * Send a signal or identify body. Tracked so `flush()` waits for it. A send
382
+ * a flush deadline deferred is kept and retried later (outside shutdown).
383
+ */
384
+ private sendDirect;
385
+ private deferSend;
386
+ private warnSendDropped;
387
+ /** The payload with its PII fields redacted when `redactPii` is on (CONTRACT 11.2). */
388
+ private redactFields;
234
389
  private warnShutdownDrop;
235
390
  private flushOne;
391
+ /**
392
+ * Put an unsent update back under any newer one and send it later
393
+ * (CONTRACT 1.2.1). During shutdown it is dropped instead.
394
+ */
395
+ private requeue;
396
+ private sendAccumulated;
236
397
  }
398
+
399
+ /** Fields `events.finish` accepts (the same set as `EventShipper.finish`). */
400
+ type FinishPatch = Parameters<EventShipper["finish"]>[1];
401
+ /**
402
+ * The manual event API every integration exposes as `client.events`. One
403
+ * implementation, so every package offers the same calls with the same
404
+ * behaviour (dawn docs/sdk-contract/sdk-plan.md, rules C2 and C11).
405
+ */
406
+ type EventsApi = {
407
+ /** Update an event: any subset of its fields. */
408
+ patch(eventId: string, patch: Patch): Promise<void>;
409
+ /** Finalize an event, optionally with its output and other final fields. */
410
+ finish(eventId: string, patch?: FinishPatch): Promise<void>;
411
+ addAttachments(eventId: string, attachments: Attachment[]): Promise<void>;
412
+ setProperties(eventId: string, properties: Record<string, unknown>): Promise<void>;
413
+ setFeatureFlags(eventId: string, featureFlags: FeatureFlags): Promise<void>;
414
+ /**
415
+ * Continue an event begun elsewhere (another process, a queue worker, a
416
+ * later request) using only its id. Later calls for this id are sent even
417
+ * without a `userId`; ingest keeps the user, conversation and start time it
418
+ * already has.
419
+ */
420
+ resume(eventId: string, options?: ResumeOptions): void;
421
+ };
237
422
  /**
238
423
  * Hook fired per OTLP span right before the span is shipped (to the Raindrop
239
424
  * API and to a local debugger). Lets callers inspect, rewrite, or drop the
@@ -270,6 +455,12 @@ type TraceShipperOptions = {
270
455
  flushIntervalMs?: number;
271
456
  maxBatchSize?: number;
272
457
  maxQueueSize?: number;
458
+ /**
459
+ * Deadline in ms for `flush()` and `shutdown()` (default 10,000; CONTRACT
460
+ * 1.2.1). Spans not yet sent at a flush deadline stay queued; a request cut
461
+ * off mid-flight is not resent; `shutdown()` drops what is left.
462
+ */
463
+ flushTimeoutMs?: number;
273
464
  sdkName?: string;
274
465
  serviceName?: string;
275
466
  serviceVersion?: string;
@@ -349,10 +540,13 @@ declare class TraceShipper {
349
540
  private flushIntervalMs;
350
541
  private maxBatchSize;
351
542
  private maxQueueSize;
543
+ private flushTimeoutMs;
352
544
  private projectId;
353
545
  private queue;
354
546
  private timer;
355
547
  private inFlight;
548
+ /** Cuts off sends still running at a flush or shutdown deadline. */
549
+ private readonly cutOff;
356
550
  /** URL of the local debugger / Workshop daemon, when one is reachable. */
357
551
  private localDebuggerUrl;
358
552
  private transformSpanHook;
@@ -438,12 +632,66 @@ declare class TraceShipper {
438
632
  status?: OtlpSpanStatus;
439
633
  }): void;
440
634
  enqueue(span: OtlpSpan): void;
441
- flush(): Promise<void>;
442
- /** See EventShipper.requestOpts — same shutdown-budget semantics. */
635
+ private scheduleDrain;
636
+ /**
637
+ * Send every queued span within `flushTimeoutMs` (CONTRACT 1.2.1). Resolves,
638
+ * never rejects, to whether they all reached ingest, and warns when they
639
+ * didn't. Spans not yet sent at the deadline stay queued.
640
+ */
641
+ flush(): Promise<FlushResultCompat>;
642
+ private drainWithin;
643
+ /** Send queued batches; with a deadline, a deferred batch goes back on the queue. */
644
+ private drain;
645
+ /** See EventShipper.requestOpts — same deadline semantics. */
443
646
  private requestOpts;
444
- shutdown(): Promise<void>;
647
+ private scheduleFor;
648
+ /**
649
+ * Flush within `flushTimeoutMs` and stop; spans left at the deadline are
650
+ * dropped with a warning. Resolves, never rejects.
651
+ */
652
+ shutdown(): Promise<FlushResultCompat>;
445
653
  }
446
654
 
655
+ /**
656
+ * The client options every integration accepts, with one meaning everywhere.
657
+ * Integration option types extend this; their own options stay beside it.
658
+ * (Each field also takes `undefined`, for `exactOptionalPropertyTypes` users.)
659
+ */
660
+ type RaindropClientOptions = {
661
+ /** Raindrop write key. Without one (and no local Workshop) nothing is sent. */
662
+ writeKey?: string | undefined;
663
+ /** Ingest base URL. Defaults to `https://api.raindrop.ai/v1`. */
664
+ endpoint?: string | undefined;
665
+ /** Project slug, sent as `X-Raindrop-Project-Id`. Unset sends no header. */
666
+ projectId?: string | undefined;
667
+ /** Log telemetry activity to the console. */
668
+ debug?: boolean | undefined;
669
+ /** Application Git identity. Auto-detects the commit SHA by default; `false` turns it off. */
670
+ appGit?: AppGitOptions | false | undefined;
671
+ /**
672
+ * Per-field character cap for captured text. Longer values end with
673
+ * `...[truncated by raindrop]`. Defaults to 1,000,000.
674
+ */
675
+ maxTextFieldChars?: number | undefined;
676
+ /**
677
+ * Redact PII (emails, phone numbers, card numbers, addresses, secrets) from
678
+ * event input and output before it leaves the process. Off by default.
679
+ */
680
+ redactPii?: boolean | undefined;
681
+ /**
682
+ * More fields to redact when `redactPii` is on, per payload kind, as wire
683
+ * field paths (`{ event: ["properties.customer.email"] }`). Adds to the
684
+ * default `ai_data.input` and `ai_data.output`.
685
+ */
686
+ redactPiiFields?: PiiFieldMap | undefined;
687
+ /**
688
+ * Deadline in ms for `flush()` and `shutdown()`, retries included (default
689
+ * 10,000). At the deadline they return: unsent payloads stay queued (a
690
+ * shutdown drops them) and a request cut off mid-flight is not resent.
691
+ */
692
+ flushTimeoutMs?: number | undefined;
693
+ };
694
+
447
695
  /**
448
696
  * Run telemetry egress with OpenTelemetry tracing suppressed.
449
697
  *
@@ -543,7 +791,7 @@ type SessionContext = {
543
791
  */
544
792
  tools?: PromptToolsOverride | undefined;
545
793
  };
546
- type OpenAIManagedAgentsOptions = SessionContext & {
794
+ type OpenAIManagedAgentsOptions = SessionContext & RaindropClientOptions & {
547
795
  writeKey?: string | undefined;
548
796
  endpoint?: string | undefined;
549
797
  projectId?: string | undefined;
@@ -585,20 +833,21 @@ declare class RaindropManagedAgentsHandler {
585
833
  type RaindropOpenAIManagedAgentsClient = {
586
834
  handler: RaindropManagedAgentsHandler;
587
835
  wrap(client: OpenAI, context?: SessionContext): OpenAI;
588
- events: {
589
- patch(eventId: string, patch: Patch): Promise<void>;
590
- finish(eventId: string, patch: Pick<Patch, "output" | "model" | "properties">): Promise<void>;
591
- addAttachments(eventId: string, attachments: Attachment[]): Promise<void>;
592
- setProperties(eventId: string, properties: NonNullable<Patch["properties"]>): Promise<void>;
593
- };
836
+ /**
837
+ * Event id of the last turn this integration finished, or `undefined`
838
+ * before the first. Use it to attach a signal or feedback to that turn.
839
+ * Finishes made through `events.*` don't change it.
840
+ */
841
+ readonly lastEventId: string | undefined;
842
+ events: EventsApi;
594
843
  users: {
595
844
  identify(users: IdentifyInput | IdentifyInput[]): Promise<void>;
596
845
  };
597
846
  signals: {
598
847
  track(signal: SignalInput): Promise<void>;
599
848
  };
600
- flush(): Promise<void>;
601
- shutdown(): Promise<void>;
849
+ flush(): Promise<FlushResultCompat>;
850
+ shutdown(): Promise<FlushResultCompat>;
602
851
  };
603
852
  declare function createRaindropOpenAIManagedAgents(opts?: OpenAIManagedAgentsOptions): RaindropOpenAIManagedAgentsClient;
604
853