@runtypelabs/flue-otel 0.4.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -1,41 +1,5 @@
1
1
  import { Tracer } from '@opentelemetry/api';
2
2
 
3
- /**
4
- * The Flue observation surface this package reads, declared structurally.
5
- *
6
- * ## Why these are hand-declared rather than imported from `@flue/runtime`
7
- *
8
- * Two reasons, and the second is the load-bearing one.
9
- *
10
- * 1. `@flue/runtime` is a PEER dependency with no dev counterpart: its tree is
11
- * ~43 packages, and pulling it into the root lockfile for type resolution
12
- * alone is the cost this monorepo already refuses for `examples/flue-persona`
13
- * (root `CLAUDE.md`, "examples/* is deliberately NOT a pnpm workspace").
14
- *
15
- * 2. The package supports BOTH the 1.x and 2.x lines from one entry point, and
16
- * their declarations are not the same type. 1.x has `run_start`/`run_end`,
17
- * envelope `runId`/`dispatchId`, and `FlueObservationDetail.toolType`; 2.x
18
- * removes all of those and adds `toolcall_delta`, the `submission_*` family,
19
- * and `FlueErrorInfo.meta`/`.stack`. Importing EITHER line's types would make
20
- * the compiler enforce a shape the other line does not have. What this
21
- * package actually consumes is the INTERSECTION — the observation plane Flue
22
- * publishes as stable — so that is what is declared here.
23
- *
24
- * ## What is safe to read
25
- *
26
- * Flue's published stability boundary (flueframework.com/docs/reference/events)
27
- * covers the event type names, the envelope/correlation fields, and the
28
- * normalized `turn_request` / `turn` / `tool_*` / `task` / `operation` /
29
- * `compaction` / `log` / `submission_settled` payloads. It EXPLICITLY excludes
30
- * `AgentMessage`, which appears on `message_start` / `message_end` /
31
- * `turn_messages` / `agent_end` — so none of those four are read here, and the
32
- * type below does not even name the field. That exclusion is why the stock
33
- * projection survived the 1.x → 2.x rewrite unchanged, and it is the same
34
- * reason this one will.
35
- *
36
- * Fields the two lines disagree on are declared OPTIONAL and probed at runtime
37
- * (`compat.ts`), never version-compared.
38
- */
39
3
  /** Correlation fields stamped onto every delivered observation. */
