@ai-agent-forge/plugin-sdk 0.85.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.
Files changed (58) hide show
  1. package/README.md +27 -0
  2. package/dist/artifact.d.ts +20 -0
  3. package/dist/artifact.d.ts.map +1 -0
  4. package/dist/artifact.js +63 -0
  5. package/dist/artifact.js.map +1 -0
  6. package/dist/capability-manifest.d.ts +11 -0
  7. package/dist/capability-manifest.d.ts.map +1 -0
  8. package/dist/capability-manifest.js +188 -0
  9. package/dist/capability-manifest.js.map +1 -0
  10. package/dist/common.d.ts +24 -0
  11. package/dist/common.d.ts.map +1 -0
  12. package/dist/common.js +115 -0
  13. package/dist/common.js.map +1 -0
  14. package/dist/context-budget.d.ts +33 -0
  15. package/dist/context-budget.d.ts.map +1 -0
  16. package/dist/context-budget.js +124 -0
  17. package/dist/context-budget.js.map +1 -0
  18. package/dist/diff.d.ts +38 -0
  19. package/dist/diff.d.ts.map +1 -0
  20. package/dist/diff.js +164 -0
  21. package/dist/diff.js.map +1 -0
  22. package/dist/durable.d.ts +108 -0
  23. package/dist/durable.d.ts.map +1 -0
  24. package/dist/durable.js +15 -0
  25. package/dist/durable.js.map +1 -0
  26. package/dist/ecosystem-manifest.d.ts +56 -0
  27. package/dist/ecosystem-manifest.d.ts.map +1 -0
  28. package/dist/ecosystem-manifest.js +11 -0
  29. package/dist/ecosystem-manifest.js.map +1 -0
  30. package/dist/index.d.ts +13 -0
  31. package/dist/index.d.ts.map +1 -0
  32. package/dist/index.js +13 -0
  33. package/dist/index.js.map +1 -0
  34. package/dist/memory.d.ts +370 -0
  35. package/dist/memory.d.ts.map +1 -0
  36. package/dist/memory.js +26 -0
  37. package/dist/memory.js.map +1 -0
  38. package/dist/observability.d.ts +354 -0
  39. package/dist/observability.d.ts.map +1 -0
  40. package/dist/observability.js +33 -0
  41. package/dist/observability.js.map +1 -0
  42. package/dist/operation.d.ts +60 -0
  43. package/dist/operation.d.ts.map +1 -0
  44. package/dist/operation.js +202 -0
  45. package/dist/operation.js.map +1 -0
  46. package/dist/public-api.d.ts +1977 -0
  47. package/dist/public-api.d.ts.map +1 -0
  48. package/dist/public-api.js +237 -0
  49. package/dist/public-api.js.map +1 -0
  50. package/dist/render.d.ts +38 -0
  51. package/dist/render.d.ts.map +1 -0
  52. package/dist/render.js +112 -0
  53. package/dist/render.js.map +1 -0
  54. package/dist/session-lineage.d.ts +36 -0
  55. package/dist/session-lineage.d.ts.map +1 -0
  56. package/dist/session-lineage.js +134 -0
  57. package/dist/session-lineage.js.map +1 -0
  58. package/package.json +55 -0