40
4
  interface FlueEventEnvelope {
41
5
  /** Durable event-format version. `3` on both supported lines. */
@@ -82,12 +46,13 @@ interface FlueModelRequestInfo {
82
46
  contextCompacted?: true;
83
47
  }
84
48
  /**
85
- * The request's payload half. Only `tools` is read, and only for its PRESENCE:
86
- * `runtype.tools.reported` is the assertion "my tool set is complete", which
87
- * needs to know the array exists, not what is in it. Message content is
88
- * deliberately untouched in this release — see the README's content section.
49
+ * The request's payload half. `tools` is read for its PRESENCE only (`runtype.tools.reported`).
50
+ * `messages` and `systemPrompt` are Flue's normalized `LlmMessage` history and system prompt,
51
+ * byte-identical on both lines, read only when message content is opted in — see `messages.ts`.
89
52
  */
90
53
  interface FlueModelRequestInput {
54
+ systemPrompt?: string;
55
+ messages?: unknown[];
91
56
  tools?: unknown[];
92
57
  }
93
58
  interface FlueModelRequest extends FlueModelRequestInfo {
@@ -96,6 +61,8 @@ interface FlueModelRequest extends FlueModelRequestInfo {
96
61
  interface FlueModelResponse {
97
62
  responseId?: string;
98
63
  responseModel?: string;
64
+ /** The assistant message this turn produced, in Flue's normalized `LlmAssistantMessage` shape. */
65
+ output?: unknown;
99
66
  usage?: FluePromptUsage;
100
67
  finishReason?: string;
101
68
  /** 2.x only. The provider's raw finish value before normalization. */
@@ -110,13 +77,8 @@ interface FlueModelResponse {
110
77
  error?: FlueErrorInfo;
111
78
  }
112
79
  /**
113
- * Classified error details on a live observation.
114
- *
115
- * `stack` is declared so the type is honest about what arrives, and is NEVER
116
- * read: it exposes filesystem paths and deployment layout, which is exactly the
117
- * class of value a vendor package landing in a healthcare stack must not put on
118
- * a wire the customer did not opt into. Flue's own durable serializers drop it
119
- * for the same reason.
80
+ * Classified error details. stack is described but never read or exported because it can expose
81
+ * deployment paths.
120
82
  */
121
83
  interface FlueErrorInfo {
122
84
  type?: string;
@@ -276,58 +238,21 @@ interface FlueInstrumentation {
276
238
  }
277
239
 
278
240
  /**
279
- * Opt-in tool content: `gen_ai.tool.call.arguments` and
280
- * `gen_ai.tool.call.result` on `execute_tool` spans.
281
- *
282
- * PURE, like `projection.ts`: value in, attribute string out. The projection
283
- * decides WHEN to call this (arguments when the tool span opens, the result
284
- * just before it closes); this module decides WHAT reaches the wire and how
285
- * much of it.
286
- *
287
- * ## Why opt-in, and why only tools
288
- *
289
- * The package's default contract is that no content leaves the process, and
290
- * that stays the default: an instrumentation that lands inside a PHI-bearing
291
- * process must not start shipping tool payloads because of a version bump.
292
- * The `content` option is the explicit decision, and it is per kind, so a
293
- * deployment can export the arguments it controls without the results a
294
- * third-party API returns.
295
- *
296
- * Tool content is the one kind this release can emit honestly. Flue declares
297
- * `tool_start.args` and `tool.result` stable, so both are on the same payloads
298
- * the span lifecycle already reads. Prompts and completions are NOT here:
299
- * they live on `AgentMessage`, which Flue marks unstable and this package
300
- * refuses to read (see `flue-types.ts`).
301
- *
302
- * ## The read-side contract this honours
303
- *
304
- * Runtype's ingest reads exactly these two attributes off an `execute_tool`
305
- * span (`packages/shared/src/otlp-runtype-semconv.ts`,
306
- * `GEN_AI_CONTENT_ATTRIBUTES`) and projects them to the tool card's input and
307
- * output. The value is a STRING: a JSON document is parsed back, anything
308
- * that does not parse is kept as plain text. It is admitted under a budget
309
- * (`packages/execution-ingest/src/ingest-content.ts`): a single attribute over
310
- * `MAX_CONTENT_ATTRIBUTE_CHARS` (256 KiB) is dropped WHOLE, not cut, and one
311
- * span may carry at most `MAX_CONTENT_SPAN_CHARS` (384 KiB) across every
312
- * content attribute. Both ceilings are why `maxChars` exists and why its
313
- * default sits where it does: arguments plus result at the default each fit
314
- * inside the per-span ceiling with room to spare, and a value this module has
315
- * already cut can never trip the per-attribute drop.
316
- *
317
- * The canonical `gen_ai.tool.call.*` names are emitted for every value,
318
- * string or not. Stock `@flue/opentelemetry` routes a stringified non-object
319
- * to `flue.tool.call.*` instead; ingest reads that alias only as a fallback,
320
- * and the semconv attribute type is a string either way, so there is nothing
321
- * to gain from the split.
322
- *
323
- * The two kinds are encoded differently because ingest projects them
324
- * differently: a RESULT is any text (a string as-is, anything else as JSON,
325
- * cut in place when over budget), while ARGUMENTS are always a JSON object,
326
- * see {@link encodeArguments}.
241
+ * One semconv structured message, the element shape of `gen_ai.input.messages`
242
+ * and `gen_ai.output.messages`. Text parts only; see the README's content section.
327
243
  */
244
+ interface FlueContentMessage {
245
+ role: 'system' | 'user' | 'assistant';
246
+ parts: Array<{
247
+ type: 'text';
248
+ content: string;
249
+ }>;
250
+ }
251
+
328
252
  /**
329
- * What to emit, per tool span. Every field is optional and every default is
330
- * OFF; passing `{}` changes nothing.
253
+ * What to emit. Every switch defaults to ON, so `{}` and an absent option both
254
+ * emit everything; set a switch to `false` to drop that one kind, or pass
255
+ * `content: false` to emit no content at all.
331
256
  */
332
257
  interface FlueContentOptions {
333
258
  /**
@@ -336,40 +261,51 @@ interface FlueContentOptions {
336
261
  */
337
262
  toolArguments?: boolean;
338
263
  /**
339
- * Emit the tool's return value as `gen_ai.tool.call.result` just before the
340
- * `execute_tool` span closes. Read from Flue's stable `tool.result` (2.x's
341
- * `effectiveResult` — what the model was actually shown — when present).
342
- *
343
- * A FAILED tool's result is never emitted: on both Flue lines it commonly
344
- * IS the error payload, and error messages stay off the wire under every
345
- * setting of this option. The span still closes with its error type and
346
- * exception class name, as before.
264
+ * Emit successful tool results when the span closes; failed-tool payloads stay off the wire. Prefer
265
+ * effectiveResult when present, including undefined; otherwise use result.
347
266
  */
348
267
  toolResults?: boolean;
349
268
  /**
350
- * Ceiling on ONE emitted value, in UTF-16 code units, marker included.
351
- * Defaults to {@link DEFAULT_CONTENT_MAX_CHARS}. A longer value is cut and
352
- * ends in an explicit `…[truncated N chars]` marker, so a cut value never
353
- * masquerades as complete.
354
- *
355
- * Clamped to {@link INGEST_CONTENT_ATTRIBUTE_CEILING}: above that, Runtype's
356
- * ingest drops the attribute whole, which is strictly worse than a cut.
269
+ * Emit the conversation the run's first model turn was given as `gen_ai.input.messages` on the
270
+ * `invoke_agent` span: text-only user and assistant rows from Flue's normalized
271
+ * `turn_request.request.input.messages`. Later turns of the same run only add tool traffic.
272
+ */
273
+ inputMessages?: boolean;
274
+ /**
275
+ * Emit the run's final assistant text as `gen_ai.output.messages` on the `invoke_agent` span, read
276
+ * from the last agent turn whose `turn.response.output` carried text.
277
+ */
278
+ outputMessages?: boolean;
279
+ /**
280
+ * Emit the system prompt as `gen_ai.system_instructions` on the `invoke_agent` span, read from
281
+ * `turn_request.request.input.systemPrompt`. Usually the largest value a run carries; mind `maxChars`.
282
+ */
283
+ systemInstructions?: boolean;
284
+ /**
285
+ * Per-value UTF-16 code-unit ceiling, including the truncation marker. Defaults to
286
+ * DEFAULT_CONTENT_MAX_CHARS and clamps to INGEST_CONTENT_ATTRIBUTE_CEILING, above which ingest drops
287
+ * the whole attribute.
357
288
  */
358
289
  maxChars?: number;
359
290
  /**
360
- * Rewrite a value before it is encoded. Runs on the RAW value, before
361
- * stringification and truncation, so a redaction can address fields by name.
362
- * Return `undefined` to suppress the attribute for this call entirely.
363
- *
364
- * An exception thrown here suppresses the attribute and nothing else: the
365
- * span still opens and closes normally.
291
+ * Redact the raw value before encoding or truncation. Returning undefined or throwing suppresses only
292
+ * this attribute, not the span.
366
293
  */
367
294
  redact?: (value: unknown, context: FlueContentRedactContext) => unknown;
368
295
  }
369
- interface FlueContentRedactContext {
296
+ /**
297
+ * What `redact` is looking at. Tool kinds receive the raw Flue payload; `input_messages` and
298
+ * `output_messages` receive the projected {@link FlueContentMessage} array; `system_instructions`
299
+ * receives the prompt string.
300
+ */
301
+ type FlueContentRedactContext = {
370
302
  kind: 'arguments' | 'result';
371
303
  toolName: string;
372
- }
304
+ } | {
305
+ kind: 'input_messages' | 'output_messages' | 'system_instructions';
306
+ toolName?: undefined;
307
+ };
308
+ type FlueMessageContentKind = 'input_messages' | 'output_messages';
373
309
  /**
374
310
  * The default per-value ceiling: 64 KiB of ASCII. Large enough for any
375
311
  * realistic tool argument object and most results, and small enough that
@@ -378,67 +314,12 @@ interface FlueContentRedactContext {
378
314
  */
379
315
  declare const DEFAULT_CONTENT_MAX_CHARS = 65536;
380
316
  /**
381
- * Runtype ingest's per-attribute ceiling (`MAX_CONTENT_ATTRIBUTE_CHARS` in
382
- * `packages/execution-ingest/src/ingest-content.ts`), inlined for the same
383
- * reason `semconv.ts` inlines the attribute names — that package is private
384
- * and cannot resolve for a customer installing from npm. `tests/contract.test.ts`
385
- * pins the two equal.
317
+ * Inline the private ingest per-attribute ceiling so npm installs need no private dependency.
318
+ * tests/contract.test.ts pins equality.
386
319
  */
387
320
  declare const INGEST_CONTENT_ATTRIBUTE_CEILING = 262144;
388
-
389
- /**
390
- * Flue observations → span intents. PURE: no `@opentelemetry/api`, no clock, no
391
- * I/O. `spans.ts` is the only module that touches a `Tracer`.
392
- *
393
- * The split is what makes the emit contract testable. Every attribute this
394
- * package puts on the wire is decided here, from a synthesized observation, and
395
- * asserted in `tests/projection.test.ts` against both the 1.x and 2.x shapes —
396
- * so a mapping regression is a unit-test failure rather than something only a
397
- * live export against a real agent would show.
398
- *
399
- * ## What is emitted, and why exactly this
400
- *
401
- * The read side is `packages/execution-ingest/src/otel-trace-ingest-service.ts`.
402
- * What it reads is this module's specification, so each choice below is a
403
- * consequence of a rule stated there:
404
- *
405
- * - **`invoke_agent` is the ENVELOPE and there is exactly one per trace.**
406
- * `selectEnvelopeSpan` takes the first ARRAY-order `invoke_agent` span, and
407
- * array order is exporter-controlled. A second one — which stock
408
- * `@flue/opentelemetry` opens for every `task` delegation — is therefore a
409
- * coin flip over which invocation bounds the run, whose status closes it, and
410
- * (via `resolveTraceAgentId`'s span-level source) which agent it files under.
411
- * So a delegated sub-agent gets a `flue.task` span with NO
412
- * `gen_ai.operation.name`, and the delegation itself is reported where it
413
- * actually belongs: as the `execute_tool` span for the `task` tool, typed
414
- * `subagent`. Stock does the opposite — it suppresses that tool span and
415
- * opens the nested `invoke_agent` — which is the one place this projection
416
- * deliberately diverges from it.
417
- *
418
- * - **Usage is rolled up ONTO the envelope, summed from `turn` leaves.**
419
- * `projectUsage` prefers an envelope roll-up precisely because a
420
- * `BatchSpanProcessor` flushes children before their parent, so the batch
421
- * carrying the terminal may hold no model calls at all. Flue's own docs say
422
- * to sum model-turn leaves rather than the `operation` roll-up, because
423
- * nested duration and usage values overlap. Both point the same way: sum
424
- * `turn.response.usage`, put the total on `invoke_agent`.
425
- *
426
- * - **The envelope carries the model.** Stock puts none there, which left the
427
- * envelope's log record showing no model at all; the server-side normalizer
428
- * now lifts one, but only for traces it recognizes as Flue's. Emitting it
429
- * directly is F1 and costs one attribute. Both ids come off the SAME turn, so
430
- * the request-model fallback can never price one call's served model against
431
- * another's requested one.
432
- *
433
- * - **`runtype.turn.index` / `runtype.iteration` ARE derived here, and are not
434
- * derivable at ingest.** The server-side normalizer deliberately refuses
435
- * them: Flue puts no absolute ordinal on the wire, and ranking opaque turn
436
- * ids ranks whatever the export batch happened to carry — a five-turn run
437
- * whose closing batch holds two turns would be recorded as two iterations.
438
- * In-process the count is exact, because we see every `turn_request` in
439
- * order. This is the clearest thing this package buys that a normalizer
440
- * structurally cannot.
441
- */
321
+ /** The resolved, always-complete policy the projection consults. */
322
+ type FlueContentSetting = FlueContentOptions | false;
442
323
 
443
324
  /** Attribute values OTLP can carry. Deliberately narrower than OTel's type. */
444
325
  type SpanAttributes = Record<string, string | number | boolean | string[]>;
@@ -461,20 +342,8 @@ interface OpenSpanIntent {
461
342
  startTime?: string;
462
343
  attributes: SpanAttributes;
463
344
  /**
464
- * Drop this span entirely rather than start it as the root of a new trace.
465
- *
466
- * Set on framework bookkeeping that only means anything INSIDE a run —
467
- * `flue.operation shell` and `flue.compaction`. Both can occur outside one:
468
- * `session.shell()` and `session.compact()` are public host APIs, and Flue
469
- * intercepts only `prompt` and `skill` operations, so neither has a parent
470
- * span or an active context to inherit.
471
- *
472
- * Started as a root, such a span becomes the ONLY span in its trace — and
473
- * ingest's `selectEnvelopeSpan` falls back to the first parentless span when
474
- * a trace carries no agent invocation, so it would be read as the envelope
475
- * of a run and write a phantom execution row for every host-initiated shell
476
- * command. This is the same reasoning `index.ts` applies to `coordinator`
477
- * operations, applied to the other two paths that reach the same state.
345
+ * Drop bookkeeping spans without a parent: ingest could otherwise treat a host shell or compaction
346
+ * span as a phantom run envelope.
478
347
  */
479
348
  requiresParent?: boolean;
480
349
  }
@@ -488,11 +357,8 @@ interface CloseSpanIntent {
488
357
  ref: string;
489
358
  endTime?: string;
490
359
  /**
491
- * Present when the span failed. Carries the error TYPE and the exception
492
- * class NAME only — never a message and never a stack. Both are content: a
493
- * provider error message routinely quotes the prompt back, and a stack
494
- * exposes filesystem paths and deployment layout. Neither is read under any
495
- * setting; the `content` opt-in covers tool payloads only.
360
+ * Failure type and exception class only. Error messages and stacks can expose prompts or deployment
361
+ * paths and are excluded even when tool-content export is enabled.
496
362
  */
497
363
  error?: {
498
364
  type: string;
@@ -502,26 +368,17 @@ interface CloseSpanIntent {
502
368
  type SpanIntent = OpenSpanIntent | UpdateSpanIntent | CloseSpanIntent;
503
369
  interface FlueProjectionOptions {
504
370
  /**
505
- * Runtype agent id per Flue agent name, e.g. `{ triage: 'agent_01j...' }`.
506
- *
507
- * Stamped on the ENVELOPE span as `runtype.agent.id` — the ratified second
508
- * attribution placement, for the one shape a `Resource` cannot express: a
509
- * single process running several Runtype agents. A single-agent deployment
510
- * should set the resource attribute instead (see
511
- * {@link runtypeFlueResourceAttributes} and the README recipe), which the
512
- * ingest reader prefers.
513
- *
514
- * A delegated sub-agent is deliberately NOT attributed separately: its work
515
- * runs inside the delegating agent's trace, and one trace is one execution.
371
+ * Agent id by Flue agent name, stamped on the envelope. Resource attribution takes precedence;
372
+ * delegated subagents are not separate executions. Use runtypeFlueResourceAttributes for single-agent
373
+ * processes.
516
374
  */
517
375
  agents?: Record<string, string>;
518
376
  /**
519
- * Opt in to tool content on `execute_tool` spans. Default: nothing. See
520
- * {@link FlueContentOptions} for the per-kind switches, the size ceiling
521
- * and the redaction hook, and `content.ts` for the read-side contract they
522
- * honour.
377
+ * Content: tool arguments and results on `execute_tool` spans, messages and the system prompt on
378
+ * the `invoke_agent` envelope. All ON by default; set a switch to `false` to drop one kind, or
379
+ * `content: false` for none. See {@link FlueContentOptions} for the size ceiling and redaction hook.
523
380
  */
524
- content?: FlueContentOptions;
381
+ content?: FlueContentSetting;
525
382
  }
526
383
  /**
527
384
  * Resource attributes for the customer's `Resource`. Exported rather than set
@@ -533,28 +390,6 @@ declare function runtypeFlueResourceAttributes(resource?: {
533
390
  agentId?: string | null;
534
391
  }): SpanAttributes;
535
392
 
536
- /**
537
- * The attribute vocabulary this instrumentation emits: the GenAI semantic
538
- * conventions plus Runtype's ratified `runtype.*` extension names.
539
- *
540
- * ## Why the literals are inlined rather than imported
541
- *
542
- * The canonical home for the `runtype.*` names is
543
- * `packages/shared/src/otlp-runtype-semconv.ts`, and this module is a
544
- * CONSUMER of that contract, never a fork of it. But `@runtypelabs/shared` is
545
- * `private: true` and is not published to npm, while this package is — so a
546
- * runtime import would resolve in the monorepo and fail for every customer who
547
- * installs `@runtypelabs/flue-otel` from the registry.
548
- *
549
- * The discipline that keeps a copy from becoming a fork is
550
- * `tests/contract.test.ts`: a DEV-ONLY cross-import that asserts every literal
551
- * here is identical to the shared module's. A rename on either side is a red
552
- * build, in the same PR, which is the only mechanism that actually holds. It is
553
- * the same posture `examples/flue-persona` uses for the unified SSE vocabulary.
554
- *
555
- * Exact strings are load-bearing: a misspelled attribute fails no build — it
556
- * silently empties a column of every ingested run.
557
- */
558
393
  /**
559
394
  * GenAI semantic-convention attribute names. Mirrors the subset of
560
395
  * `packages/shared/src/gen-ai-semconv.ts` this instrumentation can populate
@@ -586,14 +421,14 @@ declare const GEN_AI: {
586
421
  readonly serverPort: "server.port";
587
422
  };
588
423
  /**
589
- * GenAI semconv CONTENT attribute names — the semconv Opt-In set. Kept apart
590
- * from {@link GEN_AI} on purpose: that table is what this package emits by
591
- * DEFAULT, and the contract test pins that no content name is in it. These
592
- * two reach the wire only under the `content` option (`content.ts`), and only
593
- * on `execute_tool` spans. Mirrors `GEN_AI_CONTENT_ATTRIBUTES` in
594
- * `packages/shared/src/otlp-runtype-semconv.ts`, which is what ingest reads.
424
+ * Content attribute names, separate from the structural GEN_AI set. Tool content goes on
425
+ * `execute_tool` spans, message content on the `invoke_agent` envelope; each kind can be switched
426
+ * off through the content options. Mirrored in shared ingest semconv.
595
427
  */
596
428
  declare const GEN_AI_CONTENT: {
429
+ readonly inputMessages: "gen_ai.input.messages";
430
+ readonly outputMessages: "gen_ai.output.messages";
431
+ readonly systemInstructions: "gen_ai.system_instructions";
597
432
  readonly toolCallArguments: "gen_ai.tool.call.arguments";
598
433
  readonly toolCallResult: "gen_ai.tool.call.result";
599
434
  };
@@ -640,52 +475,6 @@ declare const ADAPTER_NAME = "@runtypelabs/flue-otel";
640
475
  /** What this package reports itself as in `runtype.adapter.version`. */
641
476
  declare const ADAPTER_VERSION: string;
642
477
 
643
- /**
644
- * `@runtypelabs/flue-otel` — Runtype's OpenTelemetry instrumentation for Flue
645
- * agents.
646
- *
647
- * Install it with Flue's `instrument()` and every agent run becomes a trace of
648
- * GenAI-semconv spans carrying Runtype's `runtype.*` extension vocabulary, so
649
- * the run lands in Runtype at `t2-runtype` fidelity — full parity with a native
650
- * run — instead of the generic tier a stock export reaches.
651
- *
652
- * ```ts
653
- * import { instrument } from '@flue/runtime'
654
- * import { createRuntypeFlueInstrumentation } from '@runtypelabs/flue-otel'
655
- *
656
- * const stop = instrument(createRuntypeFlueInstrumentation())
657
- * ```
658
- *
659
- * ## What it is not
660
- *
661
- * It owns **no** OpenTelemetry SDK. There is no `@opentelemetry/sdk-*`
662
- * dependency, no provider, no exporter, no sampler, no flush. The application
663
- * configures those, this package writes spans through whatever is registered,
664
- * and the README carries the twelve-line recipe for a process that has none
665
- * yet. That is deliberate: a vendor package that installs its own tracing
666
- * pipeline fights the one the customer already runs, and in a stack exporting
667
- * to two backends it silently wins one of those fights.
668
- *
669
- * It also emits **no content by default** — no prompts, no completions, no
670
- * tool arguments or results, no error messages, no stack traces. Only
671
- * identifiers, structure and metrics reach the wire, which is the right
672
- * default for a package landing inside a PHI-bearing process. The one
673
- * explicit opt-in is `content`: tool arguments and results on `execute_tool`
674
- * spans, per kind, size-capped and redactable (`content.ts`). Prompts and
675
- * completions remain off the table until Flue exposes a stable message shape.
676
- *
677
- * ## Composing with other instrumentations
678
- *
679
- * Flue's `instrument()` composes — an error reporter and a tracer can subscribe
680
- * side by side — and this instrumentation carries its own `key`, so installing
681
- * it never replaces `@flue/opentelemetry`.
682
- *
683
- * **Point exactly one instrumentation at Runtype.** If both this package and a
684
- * stock `@flue/opentelemetry` export to the same Runtype endpoint, ingest sees
685
- * two `invoke_agent` spans for one run and the usage DOUBLES. Running both is
686
- * fine when they export to different backends.
687
- */
688
-
689
478
  /**
690
479
  * The instrumentation key. Distinct from stock's
691
480
  * `Symbol.for('@flue/opentelemetry')` so `instrument()` treats the two as
@@ -702,17 +491,10 @@ interface RuntypeFlueInstrumentationOptions extends FlueProjectionOptions {
702
491
  tracer?: Tracer;
703
492
  }
704
493
  /**
705
- * Build the instrumentation. Install it with Flue's `instrument()`, which
706
- * returns a disposer.
707
- *
708
- * The returned object implements the FULL `FlueInstrumentation` contract, not
709
- * just `observe`. The `interceptor` half is not optional in practice: it is
710
- * what makes each span the OTel ACTIVE context around the real agent, model and
711
- * tool work, so the platform's own HTTP and database spans nest inside the run
712
- * instead of landing in a separate trace — and it is the only place Flue offers
713
- * `executionContext.traceCarrier`, the W3C context that joins a dispatched run
714
- * to the trace that started it.
494
+ * Build an observe/interceptor pair for Flue's instrument(). The interceptor activates spans and joins
495
+ * dispatched trace carriers. Configure the OTel SDK externally, and point only one instrumentation at
496
+ * Runtype to avoid double counting.
715
497
  */
716
498
  declare function createRuntypeFlueInstrumentation(options?: RuntypeFlueInstrumentationOptions): FlueInstrumentation;
717
499
 
718
- export { ADAPTER_NAME, ADAPTER_VERSION, DEFAULT_CONTENT_MAX_CHARS, type FlueContentOptions, type FlueContentRedactContext, type FlueInstrumentation, type FlueProjectionOptions, GEN_AI, GEN_AI_CONTENT, INGEST_CONTENT_ATTRIBUTE_CEILING, RUNTYPE, RUNTYPE_FLUE_INSTRUMENTATION_KEY, RUNTYPE_SCHEMA_VERSION, RUNTYPE_STOP_REASONS, RUNTYPE_TOOL_TYPES, type RuntypeFlueInstrumentationOptions, type RuntypeStopReason, type RuntypeToolType, type SpanAttributes, type SpanIntent, createRuntypeFlueInstrumentation, runtypeFlueResourceAttributes };
500
+ export { ADAPTER_NAME, ADAPTER_VERSION, DEFAULT_CONTENT_MAX_CHARS, type FlueContentMessage, type FlueContentOptions, type FlueContentRedactContext, type FlueContentSetting, type FlueInstrumentation, type FlueMessageContentKind, type FlueProjectionOptions, GEN_AI, GEN_AI_CONTENT, INGEST_CONTENT_ATTRIBUTE_CEILING, RUNTYPE, RUNTYPE_FLUE_INSTRUMENTATION_KEY, RUNTYPE_SCHEMA_VERSION, RUNTYPE_STOP_REASONS, RUNTYPE_TOOL_TYPES, type RuntypeFlueInstrumentationOptions, type RuntypeStopReason, type RuntypeToolType, type SpanAttributes, type SpanIntent, createRuntypeFlueInstrumentation, runtypeFlueResourceAttributes };