@@ -0,0 +1,354 @@
1
+ /**
2
+ * Public contracts for the first-party observability plugin (event journal +
3
+ * problem reports + trace sink). The plugin package
4
+ * (`@agent-forge/plugin-observability`) is the implementation; this module is
5
+ * the single authoring authority for its public types and the host-facing
6
+ * trace adapter contract. The host re-exports the surface from its historical
7
+ * `plugins/observability-types.ts` path so existing imports keep resolving.
8
+ *
9
+ * Contains types only, plus the `TRACE_KINDS_V1` value mirror with its
10
+ * compile-time drift guards (CLI `--trace-kind` validation consumes the
11
+ * value).
12
+ */
13
+ import type { LoggerRecord, OutputArtifactRef } from "./public-api.ts";
14
+ export type ObservabilityLogLevel = "info" | "warn" | "error";
15
+ /**
16
+ * Output projection surface carried by the problem report's whole-document
17
+ * projection. Authority moved from the host's `host/output-serializer.ts`
18
+ * (D-075 S4 second batch); the host re-exports it from there.
19
+ */
20
+ export type HostOutputSurfaceV1 = "tui" | "cli-text" | "inspector";
21
+ /** One bounded-journal record derived from a delivered lifecycle event. */
22
+ export interface ObservabilityJournalEntryV1 {
23
+ readonly sequence: number;
24
+ readonly at: number;
25
+ /** Session id extracted from the event data; null when the event has none. */
26
+ readonly sessionId: string | null;
27
+ /** Lifecycle event type, e.g. "model.stream" or "tool.commit". */
28
+ readonly stage: string;
29
+ readonly level: ObservabilityLogLevel;
30
+ /** Event correlation id, used as the trace grouping key. */
31
+ readonly traceId?: string;
32
+ readonly summary: string;
33
+ /** Validated lifecycle event data, retained verbatim for diagnosis. */
34
+ readonly data?: unknown;
35
+ }
36
+ export interface ObservabilityQueryFilterV1 {
37
+ readonly sessionId?: string;
38
+ readonly stage?: string;
39
+ readonly level?: ObservabilityLogLevel;
40
+ readonly traceId?: string;
41
+ /** When set, returns the most recent `limit` matching entries in order. */
42
+ readonly limit?: number;
43
+ }
44
+ export interface ObservabilityTraceStepV1 {
45
+ readonly entry: ObservabilityJournalEntryV1;
46
+ /** Milliseconds since the previous step; null on the first step. */
47
+ readonly durationMs: number | null;
48
+ }
49
+ export interface ObservabilityTraceTimelineV1 {
50
+ readonly traceId: string;
51
+ readonly steps: readonly ObservabilityTraceStepV1[];
52
+ }
53
+ export interface ObservabilityJournalStatsV1 {
54
+ readonly size: number;
55
+ readonly dropped: number;
56
+ }
57
+ export interface ObservabilityProblemReportSessionV1 {
58
+ readonly sessionId: string;
59
+ readonly entryCount: number;
60
+ readonly firstSequence: number | null;
61
+ readonly lastSequence: number | null;
62
+ }
63
+ /** Full error envelope of the most recent error-level entry for the session. */
64
+ export interface ObservabilityProblemReportErrorV1 {
65
+ readonly sequence: number;
66
+ readonly at: number;
67
+ readonly stage: string;
68
+ /** Lifecycle phase when the event carries one (model.stream). */
69
+ readonly phase?: string;
70
+ readonly traceId: string | null;
71
+ readonly code: string;
72
+ readonly message: string;
73
+ }
74
+ export interface ObservabilityProblemReportTailEntryV1 {
75
+ readonly sequence: number;
76
+ readonly at: number;
77
+ readonly stage: string;
78
+ readonly level: ObservabilityLogLevel;
79
+ readonly traceId: string | null;
80
+ readonly summary: string;
81
+ /**
82
+ * Payload rendered through the host output serializer redaction channel;
83
+ * internal field keys are replaced at any depth before export.
84
+ */
85
+ readonly data?: string;
86
+ }
87
+ export interface ObservabilityProblemReportCountsV1 {
88
+ readonly byStage: Readonly<Record<string, number>>;
89
+ readonly byLevel: Readonly<Record<string, number>>;
90
+ }
91
+ /** A metric the journal cannot currently source; the reason names the missing event granularity. */
92
+ export interface MetricsNotSampledV1 {
93
+ readonly status: "not_sampled";
94
+ readonly reason: string;
95
+ }
96
+ /** Cancel-latency samples: delay from a cancel signal to the turn's terminal event. */
97
+ export interface MetricsCancelLatencySampledV1 {
98
+ readonly status: "sampled";
99
+ readonly count: number;
100
+ readonly p50Ms: number | null;
101
+ readonly p95Ms: number | null;
102
+ }
103
+ /** Token/cost totals summed over usage-bearing journal events. */
104
+ export interface MetricsTokensSampledV1 {
105
+ readonly status: "sampled";
106
+ readonly totalTokens: number;
107
+ }
108
+ /** Memory recall hit rate over `memory_recall` tool executions in the window. */
109
+ export interface MetricsMemoryRecallSampledV1 {
110
+ readonly status: "sampled";
111
+ readonly calls: number;
112
+ readonly hits: number;
113
+ readonly hitRate: number;
114
+ }
115
+ export interface MetricsToolErrorsV1 {
116
+ readonly calls: number;
117
+ readonly errors: number;
118
+ /** `null` when no tool executions occurred in the window. */
119
+ readonly errorRate: number | null;
120
+ /** Errors per tool name, descending by count then ascending by name. */
121
+ readonly topErrors: readonly {
122
+ readonly toolName: string;
123
+ readonly errors: number;
124
+ }[];
125
+ }
126
+ export interface MetricsTurnsV1 {
127
+ readonly count: number;
128
+ readonly successCount: number;
129
+ /** `null` when no turns occurred in the window. */
130
+ readonly successRate: number | null;
131
+ readonly p50DurationMs: number | null;
132
+ readonly p95DurationMs: number | null;
133
+ }
134
+ /** Window metadata: which turns were aggregated and their time span. */
135
+ export interface MetricsWindowV1 {
136
+ /** Requested turn bound; `null` when the whole journal was aggregated. */
137
+ readonly windowTurns: number | null;
138
+ readonly sampledTurns: number;
139
+ readonly firstAt: number | null;
140
+ readonly lastAt: number | null;
141
+ }
142
+ /**
143
+ * Deterministic product metrics aggregated read-only from the journal ring.
144
+ * Metrics the journal cannot source are reported as `not_sampled` with the
145
+ * reason instead of being fabricated.
146
+ */
147
+ export interface MetricsSnapshotV1 {
148
+ readonly version: 1;
149
+ readonly window: MetricsWindowV1;
150
+ readonly turns: MetricsTurnsV1;
151
+ readonly tools: MetricsToolErrorsV1;
152
+ readonly cancelLatencyMs: MetricsCancelLatencySampledV1 | MetricsNotSampledV1;
153
+ readonly tokens: MetricsTokensSampledV1 | MetricsNotSampledV1;
154
+ readonly memoryRecall: MetricsMemoryRecallSampledV1 | MetricsNotSampledV1;
155
+ }
156
+ /** Aggregate options for {@link ObservabilityPlugin.getMetrics}. */
157
+ export interface ObservabilityMetricsOptions {
158
+ /** Aggregate only the most recent `windowTurns` correlationId groups; omit for the whole journal. */
159
+ readonly windowTurns?: number;
160
+ }
161
+ /**
162
+ * Trace record kinds for `agent-forge/trace.v1`
163
+ * (design: docs/design/会话轨迹与日志设计.md §4.2).
164
+ */
165
+ export type TraceKindV1 = "session.start" | "turn.span" | "llm.request" | "llm.response" | "tool.span" | "lifecycle" | "error" | "decision" | "plugin.log";
166
+ /**
167
+ * Runtime mirror of `TraceKindV1` for value-level consumers (CLI
168
+ * `--trace-kind` validation), kept beside the union so the two compile-time
169
+ * drift guards fail the build when they diverge in either direction:
170
+ * - the `satisfies` below rejects a list entry the union no longer has;
171
+ * - `_TRACE_KINDS_EXHAUSTIVE_CHECK` rejects a union member missing from the
172
+ * list (`never` is not assignable from `true`).
173
+ */
174
+ export declare const TRACE_KINDS_V1: readonly ["session.start", "turn.span", "llm.request", "llm.response", "tool.span", "lifecycle", "error", "decision", "plugin.log"];
175
+ /** One append-only JSONL trace record (design §4.2). */
176
+ export interface TraceRecordV1 {
177
+ readonly v: 1;
178
+ readonly schema: "agent-forge/trace.v1";
179
+ /** Monotonic sequence within one trace file, starting at 1. */
180
+ readonly seq: number;
181
+ /** Host timestamp (epoch ms). */
182
+ readonly t: number;
183
+ /** Duration of a closed span (ms). */
184
+ readonly dur?: number;
185
+ readonly kind: TraceKindV1;
186
+ /** Turn/request correlation id, shared with lifecycle events and logs. */
187
+ readonly traceId?: string;
188
+ readonly level?: ObservabilityLogLevel;
189
+ /** Kind-specific bounded data. */
190
+ readonly data?: unknown;
191
+ }
192
+ /** Bounds for trace payload snapshots (design §4.3); each field defaults when omitted. */
193
+ export interface TraceSinkOptionsV1 {
194
+ /** Strings longer than this truncate with a `…[truncated N chars]` suffix. Default 16_000. */
195
+ readonly maxStringChars?: number;
196
+ /**
197
+ * Serialized snapshot budget in bytes, enforced largest-string-first inside
198
+ * `llm.request.data.payload`. Strictly below the 512 KiB per-record budget.
199
+ * Default 393_216 (384 KiB).
200
+ */
201
+ readonly maxSnapshotBytes?: number;
202
+ /**
203
+ * OTLP/HTTP+JSON trace export (design §8). Omitted keeps the exporter
204
+ * absent: no transport, no buffering, file-sink behavior unchanged. The
205
+ * endpoint is the OTLP base URL (e.g. `http://localhost:4318`); the
206
+ * `/v1/traces` signal path is appended unless already present.
207
+ */
208
+ readonly otlp?: OtlpExportOptionsV1;
209
+ }
210
+ /** OTLP/HTTP+JSON exporter options; all durations/bounds are deterministic inputs. */
211
+ export interface OtlpExportOptionsV1 {
212
+ /** OTLP base URL; `/v1/traces` is appended unless already present. */
213
+ readonly endpoint: string;
214
+ /**
215
+ * Transport injection for deterministic tests and custom hosts (proxy,
216
+ * in-process collector); defaults to `globalThis.fetch`. Only the fields
217
+ * below are consumed by the plugin.
218
+ */
219
+ readonly fetchImpl?: (url: string, init: {
220
+ method: "POST";
221
+ headers: {
222
+ "content-type": string;
223
+ };
224
+ body: string;
225
+ signal: AbortSignal;
226
+ }) => Promise<{
227
+ ok: boolean;
228
+ status: number;
229
+ }>;
230
+ /** Per-flush HTTP timeout in ms. Default 5_000. */
231
+ readonly timeoutMs?: number;
232
+ /** Buffered-span count that triggers an early flush. Default 64. */
233
+ readonly flushThreshold?: number;
234
+ }
235
+ /**
236
+ * Telemetry export seam (design §8): the single attachment point for trace
237
+ * exporters (first OTLP/HTTP+JSON; future Prometheus-style pull surfaces hook
238
+ * here instead of growing bespoke taps). Hosts inject sinks through
239
+ * `ObservabilityTraceConfig.exportSinks`; the plugin never reaches into host
240
+ * internals and never lets a sink break event delivery.
241
+ */
242
+ export interface ObservabilityExportSinkV1 {
243
+ /** Called synchronously for every finalized trace record. Must not throw. */
244
+ readonly onTraceRecord: (record: TraceRecordV1) => void;
245
+ /** Best-effort flush; resolves after the batch is delivered or dropped. */
246
+ readonly flush: (reason: string) => Promise<void>;
247
+ }
248
+ /**
249
+ * Host-injected adapter for the trace sink (design §4.4). The host closure
250
+ * owns session-path resolution, provider payload capture, and failure
251
+ * reporting; the plugin never reaches into host internals.
252
+ */
253
+ export interface ObservabilityHostAdapterV1 {
254
+ /** Trace file path for the current session, or `null` when tracing is off. */
255
+ getTraceFilePath(): string | null;
256
+ /** Host push of the finalized provider payload (sdk `onPayload` tail). */
257
+ captureProviderPayload(payload: unknown, model: unknown): void;
258
+ /** Trace write-failure channel; the host forwards to the process log. */
259
+ onSinkFailure(error: unknown): void;
260
+ /**
261
+ * Correlation id of the turn currently in flight, or `undefined` outside a
262
+ * turn. Omitted keeps the phase-1 behavior: `llm.request` and `plugin.log`
263
+ * records carry no traceId.
264
+ */
265
+ getTurnCorrelationId?(): string | undefined;
266
+ /**
267
+ * Session-header fact: the parent agent session id recorded at session
268
+ * creation (`SessionHeader.parentAgentSession`, session-manager.ts).
269
+ * Omitted — or returning `undefined` when the header has no such field —
270
+ * keeps the previous behavior: the `session.start` record carries no
271
+ * `parentSessionId`.
272
+ */
273
+ getParentAgentSessionId?(): string | undefined;
274
+ /**
275
+ * Session-header fact: the parent-turn correlation id that created this
276
+ * session (`SessionHeader.parentTraceId`, session-manager.ts; frozen at
277
+ * creation). Omitted — or returning `undefined` — keeps the previous
278
+ * behavior: the `session.start` record carries no `parentTraceId`.
279
+ */
280
+ getParentTraceId?(): string | undefined;
281
+ /**
282
+ * Plugin-to-host facade registration (D-075 S4 second batch). The plugin
283
+ * entry calls it once after creating the plugin instance so the host's
284
+ * assembly refs (provider payload capture, plugin.log mirror, /metrics
285
+ * source) keep their embedded-era semantics; calling it again with
286
+ * `undefined` on teardown clears them. Omitted on hosts that consume the
287
+ * facade through another channel — the entry must probe before calling.
288
+ */
289
+ registerFacade?(facade: ObservabilityPluginFacadeV1 | undefined): void;
290
+ }
291
+ /**
292
+ * The narrow plugin-facing face the host consumes through
293
+ * {@link ObservabilityHostAdapterV1.registerFacade}. The plugin instance is
294
+ * structurally assignable to it.
295
+ */
296
+ export interface ObservabilityPluginFacadeV1 {
297
+ captureProviderPayload(payload: unknown, model: unknown): void;
298
+ /** Optional: present since the plugin.log mirror (design §7.2). */
299
+ mirrorPluginLog?(record: LoggerRecord): void;
300
+ getMetrics(options?: ObservabilityMetricsOptions): MetricsSnapshotV1;
301
+ }
302
+ /** Deterministic problem report; `reportId` is FNV-1a-32 hex over the canonical JSON of the report body. */
303
+ export interface ProblemReportV1 {
304
+ readonly version: 1;
305
+ readonly reportId: string;
306
+ readonly createdAt: number;
307
+ readonly reason: string;
308
+ readonly session: ObservabilityProblemReportSessionV1;
309
+ readonly lastError: ObservabilityProblemReportErrorV1 | null;
310
+ readonly journalTail: readonly ObservabilityProblemReportTailEntryV1[];
311
+ readonly counts: ObservabilityProblemReportCountsV1;
312
+ readonly dropped: number;
313
+ readonly redactionApplied: boolean;
314
+ /** Whole-report projection rendered by the host output serializer. */
315
+ readonly projection: {
316
+ readonly surface: HostOutputSurfaceV1;
317
+ readonly text: string;
318
+ };
319
+ /** Present only when the host declared the `output-artifacts` feature. */
320
+ readonly artifact?: OutputArtifactRef;
321
+ }
322
+ /**
323
+ * The observability plugin's public behavior face (journal read side, trace
324
+ * explanation, deterministic metrics, problem-report export). The plugin
325
+ * package returns this shape from its factory; hosts consume it through
326
+ * {@link ObservabilityPluginFacadeV1} or their own references.
327
+ */
328
+ export interface ObservabilityPlugin {
329
+ readonly id: string;
330
+ query(filter?: ObservabilityQueryFilterV1): readonly ObservabilityJournalEntryV1[];
331
+ explainTrace(traceId: string): ObservabilityTraceTimelineV1;
332
+ getMetrics(options?: ObservabilityMetricsOptions): MetricsSnapshotV1;
333
+ stats(): ObservabilityJournalStatsV1;
334
+ exportProblemReport(input: {
335
+ readonly sessionId: string;
336
+ readonly reason: string;
337
+ }): Promise<ProblemReportV1>;
338
+ /**
339
+ * Receives the finalized provider payload from the host adapter and emits
340
+ * one `llm.request` trace record with a bounded redacted snapshot (design
341
+ * §4.3). No-op when no trace adapter was injected.
342
+ */
343
+ captureProviderPayload(payload: unknown, model: unknown): void;
344
+ /**
345
+ * Receives one plugin LoggerAPI record forwarded by the host runtime sink
346
+ * (`CapabilityRuntimeOptions.onLogRecord`) and appends a `plugin.log` trace
347
+ * record (design §4.2/§7.2). No-op when no trace adapter was injected.
348
+ * Must never call `api.logger`: the host forwards logger output here, so
349
+ * logging from inside would recurse.
350
+ */
351
+ mirrorPluginLog(record: LoggerRecord): void;
352
+ dispose(): void;
353
+ }
354
+ //# sourceMappingURL=observability.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"observability.d.ts","sourceRoot":"","sources":["../src/observability.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Public contracts for the first-party observability plugin (event journal +\n * problem reports + trace sink). The plugin package\n * (`@agent-forge/plugin-observability`) is the implementation; this module is\n * the single authoring authority for its public types and the host-facing\n * trace adapter contract. The host re-exports the surface from its historical\n * `plugins/observability-types.ts` path so existing imports keep resolving.\n *\n * Contains types only, plus the `TRACE_KINDS_V1` value mirror with its\n * compile-time drift guards (CLI `--trace-kind` validation consumes the\n * value).\n */\n\nimport type { LoggerRecord, OutputArtifactRef } from \"./public-api.ts\";\n\nexport type ObservabilityLogLevel = \"info\" | \"warn\" | \"error\";\n\n/**\n * Output projection surface carried by the problem report's whole-document\n * projection. Authority moved from the host's `host/output-serializer.ts`\n * (D-075 S4 second batch); the host re-exports it from there.\n */\nexport type HostOutputSurfaceV1 = \"tui\" | \"cli-text\" | \"inspector\";\n\n/** One bounded-journal record derived from a delivered lifecycle event. */\nexport interface ObservabilityJournalEntryV1 {\n\treadonly sequence: number;\n\treadonly at: number;\n\t/** Session id extracted from the event data; null when the event has none. */\n\treadonly sessionId: string | null;\n\t/** Lifecycle event type, e.g. \"model.stream\" or \"tool.commit\". */\n\treadonly stage: string;\n\treadonly level: ObservabilityLogLevel;\n\t/** Event correlation id, used as the trace grouping key. */\n\treadonly traceId?: string;\n\treadonly summary: string;\n\t/** Validated lifecycle event data, retained verbatim for diagnosis. */\n\treadonly data?: unknown;\n}\n\nexport interface ObservabilityQueryFilterV1 {\n\treadonly sessionId?: string;\n\treadonly stage?: string;\n\treadonly level?: ObservabilityLogLevel;\n\treadonly traceId?: string;\n\t/** When set, returns the most recent `limit` matching entries in order. */\n\treadonly limit?: number;\n}\n\nexport interface ObservabilityTraceStepV1 {\n\treadonly entry: ObservabilityJournalEntryV1;\n\t/** Milliseconds since the previous step; null on the first step. */\n\treadonly durationMs: number | null;\n}\n\nexport interface ObservabilityTraceTimelineV1 {\n\treadonly traceId: string;\n\treadonly steps: readonly ObservabilityTraceStepV1[];\n}\n\nexport interface ObservabilityJournalStatsV1 {\n\treadonly size: number;\n\treadonly dropped: number;\n}\n\nexport interface ObservabilityProblemReportSessionV1 {\n\treadonly sessionId: string;\n\treadonly entryCount: number;\n\treadonly firstSequence: number | null;\n\treadonly lastSequence: number | null;\n}\n\n/** Full error envelope of the most recent error-level entry for the session. */\nexport interface ObservabilityProblemReportErrorV1 {\n\treadonly sequence: number;\n\treadonly at: number;\n\treadonly stage: string;\n\t/** Lifecycle phase when the event carries one (model.stream). */\n\treadonly phase?: string;\n\treadonly traceId: string | null;\n\treadonly code: string;\n\treadonly message: string;\n}\n\nexport interface ObservabilityProblemReportTailEntryV1 {\n\treadonly sequence: number;\n\treadonly at: number;\n\treadonly stage: string;\n\treadonly level: ObservabilityLogLevel;\n\treadonly traceId: string | null;\n\treadonly summary: string;\n\t/**\n\t * Payload rendered through the host output serializer redaction channel;\n\t * internal field keys are replaced at any depth before export.\n\t */\n\treadonly data?: string;\n}\n\nexport interface ObservabilityProblemReportCountsV1 {\n\treadonly byStage: Readonly<Record<string, number>>;\n\treadonly byLevel: Readonly<Record<string, number>>;\n}\n\n/** A metric the journal cannot currently source; the reason names the missing event granularity. */\nexport interface MetricsNotSampledV1 {\n\treadonly status: \"not_sampled\";\n\treadonly reason: string;\n}\n\n/** Cancel-latency samples: delay from a cancel signal to the turn's terminal event. */\nexport interface MetricsCancelLatencySampledV1 {\n\treadonly status: \"sampled\";\n\treadonly count: number;\n\treadonly p50Ms: number | null;\n\treadonly p95Ms: number | null;\n}\n\n/** Token/cost totals summed over usage-bearing journal events. */\nexport interface MetricsTokensSampledV1 {\n\treadonly status: \"sampled\";\n\treadonly totalTokens: number;\n}\n\n/** Memory recall hit rate over `memory_recall` tool executions in the window. */\nexport interface MetricsMemoryRecallSampledV1 {\n\treadonly status: \"sampled\";\n\treadonly calls: number;\n\treadonly hits: number;\n\treadonly hitRate: number;\n}\n\nexport interface MetricsToolErrorsV1 {\n\treadonly calls: number;\n\treadonly errors: number;\n\t/** `null` when no tool executions occurred in the window. */\n\treadonly errorRate: number | null;\n\t/** Errors per tool name, descending by count then ascending by name. */\n\treadonly topErrors: readonly { readonly toolName: string; readonly errors: number }[];\n}\n\nexport interface MetricsTurnsV1 {\n\treadonly count: number;\n\treadonly successCount: number;\n\t/** `null` when no turns occurred in the window. */\n\treadonly successRate: number | null;\n\treadonly p50DurationMs: number | null;\n\treadonly p95DurationMs: number | null;\n}\n\n/** Window metadata: which turns were aggregated and their time span. */\nexport interface MetricsWindowV1 {\n\t/** Requested turn bound; `null` when the whole journal was aggregated. */\n\treadonly windowTurns: number | null;\n\treadonly sampledTurns: number;\n\treadonly firstAt: number | null;\n\treadonly lastAt: number | null;\n}\n\n/**\n * Deterministic product metrics aggregated read-only from the journal ring.\n * Metrics the journal cannot source are reported as `not_sampled` with the\n * reason instead of being fabricated.\n */\nexport interface MetricsSnapshotV1 {\n\treadonly version: 1;\n\treadonly window: MetricsWindowV1;\n\treadonly turns: MetricsTurnsV1;\n\treadonly tools: MetricsToolErrorsV1;\n\treadonly cancelLatencyMs: MetricsCancelLatencySampledV1 | MetricsNotSampledV1;\n\treadonly tokens: MetricsTokensSampledV1 | MetricsNotSampledV1;\n\treadonly memoryRecall: MetricsMemoryRecallSampledV1 | MetricsNotSampledV1;\n}\n\n/** Aggregate options for {@link ObservabilityPlugin.getMetrics}. */\nexport interface ObservabilityMetricsOptions {\n\t/** Aggregate only the most recent `windowTurns` correlationId groups; omit for the whole journal. */\n\treadonly windowTurns?: number;\n}\n\n/**\n * Trace record kinds for `agent-forge/trace.v1`\n * (design: docs/design/会话轨迹与日志设计.md §4.2).\n */\nexport type TraceKindV1 =\n\t| \"session.start\"\n\t| \"turn.span\"\n\t| \"llm.request\"\n\t| \"llm.response\"\n\t| \"tool.span\"\n\t| \"lifecycle\"\n\t| \"error\"\n\t| \"decision\"\n\t| \"plugin.log\";\n\n/**\n * Runtime mirror of `TraceKindV1` for value-level consumers (CLI\n * `--trace-kind` validation), kept beside the union so the two compile-time\n * drift guards fail the build when they diverge in either direction:\n * - the `satisfies` below rejects a list entry the union no longer has;\n * - `_TRACE_KINDS_EXHAUSTIVE_CHECK` rejects a union member missing from the\n * list (`never` is not assignable from `true`).\n */\nexport const TRACE_KINDS_V1 = [\n\t\"session.start\",\n\t\"turn.span\",\n\t\"llm.request\",\n\t\"llm.response\",\n\t\"tool.span\",\n\t\"lifecycle\",\n\t\"error\",\n\t\"decision\",\n\t\"plugin.log\",\n] as const satisfies readonly TraceKindV1[];\n\ntype TraceKindListV1 = (typeof TRACE_KINDS_V1)[number];\nconst _TRACE_KINDS_EXHAUSTIVE_CHECK: TraceKindV1 extends TraceKindListV1 ? true : never = true;\n\n/** One append-only JSONL trace record (design §4.2). */\nexport interface TraceRecordV1 {\n\treadonly v: 1;\n\treadonly schema: \"agent-forge/trace.v1\";\n\t/** Monotonic sequence within one trace file, starting at 1. */\n\treadonly seq: number;\n\t/** Host timestamp (epoch ms). */\n\treadonly t: number;\n\t/** Duration of a closed span (ms). */\n\treadonly dur?: number;\n\treadonly kind: TraceKindV1;\n\t/** Turn/request correlation id, shared with lifecycle events and logs. */\n\treadonly traceId?: string;\n\treadonly level?: ObservabilityLogLevel;\n\t/** Kind-specific bounded data. */\n\treadonly data?: unknown;\n}\n\n/** Bounds for trace payload snapshots (design §4.3); each field defaults when omitted. */\nexport interface TraceSinkOptionsV1 {\n\t/** Strings longer than this truncate with a `…[truncated N chars]` suffix. Default 16_000. */\n\treadonly maxStringChars?: number;\n\t/**\n\t * Serialized snapshot budget in bytes, enforced largest-string-first inside\n\t * `llm.request.data.payload`. Strictly below the 512 KiB per-record budget.\n\t * Default 393_216 (384 KiB).\n\t */\n\treadonly maxSnapshotBytes?: number;\n\t/**\n\t * OTLP/HTTP+JSON trace export (design §8). Omitted keeps the exporter\n\t * absent: no transport, no buffering, file-sink behavior unchanged. The\n\t * endpoint is the OTLP base URL (e.g. `http://localhost:4318`); the\n\t * `/v1/traces` signal path is appended unless already present.\n\t */\n\treadonly otlp?: OtlpExportOptionsV1;\n}\n\n/** OTLP/HTTP+JSON exporter options; all durations/bounds are deterministic inputs. */\nexport interface OtlpExportOptionsV1 {\n\t/** OTLP base URL; `/v1/traces` is appended unless already present. */\n\treadonly endpoint: string;\n\t/**\n\t * Transport injection for deterministic tests and custom hosts (proxy,\n\t * in-process collector); defaults to `globalThis.fetch`. Only the fields\n\t * below are consumed by the plugin.\n\t */\n\treadonly fetchImpl?: (\n\t\turl: string,\n\t\tinit: { method: \"POST\"; headers: { \"content-type\": string }; body: string; signal: AbortSignal },\n\t) => Promise<{ ok: boolean; status: number }>;\n\t/** Per-flush HTTP timeout in ms. Default 5_000. */\n\treadonly timeoutMs?: number;\n\t/** Buffered-span count that triggers an early flush. Default 64. */\n\treadonly flushThreshold?: number;\n}\n\n/**\n * Telemetry export seam (design §8): the single attachment point for trace\n * exporters (first OTLP/HTTP+JSON; future Prometheus-style pull surfaces hook\n * here instead of growing bespoke taps). Hosts inject sinks through\n * `ObservabilityTraceConfig.exportSinks`; the plugin never reaches into host\n * internals and never lets a sink break event delivery.\n */\nexport interface ObservabilityExportSinkV1 {\n\t/** Called synchronously for every finalized trace record. Must not throw. */\n\treadonly onTraceRecord: (record: TraceRecordV1) => void;\n\t/** Best-effort flush; resolves after the batch is delivered or dropped. */\n\treadonly flush: (reason: string) => Promise<void>;\n}\n\n/**\n * Host-injected adapter for the trace sink (design §4.4). The host closure\n * owns session-path resolution, provider payload capture, and failure\n * reporting; the plugin never reaches into host internals.\n */\nexport interface ObservabilityHostAdapterV1 {\n\t/** Trace file path for the current session, or `null` when tracing is off. */\n\tgetTraceFilePath(): string | null;\n\t/** Host push of the finalized provider payload (sdk `onPayload` tail). */\n\tcaptureProviderPayload(payload: unknown, model: unknown): void;\n\t/** Trace write-failure channel; the host forwards to the process log. */\n\tonSinkFailure(error: unknown): void;\n\t/**\n\t * Correlation id of the turn currently in flight, or `undefined` outside a\n\t * turn. Omitted keeps the phase-1 behavior: `llm.request` and `plugin.log`\n\t * records carry no traceId.\n\t */\n\tgetTurnCorrelationId?(): string | undefined;\n\t/**\n\t * Session-header fact: the parent agent session id recorded at session\n\t * creation (`SessionHeader.parentAgentSession`, session-manager.ts).\n\t * Omitted — or returning `undefined` when the header has no such field —\n\t * keeps the previous behavior: the `session.start` record carries no\n\t * `parentSessionId`.\n\t */\n\tgetParentAgentSessionId?(): string | undefined;\n\t/**\n\t * Session-header fact: the parent-turn correlation id that created this\n\t * session (`SessionHeader.parentTraceId`, session-manager.ts; frozen at\n\t * creation). Omitted — or returning `undefined` — keeps the previous\n\t * behavior: the `session.start` record carries no `parentTraceId`.\n\t */\n\tgetParentTraceId?(): string | undefined;\n\t/**\n\t * Plugin-to-host facade registration (D-075 S4 second batch). The plugin\n\t * entry calls it once after creating the plugin instance so the host's\n\t * assembly refs (provider payload capture, plugin.log mirror, /metrics\n\t * source) keep their embedded-era semantics; calling it again with\n\t * `undefined` on teardown clears them. Omitted on hosts that consume the\n\t * facade through another channel — the entry must probe before calling.\n\t */\n\tregisterFacade?(facade: ObservabilityPluginFacadeV1 | undefined): void;\n}\n\n/**\n * The narrow plugin-facing face the host consumes through\n * {@link ObservabilityHostAdapterV1.registerFacade}. The plugin instance is\n * structurally assignable to it.\n */\nexport interface ObservabilityPluginFacadeV1 {\n\tcaptureProviderPayload(payload: unknown, model: unknown): void;\n\t/** Optional: present since the plugin.log mirror (design §7.2). */\n\tmirrorPluginLog?(record: LoggerRecord): void;\n\tgetMetrics(options?: ObservabilityMetricsOptions): MetricsSnapshotV1;\n}\n\n/** Deterministic problem report; `reportId` is FNV-1a-32 hex over the canonical JSON of the report body. */\nexport interface ProblemReportV1 {\n\treadonly version: 1;\n\treadonly reportId: string;\n\treadonly createdAt: number;\n\treadonly reason: string;\n\treadonly session: ObservabilityProblemReportSessionV1;\n\treadonly lastError: ObservabilityProblemReportErrorV1 | null;\n\treadonly journalTail: readonly ObservabilityProblemReportTailEntryV1[];\n\treadonly counts: ObservabilityProblemReportCountsV1;\n\treadonly dropped: number;\n\treadonly redactionApplied: boolean;\n\t/** Whole-report projection rendered by the host output serializer. */\n\treadonly projection: {\n\t\treadonly surface: HostOutputSurfaceV1;\n\t\treadonly text: string;\n\t};\n\t/** Present only when the host declared the `output-artifacts` feature. */\n\treadonly artifact?: OutputArtifactRef;\n}\n\n/**\n * The observability plugin's public behavior face (journal read side, trace\n * explanation, deterministic metrics, problem-report export). The plugin\n * package returns this shape from its factory; hosts consume it through\n * {@link ObservabilityPluginFacadeV1} or their own references.\n */\nexport interface ObservabilityPlugin {\n\treadonly id: string;\n\tquery(filter?: ObservabilityQueryFilterV1): readonly ObservabilityJournalEntryV1[];\n\texplainTrace(traceId: string): ObservabilityTraceTimelineV1;\n\tgetMetrics(options?: ObservabilityMetricsOptions): MetricsSnapshotV1;\n\tstats(): ObservabilityJournalStatsV1;\n\texportProblemReport(input: { readonly sessionId: string; readonly reason: string }): Promise<ProblemReportV1>;\n\t/**\n\t * Receives the finalized provider payload from the host adapter and emits\n\t * one `llm.request` trace record with a bounded redacted snapshot (design\n\t * §4.3). No-op when no trace adapter was injected.\n\t */\n\tcaptureProviderPayload(payload: unknown, model: unknown): void;\n\t/**\n\t * Receives one plugin LoggerAPI record forwarded by the host runtime sink\n\t * (`CapabilityRuntimeOptions.onLogRecord`) and appends a `plugin.log` trace\n\t * record (design §4.2/§7.2). No-op when no trace adapter was injected.\n\t * Must never call `api.logger`: the host forwards logger output here, so\n\t * logging from inside would recurse.\n\t */\n\tmirrorPluginLog(record: LoggerRecord): void;\n\tdispose(): void;\n}\n"]}
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Public contracts for the first-party observability plugin (event journal +
3
+ * problem reports + trace sink). The plugin package
4
+ * (`@agent-forge/plugin-observability`) is the implementation; this module is
5
+ * the single authoring authority for its public types and the host-facing
6
+ * trace adapter contract. The host re-exports the surface from its historical
7
+ * `plugins/observability-types.ts` path so existing imports keep resolving.
8
+ *
9
+ * Contains types only, plus the `TRACE_KINDS_V1` value mirror with its
10
+ * compile-time drift guards (CLI `--trace-kind` validation consumes the
11
+ * value).
12
+ */
13
+ /**
14
+ * Runtime mirror of `TraceKindV1` for value-level consumers (CLI
15
+ * `--trace-kind` validation), kept beside the union so the two compile-time
16
+ * drift guards fail the build when they diverge in either direction:
17
+ * - the `satisfies` below rejects a list entry the union no longer has;
18
+ * - `_TRACE_KINDS_EXHAUSTIVE_CHECK` rejects a union member missing from the
19
+ * list (`never` is not assignable from `true`).
20
+ */
21
+ export const TRACE_KINDS_V1 = [
22
+ "session.start",
23
+ "turn.span",
24
+ "llm.request",
25
+ "llm.response",
26
+ "tool.span",
27
+ "lifecycle",
28
+ "error",
29
+ "decision",
30
+ "plugin.log",
31
+ ];
32
+ const _TRACE_KINDS_EXHAUSTIVE_CHECK = true;
33
+ //# sourceMappingURL=observability.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"observability.js","sourceRoot":"","sources":["../src/observability.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAuLH;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG;IAC7B,eAAe;IACf,WAAW;IACX,aAAa;IACb,cAAc;IACd,WAAW;IACX,WAAW;IACX,OAAO;IACP,UAAU;IACV,YAAY;CAC8B,CAAC;AAG5C,MAAM,6BAA6B,GAAuD,IAAI,CAAC","sourcesContent":["/**\n * Public contracts for the first-party observability plugin (event journal +\n * problem reports + trace sink). The plugin package\n * (`@agent-forge/plugin-observability`) is the implementation; this module is\n * the single authoring authority for its public types and the host-facing\n * trace adapter contract. The host re-exports the surface from its historical\n * `plugins/observability-types.ts` path so existing imports keep resolving.\n *\n * Contains types only, plus the `TRACE_KINDS_V1` value mirror with its\n * compile-time drift guards (CLI `--trace-kind` validation consumes the\n * value).\n */\n\nimport type { LoggerRecord, OutputArtifactRef } from \"./public-api.ts\";\n\nexport type ObservabilityLogLevel = \"info\" | \"warn\" | \"error\";\n\n/**\n * Output projection surface carried by the problem report's whole-document\n * projection. Authority moved from the host's `host/output-serializer.ts`\n * (D-075 S4 second batch); the host re-exports it from there.\n */\nexport type HostOutputSurfaceV1 = \"tui\" | \"cli-text\" | \"inspector\";\n\n/** One bounded-journal record derived from a delivered lifecycle event. */\nexport interface ObservabilityJournalEntryV1 {\n\treadonly sequence: number;\n\treadonly at: number;\n\t/** Session id extracted from the event data; null when the event has none. */\n\treadonly sessionId: string | null;\n\t/** Lifecycle event type, e.g. \"model.stream\" or \"tool.commit\". */\n\treadonly stage: string;\n\treadonly level: ObservabilityLogLevel;\n\t/** Event correlation id, used as the trace grouping key. */\n\treadonly traceId?: string;\n\treadonly summary: string;\n\t/** Validated lifecycle event data, retained verbatim for diagnosis. */\n\treadonly data?: unknown;\n}\n\nexport interface ObservabilityQueryFilterV1 {\n\treadonly sessionId?: string;\n\treadonly stage?: string;\n\treadonly level?: ObservabilityLogLevel;\n\treadonly traceId?: string;\n\t/** When set, returns the most recent `limit` matching entries in order. */\n\treadonly limit?: number;\n}\n\nexport interface ObservabilityTraceStepV1 {\n\treadonly entry: ObservabilityJournalEntryV1;\n\t/** Milliseconds since the previous step; null on the first step. */\n\treadonly durationMs: number | null;\n}\n\nexport interface ObservabilityTraceTimelineV1 {\n\treadonly traceId: string;\n\treadonly steps: readonly ObservabilityTraceStepV1[];\n}\n\nexport interface ObservabilityJournalStatsV1 {\n\treadonly size: number;\n\treadonly dropped: number;\n}\n\nexport interface ObservabilityProblemReportSessionV1 {\n\treadonly sessionId: string;\n\treadonly entryCount: number;\n\treadonly firstSequence: number | null;\n\treadonly lastSequence: number | null;\n}\n\n/** Full error envelope of the most recent error-level entry for the session. */\nexport interface ObservabilityProblemReportErrorV1 {\n\treadonly sequence: number;\n\treadonly at: number;\n\treadonly stage: string;\n\t/** Lifecycle phase when the event carries one (model.stream). */\n\treadonly phase?: string;\n\treadonly traceId: string | null;\n\treadonly code: string;\n\treadonly message: string;\n}\n\nexport interface ObservabilityProblemReportTailEntryV1 {\n\treadonly sequence: number;\n\treadonly at: number;\n\treadonly stage: string;\n\treadonly level: ObservabilityLogLevel;\n\treadonly traceId: string | null;\n\treadonly summary: string;\n\t/**\n\t * Payload rendered through the host output serializer redaction channel;\n\t * internal field keys are replaced at any depth before export.\n\t */\n\treadonly data?: string;\n}\n\nexport interface ObservabilityProblemReportCountsV1 {\n\treadonly byStage: Readonly<Record<string, number>>;\n\treadonly byLevel: Readonly<Record<string, number>>;\n}\n\n/** A metric the journal cannot currently source; the reason names the missing event granularity. */\nexport interface MetricsNotSampledV1 {\n\treadonly status: \"not_sampled\";\n\treadonly reason: string;\n}\n\n/** Cancel-latency samples: delay from a cancel signal to the turn's terminal event. */\nexport interface MetricsCancelLatencySampledV1 {\n\treadonly status: \"sampled\";\n\treadonly count: number;\n\treadonly p50Ms: number | null;\n\treadonly p95Ms: number | null;\n}\n\n/** Token/cost totals summed over usage-bearing journal events. */\nexport interface MetricsTokensSampledV1 {\n\treadonly status: \"sampled\";\n\treadonly totalTokens: number;\n}\n\n/** Memory recall hit rate over `memory_recall` tool executions in the window. */\nexport interface MetricsMemoryRecallSampledV1 {\n\treadonly status: \"sampled\";\n\treadonly calls: number;\n\treadonly hits: number;\n\treadonly hitRate: number;\n}\n\nexport interface MetricsToolErrorsV1 {\n\treadonly calls: number;\n\treadonly errors: number;\n\t/** `null` when no tool executions occurred in the window. */\n\treadonly errorRate: number | null;\n\t/** Errors per tool name, descending by count then ascending by name. */\n\treadonly topErrors: readonly { readonly toolName: string; readonly errors: number }[];\n}\n\nexport interface MetricsTurnsV1 {\n\treadonly count: number;\n\treadonly successCount: number;\n\t/** `null` when no turns occurred in the window. */\n\treadonly successRate: number | null;\n\treadonly p50DurationMs: number | null;\n\treadonly p95DurationMs: number | null;\n}\n\n/** Window metadata: which turns were aggregated and their time span. */\nexport interface MetricsWindowV1 {\n\t/** Requested turn bound; `null` when the whole journal was aggregated. */\n\treadonly windowTurns: number | null;\n\treadonly sampledTurns: number;\n\treadonly firstAt: number | null;\n\treadonly lastAt: number | null;\n}\n\n/**\n * Deterministic product metrics aggregated read-only from the journal ring.\n * Metrics the journal cannot source are reported as `not_sampled` with the\n * reason instead of being fabricated.\n */\nexport interface MetricsSnapshotV1 {\n\treadonly version: 1;\n\treadonly window: MetricsWindowV1;\n\treadonly turns: MetricsTurnsV1;\n\treadonly tools: MetricsToolErrorsV1;\n\treadonly cancelLatencyMs: MetricsCancelLatencySampledV1 | MetricsNotSampledV1;\n\treadonly tokens: MetricsTokensSampledV1 | MetricsNotSampledV1;\n\treadonly memoryRecall: MetricsMemoryRecallSampledV1 | MetricsNotSampledV1;\n}\n\n/** Aggregate options for {@link ObservabilityPlugin.getMetrics}. */\nexport interface ObservabilityMetricsOptions {\n\t/** Aggregate only the most recent `windowTurns` correlationId groups; omit for the whole journal. */\n\treadonly windowTurns?: number;\n}\n\n/**\n * Trace record kinds for `agent-forge/trace.v1`\n * (design: docs/design/会话轨迹与日志设计.md §4.2).\n */\nexport type TraceKindV1 =\n\t| \"session.start\"\n\t| \"turn.span\"\n\t| \"llm.request\"\n\t| \"llm.response\"\n\t| \"tool.span\"\n\t| \"lifecycle\"\n\t| \"error\"\n\t| \"decision\"\n\t| \"plugin.log\";\n\n/**\n * Runtime mirror of `TraceKindV1` for value-level consumers (CLI\n * `--trace-kind` validation), kept beside the union so the two compile-time\n * drift guards fail the build when they diverge in either direction:\n * - the `satisfies` below rejects a list entry the union no longer has;\n * - `_TRACE_KINDS_EXHAUSTIVE_CHECK` rejects a union member missing from the\n * list (`never` is not assignable from `true`).\n */\nexport const TRACE_KINDS_V1 = [\n\t\"session.start\",\n\t\"turn.span\",\n\t\"llm.request\",\n\t\"llm.response\",\n\t\"tool.span\",\n\t\"lifecycle\",\n\t\"error\",\n\t\"decision\",\n\t\"plugin.log\",\n] as const satisfies readonly TraceKindV1[];\n\ntype TraceKindListV1 = (typeof TRACE_KINDS_V1)[number];\nconst _TRACE_KINDS_EXHAUSTIVE_CHECK: TraceKindV1 extends TraceKindListV1 ? true : never = true;\n\n/** One append-only JSONL trace record (design §4.2). */\nexport interface TraceRecordV1 {\n\treadonly v: 1;\n\treadonly schema: \"agent-forge/trace.v1\";\n\t/** Monotonic sequence within one trace file, starting at 1. */\n\treadonly seq: number;\n\t/** Host timestamp (epoch ms). */\n\treadonly t: number;\n\t/** Duration of a closed span (ms). */\n\treadonly dur?: number;\n\treadonly kind: TraceKindV1;\n\t/** Turn/request correlation id, shared with lifecycle events and logs. */\n\treadonly traceId?: string;\n\treadonly level?: ObservabilityLogLevel;\n\t/** Kind-specific bounded data. */\n\treadonly data?: unknown;\n}\n\n/** Bounds for trace payload snapshots (design §4.3); each field defaults when omitted. */\nexport interface TraceSinkOptionsV1 {\n\t/** Strings longer than this truncate with a `…[truncated N chars]` suffix. Default 16_000. */\n\treadonly maxStringChars?: number;\n\t/**\n\t * Serialized snapshot budget in bytes, enforced largest-string-first inside\n\t * `llm.request.data.payload`. Strictly below the 512 KiB per-record budget.\n\t * Default 393_216 (384 KiB).\n\t */\n\treadonly maxSnapshotBytes?: number;\n\t/**\n\t * OTLP/HTTP+JSON trace export (design §8). Omitted keeps the exporter\n\t * absent: no transport, no buffering, file-sink behavior unchanged. The\n\t * endpoint is the OTLP base URL (e.g. `http://localhost:4318`); the\n\t * `/v1/traces` signal path is appended unless already present.\n\t */\n\treadonly otlp?: OtlpExportOptionsV1;\n}\n\n/** OTLP/HTTP+JSON exporter options; all durations/bounds are deterministic inputs. */\nexport interface OtlpExportOptionsV1 {\n\t/** OTLP base URL; `/v1/traces` is appended unless already present. */\n\treadonly endpoint: string;\n\t/**\n\t * Transport injection for deterministic tests and custom hosts (proxy,\n\t * in-process collector); defaults to `globalThis.fetch`. Only the fields\n\t * below are consumed by the plugin.\n\t */\n\treadonly fetchImpl?: (\n\t\turl: string,\n\t\tinit: { method: \"POST\"; headers: { \"content-type\": string }; body: string; signal: AbortSignal },\n\t) => Promise<{ ok: boolean; status: number }>;\n\t/** Per-flush HTTP timeout in ms. Default 5_000. */\n\treadonly timeoutMs?: number;\n\t/** Buffered-span count that triggers an early flush. Default 64. */\n\treadonly flushThreshold?: number;\n}\n\n/**\n * Telemetry export seam (design §8): the single attachment point for trace\n * exporters (first OTLP/HTTP+JSON; future Prometheus-style pull surfaces hook\n * here instead of growing bespoke taps). Hosts inject sinks through\n * `ObservabilityTraceConfig.exportSinks`; the plugin never reaches into host\n * internals and never lets a sink break event delivery.\n */\nexport interface ObservabilityExportSinkV1 {\n\t/** Called synchronously for every finalized trace record. Must not throw. */\n\treadonly onTraceRecord: (record: TraceRecordV1) => void;\n\t/** Best-effort flush; resolves after the batch is delivered or dropped. */\n\treadonly flush: (reason: string) => Promise<void>;\n}\n\n/**\n * Host-injected adapter for the trace sink (design §4.4). The host closure\n * owns session-path resolution, provider payload capture, and failure\n * reporting; the plugin never reaches into host internals.\n */\nexport interface ObservabilityHostAdapterV1 {\n\t/** Trace file path for the current session, or `null` when tracing is off. */\n\tgetTraceFilePath(): string | null;\n\t/** Host push of the finalized provider payload (sdk `onPayload` tail). */\n\tcaptureProviderPayload(payload: unknown, model: unknown): void;\n\t/** Trace write-failure channel; the host forwards to the process log. */\n\tonSinkFailure(error: unknown): void;\n\t/**\n\t * Correlation id of the turn currently in flight, or `undefined` outside a\n\t * turn. Omitted keeps the phase-1 behavior: `llm.request` and `plugin.log`\n\t * records carry no traceId.\n\t */\n\tgetTurnCorrelationId?(): string | undefined;\n\t/**\n\t * Session-header fact: the parent agent session id recorded at session\n\t * creation (`SessionHeader.parentAgentSession`, session-manager.ts).\n\t * Omitted — or returning `undefined` when the header has no such field —\n\t * keeps the previous behavior: the `session.start` record carries no\n\t * `parentSessionId`.\n\t */\n\tgetParentAgentSessionId?(): string | undefined;\n\t/**\n\t * Session-header fact: the parent-turn correlation id that created this\n\t * session (`SessionHeader.parentTraceId`, session-manager.ts; frozen at\n\t * creation). Omitted — or returning `undefined` — keeps the previous\n\t * behavior: the `session.start` record carries no `parentTraceId`.\n\t */\n\tgetParentTraceId?(): string | undefined;\n\t/**\n\t * Plugin-to-host facade registration (D-075 S4 second batch). The plugin\n\t * entry calls it once after creating the plugin instance so the host's\n\t * assembly refs (provider payload capture, plugin.log mirror, /metrics\n\t * source) keep their embedded-era semantics; calling it again with\n\t * `undefined` on teardown clears them. Omitted on hosts that consume the\n\t * facade through another channel — the entry must probe before calling.\n\t */\n\tregisterFacade?(facade: ObservabilityPluginFacadeV1 | undefined): void;\n}\n\n/**\n * The narrow plugin-facing face the host consumes through\n * {@link ObservabilityHostAdapterV1.registerFacade}. The plugin instance is\n * structurally assignable to it.\n */\nexport interface ObservabilityPluginFacadeV1 {\n\tcaptureProviderPayload(payload: unknown, model: unknown): void;\n\t/** Optional: present since the plugin.log mirror (design §7.2). */\n\tmirrorPluginLog?(record: LoggerRecord): void;\n\tgetMetrics(options?: ObservabilityMetricsOptions): MetricsSnapshotV1;\n}\n\n/** Deterministic problem report; `reportId` is FNV-1a-32 hex over the canonical JSON of the report body. */\nexport interface ProblemReportV1 {\n\treadonly version: 1;\n\treadonly reportId: string;\n\treadonly createdAt: number;\n\treadonly reason: string;\n\treadonly session: ObservabilityProblemReportSessionV1;\n\treadonly lastError: ObservabilityProblemReportErrorV1 | null;\n\treadonly journalTail: readonly ObservabilityProblemReportTailEntryV1[];\n\treadonly counts: ObservabilityProblemReportCountsV1;\n\treadonly dropped: number;\n\treadonly redactionApplied: boolean;\n\t/** Whole-report projection rendered by the host output serializer. */\n\treadonly projection: {\n\t\treadonly surface: HostOutputSurfaceV1;\n\t\treadonly text: string;\n\t};\n\t/** Present only when the host declared the `output-artifacts` feature. */\n\treadonly artifact?: OutputArtifactRef;\n}\n\n/**\n * The observability plugin's public behavior face (journal read side, trace\n * explanation, deterministic metrics, problem-report export). The plugin\n * package returns this shape from its factory; hosts consume it through\n * {@link ObservabilityPluginFacadeV1} or their own references.\n */\nexport interface ObservabilityPlugin {\n\treadonly id: string;\n\tquery(filter?: ObservabilityQueryFilterV1): readonly ObservabilityJournalEntryV1[];\n\texplainTrace(traceId: string): ObservabilityTraceTimelineV1;\n\tgetMetrics(options?: ObservabilityMetricsOptions): MetricsSnapshotV1;\n\tstats(): ObservabilityJournalStatsV1;\n\texportProblemReport(input: { readonly sessionId: string; readonly reason: string }): Promise<ProblemReportV1>;\n\t/**\n\t * Receives the finalized provider payload from the host adapter and emits\n\t * one `llm.request` trace record with a bounded redacted snapshot (design\n\t * §4.3). No-op when no trace adapter was injected.\n\t */\n\tcaptureProviderPayload(payload: unknown, model: unknown): void;\n\t/**\n\t * Receives one plugin LoggerAPI record forwarded by the host runtime sink\n\t * (`CapabilityRuntimeOptions.onLogRecord`) and appends a `plugin.log` trace\n\t * record (design §4.2/§7.2). No-op when no trace adapter was injected.\n\t * Must never call `api.logger`: the host forwards logger output here, so\n\t * logging from inside would recurse.\n\t */\n\tmirrorPluginLog(record: LoggerRecord): void;\n\tdispose(): void;\n}\n"]}
@@ -0,0 +1,60 @@
1
+ import { type ArtifactRefV1 } from "./artifact.ts";
2
+ import { type JsonValue } from "./common.ts";
3
+ export type PluginOperationStatusV1 = "queued" | "running" | "succeeded" | "failed" | "cancelled" | "needs_user" | "unsupported";
4
+ export declare const PLUGIN_OPERATION_TERMINAL_STATUSES_V1: readonly PluginOperationStatusV1[];
5
+ export interface PluginOperationV1 {
6
+ readonly version: 1;
7
+ readonly operationId: string;
8
+ readonly pluginId: string;
9
+ readonly kind: string;
10
+ readonly status: PluginOperationStatusV1;
11
+ readonly inputArtifacts?: readonly ArtifactRefV1[];
12
+ readonly outputArtifacts?: readonly ArtifactRefV1[];
13
+ readonly startedAt?: string;
14
+ readonly endedAt?: string;
15
+ }
16
+ export type EvidenceKindV1 = "screenshot" | "dom" | "render" | "diff" | "log" | "assertion" | "trace" | "network" | "structure";
17
+ export interface EvidenceRefV1 {
18
+ readonly version: 1;
19
+ readonly evidenceId: string;
20
+ readonly kind: EvidenceKindV1;
21
+ readonly operationId: string;
22
+ readonly artifactRef?: ArtifactRefV1;
23
+ readonly summary?: string;
24
+ readonly observedAt?: string;
25
+ }
26
+ export interface VerificationV1 {
27
+ readonly version: 1;
28
+ readonly status: "passed" | "failed" | "unknown";
29
+ readonly checks: readonly string[];
30
+ }
31
+ export interface PluginErrorV1 {
32
+ readonly version: 1;
33
+ readonly code: string;
34
+ readonly message: string;
35
+ readonly causeCode?: string;
36
+ readonly diagnosticRef?: ArtifactRefV1;
37
+ }
38
+ export type PluginResultStatusV1 = Exclude<PluginOperationStatusV1, "queued" | "running">;
39
+ export interface PluginResultV1 {
40
+ readonly version: 1;
41
+ readonly operationId: string;
42
+ readonly status: PluginResultStatusV1;
43
+ readonly summary: string;
44
+ readonly artifacts: readonly ArtifactRefV1[];
45
+ readonly evidence: readonly EvidenceRefV1[];
46
+ readonly verification?: VerificationV1;
47
+ readonly error?: PluginErrorV1;
48
+ }
49
+ export declare function validatePluginOperationV1(value: unknown, label?: string): PluginOperationV1;
50
+ export declare function buildPluginOperationV1(input: PluginOperationV1): PluginOperationV1;
51
+ export declare function validateEvidenceRefV1(value: unknown, label?: string): EvidenceRefV1;
52
+ export declare function buildEvidenceRefV1(input: EvidenceRefV1): EvidenceRefV1;
53
+ export declare function validateVerificationV1(value: unknown, label?: string): VerificationV1;
54
+ export declare function buildVerificationV1(input: VerificationV1): VerificationV1;
55
+ export declare function validatePluginErrorV1(value: unknown, label?: string): PluginErrorV1;
56
+ export declare function buildPluginErrorV1(input: PluginErrorV1): PluginErrorV1;
57
+ export declare function validatePluginResultV1(value: unknown, label?: string): PluginResultV1;
58
+ export declare function buildPluginResultV1(input: PluginResultV1): PluginResultV1;
59
+ export declare function assertJsonResultInputV1(value: unknown, label?: string): JsonValue;
60
+ //# sourceMappingURL=operation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"operation.d.ts","sourceRoot":"","sources":["../src/operation.ts"],"names":[],"mappings":"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","sourcesContent":["import { type ArtifactRefV1, validateArtifactRefV1 } from \"./artifact.ts\";\nimport {\n\tassertExactKeys,\n\tassertJsonValue,\n\tassertNonEmptyString,\n\tassertOptionalString,\n\tassertPlainRecord,\n\tdetachedFreeze,\n\ttype JsonValue,\n\tsortByKey,\n} from \"./common.ts\";\n\nexport type PluginOperationStatusV1 =\n\t| \"queued\"\n\t| \"running\"\n\t| \"succeeded\"\n\t| \"failed\"\n\t| \"cancelled\"\n\t| \"needs_user\"\n\t| \"unsupported\";\n\nexport const PLUGIN_OPERATION_TERMINAL_STATUSES_V1: readonly PluginOperationStatusV1[] = [\n\t\"succeeded\",\n\t\"failed\",\n\t\"cancelled\",\n\t\"needs_user\",\n\t\"unsupported\",\n];\n\nexport interface PluginOperationV1 {\n\treadonly version: 1;\n\treadonly operationId: string;\n\treadonly pluginId: string;\n\treadonly kind: string;\n\treadonly status: PluginOperationStatusV1;\n\treadonly inputArtifacts?: readonly ArtifactRefV1[];\n\treadonly outputArtifacts?: readonly ArtifactRefV1[];\n\treadonly startedAt?: string;\n\treadonly endedAt?: string;\n}\n\nexport type EvidenceKindV1 =\n\t| \"screenshot\"\n\t| \"dom\"\n\t| \"render\"\n\t| \"diff\"\n\t| \"log\"\n\t| \"assertion\"\n\t| \"trace\"\n\t| \"network\"\n\t| \"structure\";\n\nexport interface EvidenceRefV1 {\n\treadonly version: 1;\n\treadonly evidenceId: string;\n\treadonly kind: EvidenceKindV1;\n\treadonly operationId: string;\n\treadonly artifactRef?: ArtifactRefV1;\n\treadonly summary?: string;\n\treadonly observedAt?: string;\n}\n\nexport interface VerificationV1 {\n\treadonly version: 1;\n\treadonly status: \"passed\" | \"failed\" | \"unknown\";\n\treadonly checks: readonly string[];\n}\n\nexport interface PluginErrorV1 {\n\treadonly version: 1;\n\treadonly code: string;\n\treadonly message: string;\n\treadonly causeCode?: string;\n\treadonly diagnosticRef?: ArtifactRefV1;\n}\n\nexport type PluginResultStatusV1 = Exclude<PluginOperationStatusV1, \"queued\" | \"running\">;\n\nexport interface PluginResultV1 {\n\treadonly version: 1;\n\treadonly operationId: string;\n\treadonly status: PluginResultStatusV1;\n\treadonly summary: string;\n\treadonly artifacts: readonly ArtifactRefV1[];\n\treadonly evidence: readonly EvidenceRefV1[];\n\treadonly verification?: VerificationV1;\n\treadonly error?: PluginErrorV1;\n}\n\nconst EVIDENCE_KINDS_V1: readonly EvidenceKindV1[] = [\n\t\"screenshot\",\n\t\"dom\",\n\t\"render\",\n\t\"diff\",\n\t\"log\",\n\t\"assertion\",\n\t\"trace\",\n\t\"network\",\n\t\"structure\",\n];\nconst OPERATION_STATUSES_V1: readonly PluginOperationStatusV1[] = [\n\t\"queued\",\n\t\"running\",\n\t\"succeeded\",\n\t\"failed\",\n\t\"cancelled\",\n\t\"needs_user\",\n\t\"unsupported\",\n];\nconst RESULT_STATUSES_V1: readonly PluginResultStatusV1[] = [\n\t\"succeeded\",\n\t\"failed\",\n\t\"cancelled\",\n\t\"needs_user\",\n\t\"unsupported\",\n];\n\nfunction validateStatus(value: unknown, label: string, values: readonly string[]): string {\n\tif (typeof value !== \"string\" || !values.includes(value)) throw new Error(`${label} is invalid`);\n\treturn value;\n}\n\nfunction validateIsoLike(value: unknown, label: string): string | undefined {\n\tif (value === undefined) return undefined;\n\tassertNonEmptyString(value, label);\n\treturn value;\n}\n\nfunction validateArtifactArray(value: unknown, label: string): readonly ArtifactRefV1[] {\n\tif (!Array.isArray(value)) throw new Error(`${label} must be an array`);\n\tconst result = value.map((item, index) => validateArtifactRefV1(item, `${label}[${index}]`));\n\tif (new Set(result.map((item) => item.artifactRef)).size !== result.length) {\n\t\tthrow new Error(`${label} must not contain duplicate artifactRef values`);\n\t}\n\treturn Object.freeze(sortByKey(result, (item) => item.artifactRef));\n}\n\nexport function validatePluginOperationV1(value: unknown, label = \"Plugin operation\"): PluginOperationV1 {\n\tassertPlainRecord(value, label);\n\tassertExactKeys(\n\t\tvalue,\n\t\t[\"version\", \"operationId\", \"pluginId\", \"kind\", \"status\"],\n\t\t[\"inputArtifacts\", \"outputArtifacts\", \"startedAt\", \"endedAt\"],\n\t\tlabel,\n\t);\n\tif (value.version !== 1) throw new Error(`${label}.version must be 1`);\n\tassertNonEmptyString(value.operationId, `${label}.operationId`);\n\tassertNonEmptyString(value.pluginId, `${label}.pluginId`);\n\tassertNonEmptyString(value.kind, `${label}.kind`);\n\tconst status = validateStatus(value.status, `${label}.status`, OPERATION_STATUSES_V1) as PluginOperationStatusV1;\n\tconst inputArtifacts =\n\t\tvalue.inputArtifacts === undefined\n\t\t\t? undefined\n\t\t\t: validateArtifactArray(value.inputArtifacts, `${label}.inputArtifacts`);\n\tconst outputArtifacts =\n\t\tvalue.outputArtifacts === undefined\n\t\t\t? undefined\n\t\t\t: validateArtifactArray(value.outputArtifacts, `${label}.outputArtifacts`);\n\tconst startedAt = validateIsoLike(value.startedAt, `${label}.startedAt`);\n\tconst endedAt = validateIsoLike(value.endedAt, `${label}.endedAt`);\n\tif (endedAt !== undefined && startedAt === undefined) throw new Error(`${label}.endedAt requires startedAt`);\n\tif (status === \"running\" && endedAt !== undefined) throw new Error(`${label}.running must not have endedAt`);\n\treturn detachedFreeze({\n\t\tversion: 1,\n\t\toperationId: value.operationId,\n\t\tpluginId: value.pluginId,\n\t\tkind: value.kind,\n\t\tstatus,\n\t\t...(inputArtifacts === undefined ? {} : { inputArtifacts }),\n\t\t...(outputArtifacts === undefined ? {} : { outputArtifacts }),\n\t\t...(startedAt === undefined ? {} : { startedAt }),\n\t\t...(endedAt === undefined ? {} : { endedAt }),\n\t});\n}\n\nexport function buildPluginOperationV1(input: PluginOperationV1): PluginOperationV1 {\n\treturn validatePluginOperationV1(input);\n}\n\nexport function validateEvidenceRefV1(value: unknown, label = \"Evidence reference\"): EvidenceRefV1 {\n\tassertPlainRecord(value, label);\n\tassertExactKeys(\n\t\tvalue,\n\t\t[\"version\", \"evidenceId\", \"kind\", \"operationId\"],\n\t\t[\"artifactRef\", \"summary\", \"observedAt\"],\n\t\tlabel,\n\t);\n\tif (value.version !== 1) throw new Error(`${label}.version must be 1`);\n\tassertNonEmptyString(value.evidenceId, `${label}.evidenceId`);\n\tconst kind = validateStatus(value.kind, `${label}.kind`, EVIDENCE_KINDS_V1) as EvidenceKindV1;\n\tassertNonEmptyString(value.operationId, `${label}.operationId`);\n\tconst artifactRef =\n\t\tvalue.artifactRef === undefined ? undefined : validateArtifactRefV1(value.artifactRef, `${label}.artifactRef`);\n\tassertOptionalString(value.summary, `${label}.summary`);\n\tconst observedAt = validateIsoLike(value.observedAt, `${label}.observedAt`);\n\treturn detachedFreeze({\n\t\tversion: 1,\n\t\tevidenceId: value.evidenceId,\n\t\tkind,\n\t\toperationId: value.operationId,\n\t\t...(artifactRef === undefined ? {} : { artifactRef }),\n\t\t...(value.summary === undefined ? {} : { summary: value.summary }),\n\t\t...(observedAt === undefined ? {} : { observedAt }),\n\t});\n}\n\nexport function buildEvidenceRefV1(input: EvidenceRefV1): EvidenceRefV1 {\n\treturn validateEvidenceRefV1(input);\n}\n\nexport function validateVerificationV1(value: unknown, label = \"Verification\"): VerificationV1 {\n\tassertPlainRecord(value, label);\n\tassertExactKeys(value, [\"version\", \"status\", \"checks\"], [], label);\n\tif (value.version !== 1) throw new Error(`${label}.version must be 1`);\n\tconst status = validateStatus(value.status, `${label}.status`, [\n\t\t\"passed\",\n\t\t\"failed\",\n\t\t\"unknown\",\n\t]) as VerificationV1[\"status\"];\n\tif (!Array.isArray(value.checks) || value.checks.some((item) => typeof item !== \"string\" || item.trim() === \"\")) {\n\t\tthrow new Error(`${label}.checks must contain non-empty strings`);\n\t}\n\tif (new Set(value.checks).size !== value.checks.length)\n\t\tthrow new Error(`${label}.checks must not contain duplicates`);\n\treturn detachedFreeze({ version: 1, status, checks: [...value.checks].sort() });\n}\n\nexport function buildVerificationV1(input: VerificationV1): VerificationV1 {\n\treturn validateVerificationV1(input);\n}\n\nexport function validatePluginErrorV1(value: unknown, label = \"Plugin error\"): PluginErrorV1 {\n\tassertPlainRecord(value, label);\n\tassertExactKeys(value, [\"version\", \"code\", \"message\"], [\"causeCode\", \"diagnosticRef\"], label);\n\tif (value.version !== 1) throw new Error(`${label}.version must be 1`);\n\tassertNonEmptyString(value.code, `${label}.code`);\n\tassertNonEmptyString(value.message, `${label}.message`);\n\tassertOptionalString(value.causeCode, `${label}.causeCode`);\n\tconst diagnosticRef =\n\t\tvalue.diagnosticRef === undefined\n\t\t\t? undefined\n\t\t\t: validateArtifactRefV1(value.diagnosticRef, `${label}.diagnosticRef`);\n\treturn detachedFreeze({\n\t\tversion: 1,\n\t\tcode: value.code,\n\t\tmessage: value.message,\n\t\t...(value.causeCode === undefined ? {} : { causeCode: value.causeCode }),\n\t\t...(diagnosticRef === undefined ? {} : { diagnosticRef }),\n\t});\n}\n\nexport function buildPluginErrorV1(input: PluginErrorV1): PluginErrorV1 {\n\treturn validatePluginErrorV1(input);\n}\n\nexport function validatePluginResultV1(value: unknown, label = \"Plugin result\"): PluginResultV1 {\n\tassertPlainRecord(value, label);\n\tassertExactKeys(\n\t\tvalue,\n\t\t[\"version\", \"operationId\", \"status\", \"summary\", \"artifacts\", \"evidence\"],\n\t\t[\"verification\", \"error\"],\n\t\tlabel,\n\t);\n\tif (value.version !== 1) throw new Error(`${label}.version must be 1`);\n\tassertNonEmptyString(value.operationId, `${label}.operationId`);\n\tconst status = validateStatus(value.status, `${label}.status`, RESULT_STATUSES_V1) as PluginResultStatusV1;\n\tassertNonEmptyString(value.summary, `${label}.summary`);\n\tconst artifacts = validateArtifactArray(value.artifacts, `${label}.artifacts`);\n\tif (!Array.isArray(value.evidence)) throw new Error(`${label}.evidence must be an array`);\n\tconst evidence = value.evidence.map((item, index) => validateEvidenceRefV1(item, `${label}.evidence[${index}]`));\n\tif (new Set(evidence.map((item) => item.evidenceId)).size !== evidence.length)\n\t\tthrow new Error(`${label}.evidence must not contain duplicate evidenceId values`);\n\tconst orderedEvidence = Object.freeze(sortByKey(evidence, (item) => item.evidenceId));\n\tconst verification =\n\t\tvalue.verification === undefined\n\t\t\t? undefined\n\t\t\t: validateVerificationV1(value.verification, `${label}.verification`);\n\tconst error = value.error === undefined ? undefined : validatePluginErrorV1(value.error, `${label}.error`);\n\tif (status !== \"succeeded\" && error === undefined)\n\t\tthrow new Error(`${label}.error is required for status ${status}`);\n\tif (status === \"succeeded\" && error !== undefined) throw new Error(`${label}.succeeded must not include error`);\n\tif (status === \"succeeded\" && verification?.status === \"failed\")\n\t\tthrow new Error(`${label}.succeeded cannot have failed verification`);\n\treturn detachedFreeze({\n\t\tversion: 1,\n\t\toperationId: value.operationId,\n\t\tstatus,\n\t\tsummary: value.summary,\n\t\tartifacts,\n\t\tevidence: orderedEvidence,\n\t\t...(verification === undefined ? {} : { verification }),\n\t\t...(error === undefined ? {} : { error }),\n\t});\n}\n\nexport function buildPluginResultV1(input: PluginResultV1): PluginResultV1 {\n\treturn validatePluginResultV1(input);\n}\n\nexport function assertJsonResultInputV1(value: unknown, label = \"Result input\"): JsonValue {\n\tassertJsonValue(value, label);\n\treturn detachedFreeze(value);\n}\n"]}