@nifrajs/agent-telemetry 2.14.1 → 3.2.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/README.md +29 -0
- package/dist/index.d.ts +32 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +183 -0
- package/dist/index.js.map +1 -1
- package/dist/run.d.ts +72 -0
- package/dist/run.d.ts.map +1 -0
- package/dist/run.js +106 -0
- package/dist/run.js.map +1 -0
- package/package.json +3 -3
- package/src/index.ts +243 -0
- package/src/run.ts +159 -0
package/README.md
CHANGED
|
@@ -16,6 +16,35 @@ Requests under `/_nifra/tool/*` and requests to `/mcp` receive tool-name, input-
|
|
|
16
16
|
duration, and status observations. When `@nifrajs/otel` owns the request observation, tool spans are
|
|
17
17
|
attached as children; otherwise the plugin creates a standalone observation. Other routes pass through.
|
|
18
18
|
|
|
19
|
+
## Tracing agent runs
|
|
20
|
+
|
|
21
|
+
`traceAgentRun` turns a `@nifrajs/agent` run's step evidence into an OpenTelemetry trace: one
|
|
22
|
+
`nifra.agent.run` span per run, one child span per evidence item, with tool names, effect ids, error
|
|
23
|
+
codes, and effect-ledger heads carried as span attributes. Only the runner's constrained evidence
|
|
24
|
+
fields are exported - prompts, tool inputs, and outputs cannot enter a trace.
|
|
25
|
+
|
|
26
|
+
Orchestration correlation is opt-in and cardinality-bounded. Allowed attributes are run, plan, node,
|
|
27
|
+
attempt, evidence, replay, and trace coordinates plus bounded status/error codes. Content-bearing or
|
|
28
|
+
unbounded attributes are rejected; the exporter is not a payload retention sink.
|
|
29
|
+
|
|
30
|
+
```ts
|
|
31
|
+
import { traceAgentRun } from "@nifrajs/agent-telemetry"
|
|
32
|
+
import { combineAgentTelemetry, runAgent } from "@nifrajs/agent"
|
|
33
|
+
|
|
34
|
+
const trace = traceAgentRun({ agent: agent.name, turnId, exporter })
|
|
35
|
+
const result = await runAgent(agent, input, {
|
|
36
|
+
...ports,
|
|
37
|
+
telemetry: combineAgentTelemetry(ports.telemetry, trace.telemetry),
|
|
38
|
+
})
|
|
39
|
+
trace.end(result)
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Model evidence arrives as a started/terminal pair and becomes one timed `nifra.agent.model` span;
|
|
43
|
+
tool, approval, budget, and state evidence are terminal-only and become instant children. Pass
|
|
44
|
+
`parent` (for example the enclosing request observation's `context`) to nest runs under the HTTP
|
|
45
|
+
span, or `traceparent` to continue an inbound W3C trace; `trace.context` is the run span's identity,
|
|
46
|
+
forwardable with `traceHeaders`. Telemetry is fail-open: a throwing exporter never fails the turn.
|
|
47
|
+
|
|
19
48
|
## For AI agents
|
|
20
49
|
|
|
21
50
|
Start with [`LLM.md`](./LLM.md) - this package's contract card (the exports you call + its footguns),
|
package/dist/index.d.ts
CHANGED
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
* routes on the sync fast path unless hooks are present at registration time.
|
|
10
10
|
*/
|
|
11
11
|
import { type ActiveObservation, type ObservationAdapter } from "@nifrajs/otel";
|
|
12
|
+
export { type AgentRunEvidence, type AgentRunOutcome, type AgentRunTrace, type TraceAgentRunOptions, traceAgentRun, } from "./run.js";
|
|
12
13
|
export interface AgentTelemetryOptions {
|
|
13
14
|
/** Exporter that receives completed tool-call spans. */
|
|
14
15
|
readonly exporter: ObservationAdapter;
|
|
@@ -50,5 +51,35 @@ export declare function agentTelemetry(options: AgentTelemetryOptions): {
|
|
|
50
51
|
* ```
|
|
51
52
|
*/
|
|
52
53
|
export declare function consoleAgentExporter(log?: (line: string) => void): ObservationAdapter;
|
|
53
|
-
|
|
54
|
+
/** Content-free lifecycle kinds emitted by run dispatch and recovery. */
|
|
55
|
+
export type OrchestrationTelemetryKind = "started" | "checkpointed" | "retrying" | "recovered" | "cancelled" | "completed" | "failed" | "dead-lettered";
|
|
56
|
+
/** Allowlisted correlation fields. No index signature is intentional: arbitrary attributes are rejected. */
|
|
57
|
+
export interface OrchestrationTelemetryEvent {
|
|
58
|
+
readonly kind: OrchestrationTelemetryKind;
|
|
59
|
+
readonly runId: string;
|
|
60
|
+
readonly planDigest: string;
|
|
61
|
+
readonly nodeId: string;
|
|
62
|
+
readonly attempt: number;
|
|
63
|
+
readonly evidenceSeq: number;
|
|
64
|
+
readonly at: number;
|
|
65
|
+
readonly evidenceId?: string;
|
|
66
|
+
readonly replayId?: string;
|
|
67
|
+
readonly traceRef?: string;
|
|
68
|
+
readonly scheduleToken?: string;
|
|
69
|
+
readonly statusCode?: string;
|
|
70
|
+
}
|
|
71
|
+
export interface OrchestrationTelemetryOptions {
|
|
72
|
+
/** No exporter means disabled telemetry and zero spans. */
|
|
73
|
+
readonly exporter?: ObservationAdapter;
|
|
74
|
+
readonly adapters?: readonly ObservationAdapter[];
|
|
75
|
+
readonly maxDistinctValues?: number;
|
|
76
|
+
}
|
|
77
|
+
export interface OrchestrationTelemetry {
|
|
78
|
+
readonly enabled: boolean;
|
|
79
|
+
readonly dropped: number;
|
|
80
|
+
record(event: OrchestrationTelemetryEvent | unknown): boolean;
|
|
81
|
+
close(): void;
|
|
82
|
+
}
|
|
83
|
+
/** Create an opt-in, bounded telemetry bridge for run/retry/recovery correlation. */
|
|
84
|
+
export declare function orchestrationTelemetry(options?: OrchestrationTelemetryOptions): OrchestrationTelemetry;
|
|
54
85
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EACL,KAAK,iBAAiB,EAEtB,KAAK,kBAAkB,EACxB,MAAM,eAAe,CAAA;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EACL,KAAK,iBAAiB,EAEtB,KAAK,kBAAkB,EACxB,MAAM,eAAe,CAAA;AAEtB,OAAO,EACL,KAAK,gBAAgB,EACrB,KAAK,eAAe,EACpB,KAAK,aAAa,EAClB,KAAK,oBAAoB,EACzB,aAAa,GACd,MAAM,UAAU,CAAA;AAMjB,MAAM,WAAW,qBAAqB;IACpC,wDAAwD;IACxD,QAAQ,CAAC,QAAQ,EAAE,kBAAkB,CAAA;IACrC,qEAAqE;IACrE,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IAC5C,oDAAoD;IACpD,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;CACtC;AAED,gEAAgE;AAChE,UAAU,WAAW;IACnB,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAA;IACzB,QAAQ,CAAC,KAAK,CAAC,EAAE;QACf,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;QACxB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;QACvB,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAA;KAC1B,CAAA;IACD,2EAA2E;IAC3E,QAAQ,CAAC,WAAW,CAAC,EAAE,iBAAiB,CAAA;CACzC;AAMD;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,qBAAqB;;0BAUnC,WAAW;mBAiClB,OAAO,WAAW,WAAW;yBAMvB,QAAQ,WAAW,OAAO;EAqDlD;AAMD;;;;;;;GAOG;AACH,wBAAgB,oBAAoB,CAClC,GAAG,GAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAEtB,GACA,kBAAkB,CAWpB;AAMD,yEAAyE;AACzE,MAAM,MAAM,0BAA0B,GAClC,SAAS,GACT,cAAc,GACd,UAAU,GACV,WAAW,GACX,WAAW,GACX,WAAW,GACX,QAAQ,GACR,eAAe,CAAA;AAEnB,4GAA4G;AAC5G,MAAM,WAAW,2BAA2B;IAC1C,QAAQ,CAAC,IAAI,EAAE,0BAA0B,CAAA;IACzC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAA;IAC3B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IACxB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAA;IAC5B,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;IACnB,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAA;IAC5B,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;IAC1B,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;IAC1B,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAA;IAC/B,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAA;CAC7B;AAED,MAAM,WAAW,6BAA6B;IAC5C,2DAA2D;IAC3D,QAAQ,CAAC,QAAQ,CAAC,EAAE,kBAAkB,CAAA;IACtC,QAAQ,CAAC,QAAQ,CAAC,EAAE,SAAS,kBAAkB,EAAE,CAAA;IACjD,QAAQ,CAAC,iBAAiB,CAAC,EAAE,MAAM,CAAA;CACpC;AAED,MAAM,WAAW,sBAAsB;IACrC,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAA;IACzB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IACxB,MAAM,CAAC,KAAK,EAAE,2BAA2B,GAAG,OAAO,GAAG,OAAO,CAAA;IAC7D,KAAK,IAAI,IAAI,CAAA;CACd;AAiID,qFAAqF;AACrF,wBAAgB,sBAAsB,CACpC,OAAO,GAAE,6BAAkC,GAC1C,sBAAsB,CA0DxB"}
|
package/dist/index.js
CHANGED
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
* routes on the sync fast path unless hooks are present at registration time.
|
|
10
10
|
*/
|
|
11
11
|
import { createObservationLifecycle, } from "@nifrajs/otel";
|
|
12
|
+
export { traceAgentRun, } from "./run.js";
|
|
12
13
|
// ---------------------------------------------------------------------------
|
|
13
14
|
// Middleware
|
|
14
15
|
// ---------------------------------------------------------------------------
|
|
@@ -135,4 +136,186 @@ export function consoleAgentExporter(log = (line) => {
|
|
|
135
136
|
},
|
|
136
137
|
};
|
|
137
138
|
}
|
|
139
|
+
const ORCHESTRATION_TOKEN = /^[A-Za-z0-9._:-]{1,128}$/;
|
|
140
|
+
const ORCHESTRATION_DIGEST = /^[0-9a-f]{64}$/;
|
|
141
|
+
const ORCHESTRATION_KINDS = new Set([
|
|
142
|
+
"started",
|
|
143
|
+
"checkpointed",
|
|
144
|
+
"retrying",
|
|
145
|
+
"recovered",
|
|
146
|
+
"cancelled",
|
|
147
|
+
"completed",
|
|
148
|
+
"failed",
|
|
149
|
+
"dead-lettered",
|
|
150
|
+
]);
|
|
151
|
+
const ORCHESTRATION_FIELDS = new Set([
|
|
152
|
+
"kind",
|
|
153
|
+
"runId",
|
|
154
|
+
"planDigest",
|
|
155
|
+
"nodeId",
|
|
156
|
+
"attempt",
|
|
157
|
+
"evidenceSeq",
|
|
158
|
+
"at",
|
|
159
|
+
"evidenceId",
|
|
160
|
+
"replayId",
|
|
161
|
+
"traceRef",
|
|
162
|
+
"scheduleToken",
|
|
163
|
+
"statusCode",
|
|
164
|
+
]);
|
|
165
|
+
const ORCHESTRATION_FORBIDDEN = new Set([
|
|
166
|
+
"prompt",
|
|
167
|
+
"message",
|
|
168
|
+
"text",
|
|
169
|
+
"input",
|
|
170
|
+
"output",
|
|
171
|
+
"arguments",
|
|
172
|
+
"body",
|
|
173
|
+
"response",
|
|
174
|
+
"secret",
|
|
175
|
+
"credential",
|
|
176
|
+
"diagnostic",
|
|
177
|
+
"stack",
|
|
178
|
+
"content",
|
|
179
|
+
"transcript",
|
|
180
|
+
"artifact",
|
|
181
|
+
"path",
|
|
182
|
+
]);
|
|
183
|
+
function orchestrationRecord(value) {
|
|
184
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
185
|
+
}
|
|
186
|
+
function boundedTelemetryToken(value) {
|
|
187
|
+
return typeof value === "string" && ORCHESTRATION_TOKEN.test(value);
|
|
188
|
+
}
|
|
189
|
+
function parseOrchestrationEvent(value) {
|
|
190
|
+
if (!orchestrationRecord(value))
|
|
191
|
+
return undefined;
|
|
192
|
+
for (const key of Object.keys(value)) {
|
|
193
|
+
if (ORCHESTRATION_FORBIDDEN.has(key.toLowerCase()) || !ORCHESTRATION_FIELDS.has(key))
|
|
194
|
+
return undefined;
|
|
195
|
+
}
|
|
196
|
+
if (typeof value.kind !== "string" ||
|
|
197
|
+
!ORCHESTRATION_KINDS.has(value.kind) ||
|
|
198
|
+
!boundedTelemetryToken(value.runId) ||
|
|
199
|
+
!boundedTelemetryToken(value.nodeId) ||
|
|
200
|
+
typeof value.planDigest !== "string" ||
|
|
201
|
+
!ORCHESTRATION_DIGEST.test(value.planDigest) ||
|
|
202
|
+
typeof value.at !== "number" ||
|
|
203
|
+
!Number.isSafeInteger(value.at) ||
|
|
204
|
+
value.at < 0 ||
|
|
205
|
+
typeof value.attempt !== "number" ||
|
|
206
|
+
!Number.isSafeInteger(value.attempt) ||
|
|
207
|
+
value.attempt < 1 ||
|
|
208
|
+
typeof value.evidenceSeq !== "number" ||
|
|
209
|
+
!Number.isSafeInteger(value.evidenceSeq) ||
|
|
210
|
+
value.evidenceSeq < 0)
|
|
211
|
+
return undefined;
|
|
212
|
+
for (const key of [
|
|
213
|
+
"evidenceId",
|
|
214
|
+
"replayId",
|
|
215
|
+
"traceRef",
|
|
216
|
+
"scheduleToken",
|
|
217
|
+
"statusCode",
|
|
218
|
+
]) {
|
|
219
|
+
const candidate = value[key];
|
|
220
|
+
if (candidate !== undefined && !boundedTelemetryToken(candidate))
|
|
221
|
+
return undefined;
|
|
222
|
+
}
|
|
223
|
+
return Object.freeze({
|
|
224
|
+
kind: value.kind,
|
|
225
|
+
runId: value.runId,
|
|
226
|
+
planDigest: value.planDigest,
|
|
227
|
+
nodeId: value.nodeId,
|
|
228
|
+
attempt: value.attempt,
|
|
229
|
+
evidenceSeq: value.evidenceSeq,
|
|
230
|
+
at: value.at,
|
|
231
|
+
...(value.evidenceId === undefined ? {} : { evidenceId: value.evidenceId }),
|
|
232
|
+
...(value.replayId === undefined ? {} : { replayId: value.replayId }),
|
|
233
|
+
...(value.traceRef === undefined ? {} : { traceRef: value.traceRef }),
|
|
234
|
+
...(value.scheduleToken === undefined ? {} : { scheduleToken: value.scheduleToken }),
|
|
235
|
+
...(value.statusCode === undefined ? {} : { statusCode: value.statusCode }),
|
|
236
|
+
});
|
|
237
|
+
}
|
|
238
|
+
function orchestrationAttributes(event) {
|
|
239
|
+
return {
|
|
240
|
+
"nifra.orchestration.run_id": event.runId,
|
|
241
|
+
"nifra.orchestration.plan_digest": event.planDigest,
|
|
242
|
+
"nifra.orchestration.node_id": event.nodeId,
|
|
243
|
+
"nifra.orchestration.attempt": event.attempt,
|
|
244
|
+
"nifra.orchestration.evidence_seq": event.evidenceSeq,
|
|
245
|
+
"nifra.orchestration.at": event.at,
|
|
246
|
+
...(event.evidenceId === undefined
|
|
247
|
+
? {}
|
|
248
|
+
: { "nifra.orchestration.evidence_id": event.evidenceId }),
|
|
249
|
+
...(event.replayId === undefined ? {} : { "nifra.orchestration.replay_id": event.replayId }),
|
|
250
|
+
...(event.traceRef === undefined ? {} : { "nifra.orchestration.trace_ref": event.traceRef }),
|
|
251
|
+
...(event.scheduleToken === undefined
|
|
252
|
+
? {}
|
|
253
|
+
: { "nifra.orchestration.schedule": event.scheduleToken }),
|
|
254
|
+
...(event.statusCode === undefined
|
|
255
|
+
? {}
|
|
256
|
+
: { "nifra.orchestration.status_code": event.statusCode }),
|
|
257
|
+
};
|
|
258
|
+
}
|
|
259
|
+
/** Create an opt-in, bounded telemetry bridge for run/retry/recovery correlation. */
|
|
260
|
+
export function orchestrationTelemetry(options = {}) {
|
|
261
|
+
const adapters = [
|
|
262
|
+
...(options.exporter === undefined ? [] : [options.exporter]),
|
|
263
|
+
...(options.adapters ?? []),
|
|
264
|
+
];
|
|
265
|
+
const enabled = adapters.length > 0;
|
|
266
|
+
const maxDistinct = options.maxDistinctValues ?? 512;
|
|
267
|
+
if (!Number.isSafeInteger(maxDistinct) || maxDistinct < 1 || maxDistinct > 10_000)
|
|
268
|
+
throw new RangeError("orchestration telemetry maxDistinctValues is invalid");
|
|
269
|
+
if (!enabled)
|
|
270
|
+
return {
|
|
271
|
+
enabled: false,
|
|
272
|
+
get dropped() {
|
|
273
|
+
return 0;
|
|
274
|
+
},
|
|
275
|
+
record: () => false,
|
|
276
|
+
close: () => undefined,
|
|
277
|
+
};
|
|
278
|
+
const lifecycle = createObservationLifecycle({ adapters });
|
|
279
|
+
const run = lifecycle.start({ name: "nifra.orchestration.run", attributes: {} });
|
|
280
|
+
const distinct = new Map();
|
|
281
|
+
let droppedCount = 0;
|
|
282
|
+
let closed = false;
|
|
283
|
+
const record = (raw) => {
|
|
284
|
+
if (closed)
|
|
285
|
+
return false;
|
|
286
|
+
const event = parseOrchestrationEvent(raw);
|
|
287
|
+
if (event === undefined) {
|
|
288
|
+
droppedCount = Math.min(1_000_000, droppedCount + 1);
|
|
289
|
+
return false;
|
|
290
|
+
}
|
|
291
|
+
const attrs = orchestrationAttributes(event);
|
|
292
|
+
for (const [key, value] of Object.entries(attrs)) {
|
|
293
|
+
if (typeof value !== "string")
|
|
294
|
+
continue;
|
|
295
|
+
const values = distinct.get(key) ?? new Set();
|
|
296
|
+
if (!values.has(value) && values.size >= maxDistinct) {
|
|
297
|
+
droppedCount = Math.min(1_000_000, droppedCount + 1);
|
|
298
|
+
return false;
|
|
299
|
+
}
|
|
300
|
+
values.add(value);
|
|
301
|
+
distinct.set(key, values);
|
|
302
|
+
}
|
|
303
|
+
const status = event.kind === "failed" || event.kind === "dead-lettered" ? "error" : "ok";
|
|
304
|
+
run.startChild({ name: `nifra.orchestration.${event.kind}`, attributes: attrs }).end({ status });
|
|
305
|
+
return true;
|
|
306
|
+
};
|
|
307
|
+
return {
|
|
308
|
+
enabled: true,
|
|
309
|
+
get dropped() {
|
|
310
|
+
return droppedCount;
|
|
311
|
+
},
|
|
312
|
+
record,
|
|
313
|
+
close() {
|
|
314
|
+
if (closed)
|
|
315
|
+
return;
|
|
316
|
+
closed = true;
|
|
317
|
+
run.end({ status: "ok" });
|
|
318
|
+
},
|
|
319
|
+
};
|
|
320
|
+
}
|
|
138
321
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAEL,0BAA0B,GAE3B,MAAM,eAAe,CAAA;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/run.d.ts
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agent-run tracing - one OpenTelemetry span per bounded agent run, one child span per step
|
|
3
|
+
* evidence item, effect ledger heads carried as span attributes.
|
|
4
|
+
*
|
|
5
|
+
* The bridge consumes only the runner's constrained step-evidence contract (`seq`, `kind`,
|
|
6
|
+
* `outcome`, tool name, effect id, error code, ledger head): prompts, tool inputs, and outputs
|
|
7
|
+
* cannot enter an export. The evidence shape is declared structurally so the package stays
|
|
8
|
+
* dependency-free on `@nifrajs/agent`; the returned `telemetry` object satisfies its
|
|
9
|
+
* `AgentTelemetryPort` as-is.
|
|
10
|
+
*/
|
|
11
|
+
import { type ObservationAdapter, type ObservationContext, type ObservationParent } from "@nifrajs/otel";
|
|
12
|
+
/** Structural twin of `@nifrajs/agent`'s `AgentStepEvidence`. */
|
|
13
|
+
export interface AgentRunEvidence {
|
|
14
|
+
readonly seq: number;
|
|
15
|
+
readonly at: number;
|
|
16
|
+
readonly kind: "model" | "tool" | "approval" | "budget" | "state";
|
|
17
|
+
readonly outcome: "started" | "passed" | "failed" | "denied" | "suspended" | "committed";
|
|
18
|
+
readonly name?: string;
|
|
19
|
+
readonly effectId?: string;
|
|
20
|
+
readonly code?: string;
|
|
21
|
+
readonly ledgerHead?: string;
|
|
22
|
+
}
|
|
23
|
+
/** Structural slice of the runner's `AgentRunResult` that `end()` reads. */
|
|
24
|
+
export interface AgentRunOutcome {
|
|
25
|
+
readonly status: "completed" | "suspended" | "continue";
|
|
26
|
+
readonly error?: {
|
|
27
|
+
readonly code: string;
|
|
28
|
+
};
|
|
29
|
+
readonly reason?: string;
|
|
30
|
+
}
|
|
31
|
+
export interface TraceAgentRunOptions {
|
|
32
|
+
/** Agent name, recorded as `nifra.agent.name`. */
|
|
33
|
+
readonly agent: string;
|
|
34
|
+
/** Turn id, recorded as `nifra.agent.turn_id`. */
|
|
35
|
+
readonly turnId: string;
|
|
36
|
+
/** Exporter that receives the run span and its evidence child spans. */
|
|
37
|
+
readonly exporter?: ObservationAdapter;
|
|
38
|
+
readonly adapters?: readonly ObservationAdapter[];
|
|
39
|
+
/**
|
|
40
|
+
* Parent span for the run - pass the enclosing request observation's `context` to nest agent
|
|
41
|
+
* runs under the HTTP span. `null` forces a root span; `undefined` falls back to `traceparent`.
|
|
42
|
+
*/
|
|
43
|
+
readonly parent?: ObservationParent | null;
|
|
44
|
+
/** Inbound W3C header used when `parent` is undefined. */
|
|
45
|
+
readonly traceparent?: string | null;
|
|
46
|
+
}
|
|
47
|
+
export interface AgentRunTrace {
|
|
48
|
+
/** Pass as (or combine into) `ports.telemetry`. Never throws; a failing adapter cannot fail the turn. */
|
|
49
|
+
readonly telemetry: {
|
|
50
|
+
step(evidence: AgentRunEvidence): void;
|
|
51
|
+
};
|
|
52
|
+
/** The run span's trace identity, forwardable with `traceHeaders`. */
|
|
53
|
+
readonly context: ObservationContext;
|
|
54
|
+
/**
|
|
55
|
+
* End the run span. Pass the runner's result to record the final status
|
|
56
|
+
* (`nifra.agent.status`, pending reason, error code); omit it when the run threw.
|
|
57
|
+
* Exactly-once: repeated calls are ignored.
|
|
58
|
+
*/
|
|
59
|
+
end(result?: AgentRunOutcome): void;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Open a run span and adapt the runner's step evidence into child spans.
|
|
63
|
+
*
|
|
64
|
+
* const trace = traceAgentRun({ agent: agent.name, turnId, exporter, parent: c.observation?.context })
|
|
65
|
+
* const result = await runAgent(agent, input, { ...ports, telemetry: trace.telemetry }, options)
|
|
66
|
+
* trace.end(result)
|
|
67
|
+
*
|
|
68
|
+
* Model evidence arrives as a started/terminal pair and becomes one timed `nifra.agent.model`
|
|
69
|
+
* span; every other evidence item is terminal-only and becomes an instant child span.
|
|
70
|
+
*/
|
|
71
|
+
export declare function traceAgentRun(options: TraceAgentRunOptions): AgentRunTrace;
|
|
72
|
+
//# sourceMappingURL=run.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../src/run.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAIL,KAAK,kBAAkB,EACvB,KAAK,kBAAkB,EACvB,KAAK,iBAAiB,EACvB,MAAM,eAAe,CAAA;AAEtB,iEAAiE;AACjE,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;IACpB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;IACnB,QAAQ,CAAC,IAAI,EAAE,OAAO,GAAG,MAAM,GAAG,UAAU,GAAG,QAAQ,GAAG,OAAO,CAAA;IACjE,QAAQ,CAAC,OAAO,EAAE,SAAS,GAAG,QAAQ,GAAG,QAAQ,GAAG,QAAQ,GAAG,WAAW,GAAG,WAAW,CAAA;IACxF,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;IAC1B,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAA;CAC7B;AAED,4EAA4E;AAC5E,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,MAAM,EAAE,WAAW,GAAG,WAAW,GAAG,UAAU,CAAA;IACvD,QAAQ,CAAC,KAAK,CAAC,EAAE;QAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAA;IAC1C,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CACzB;AAED,MAAM,WAAW,oBAAoB;IACnC,kDAAkD;IAClD,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,kDAAkD;IAClD,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,wEAAwE;IACxE,QAAQ,CAAC,QAAQ,CAAC,EAAE,kBAAkB,CAAA;IACtC,QAAQ,CAAC,QAAQ,CAAC,EAAE,SAAS,kBAAkB,EAAE,CAAA;IACjD;;;OAGG;IACH,QAAQ,CAAC,MAAM,CAAC,EAAE,iBAAiB,GAAG,IAAI,CAAA;IAC1C,0DAA0D;IAC1D,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CACrC;AAED,MAAM,WAAW,aAAa;IAC5B,yGAAyG;IACzG,QAAQ,CAAC,SAAS,EAAE;QAAE,IAAI,CAAC,QAAQ,EAAE,gBAAgB,GAAG,IAAI,CAAA;KAAE,CAAA;IAC9D,sEAAsE;IACtE,QAAQ,CAAC,OAAO,EAAE,kBAAkB,CAAA;IACpC;;;;OAIG;IACH,GAAG,CAAC,MAAM,CAAC,EAAE,eAAe,GAAG,IAAI,CAAA;CACpC;AAcD;;;;;;;;;GASG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,oBAAoB,GAAG,aAAa,CAmE1E"}
|
package/dist/run.js
ADDED
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agent-run tracing - one OpenTelemetry span per bounded agent run, one child span per step
|
|
3
|
+
* evidence item, effect ledger heads carried as span attributes.
|
|
4
|
+
*
|
|
5
|
+
* The bridge consumes only the runner's constrained step-evidence contract (`seq`, `kind`,
|
|
6
|
+
* `outcome`, tool name, effect id, error code, ledger head): prompts, tool inputs, and outputs
|
|
7
|
+
* cannot enter an export. The evidence shape is declared structurally so the package stays
|
|
8
|
+
* dependency-free on `@nifrajs/agent`; the returned `telemetry` object satisfies its
|
|
9
|
+
* `AgentTelemetryPort` as-is.
|
|
10
|
+
*/
|
|
11
|
+
import { createObservationLifecycle, } from "@nifrajs/otel";
|
|
12
|
+
function evidenceAttributes(evidence) {
|
|
13
|
+
const attributes = {
|
|
14
|
+
"nifra.agent.evidence.seq": evidence.seq,
|
|
15
|
+
"nifra.agent.evidence.outcome": evidence.outcome,
|
|
16
|
+
};
|
|
17
|
+
if (evidence.name !== undefined)
|
|
18
|
+
attributes["tool.name"] = evidence.name;
|
|
19
|
+
if (evidence.effectId !== undefined)
|
|
20
|
+
attributes["nifra.effect.id"] = evidence.effectId;
|
|
21
|
+
if (evidence.code !== undefined)
|
|
22
|
+
attributes["nifra.agent.error_code"] = evidence.code;
|
|
23
|
+
if (evidence.ledgerHead !== undefined)
|
|
24
|
+
attributes["nifra.ledger.head"] = evidence.ledgerHead;
|
|
25
|
+
return attributes;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Open a run span and adapt the runner's step evidence into child spans.
|
|
29
|
+
*
|
|
30
|
+
* const trace = traceAgentRun({ agent: agent.name, turnId, exporter, parent: c.observation?.context })
|
|
31
|
+
* const result = await runAgent(agent, input, { ...ports, telemetry: trace.telemetry }, options)
|
|
32
|
+
* trace.end(result)
|
|
33
|
+
*
|
|
34
|
+
* Model evidence arrives as a started/terminal pair and becomes one timed `nifra.agent.model`
|
|
35
|
+
* span; every other evidence item is terminal-only and becomes an instant child span.
|
|
36
|
+
*/
|
|
37
|
+
export function traceAgentRun(options) {
|
|
38
|
+
const adapters = [
|
|
39
|
+
...(options.exporter === undefined ? [] : [options.exporter]),
|
|
40
|
+
...(options.adapters ?? []),
|
|
41
|
+
];
|
|
42
|
+
const lifecycle = createObservationLifecycle({ adapters });
|
|
43
|
+
const run = lifecycle.start({
|
|
44
|
+
name: "nifra.agent.run",
|
|
45
|
+
...(options.parent === undefined ? {} : { parent: options.parent }),
|
|
46
|
+
...(options.traceparent === undefined ? {} : { traceparent: options.traceparent }),
|
|
47
|
+
attributes: {
|
|
48
|
+
"nifra.agent.name": options.agent,
|
|
49
|
+
"nifra.agent.turn_id": options.turnId,
|
|
50
|
+
},
|
|
51
|
+
});
|
|
52
|
+
// The runner is sequential - at most one model call is in flight, so a single slot pairs the
|
|
53
|
+
// `started` item with its terminal twin. Kinds other than "model" emit terminal items only.
|
|
54
|
+
let openModel;
|
|
55
|
+
const step = (evidence) => {
|
|
56
|
+
try {
|
|
57
|
+
if (evidence.kind === "model" && evidence.outcome === "started") {
|
|
58
|
+
openModel?.end({ status: "error", attributes: { "nifra.agent.error_code": "unpaired" } });
|
|
59
|
+
openModel = run.startChild({
|
|
60
|
+
name: "nifra.agent.model",
|
|
61
|
+
attributes: evidenceAttributes(evidence),
|
|
62
|
+
});
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
const failed = evidence.outcome === "failed" || evidence.outcome === "denied";
|
|
66
|
+
const status = failed ? "error" : "ok";
|
|
67
|
+
if (evidence.kind === "model" && openModel !== undefined) {
|
|
68
|
+
const span = openModel;
|
|
69
|
+
openModel = undefined;
|
|
70
|
+
span.setAttributes(evidenceAttributes(evidence));
|
|
71
|
+
span.end({ status });
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
run
|
|
75
|
+
.startChild({
|
|
76
|
+
name: `nifra.agent.${evidence.kind}`,
|
|
77
|
+
attributes: evidenceAttributes(evidence),
|
|
78
|
+
})
|
|
79
|
+
.end({ status });
|
|
80
|
+
}
|
|
81
|
+
catch {
|
|
82
|
+
// Telemetry must never fail the turn it observes.
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
const end = (result) => {
|
|
86
|
+
try {
|
|
87
|
+
openModel?.end({ status: "error", attributes: { "nifra.agent.error_code": "run_ended" } });
|
|
88
|
+
openModel = undefined;
|
|
89
|
+
if (result === undefined) {
|
|
90
|
+
run.end({ status: "error", attributes: { "nifra.agent.status": "threw" } });
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
const attributes = { "nifra.agent.status": result.status };
|
|
94
|
+
if (result.reason !== undefined)
|
|
95
|
+
attributes["nifra.agent.pending_reason"] = result.reason;
|
|
96
|
+
if (result.error !== undefined)
|
|
97
|
+
attributes["nifra.agent.error_code"] = result.error.code;
|
|
98
|
+
run.end({ status: result.error === undefined ? "ok" : "error", attributes });
|
|
99
|
+
}
|
|
100
|
+
catch {
|
|
101
|
+
// Same fail-open rule as step().
|
|
102
|
+
}
|
|
103
|
+
};
|
|
104
|
+
return Object.freeze({ telemetry: Object.freeze({ step }), context: run.context, end });
|
|
105
|
+
}
|
|
106
|
+
//# sourceMappingURL=run.js.map
|
package/dist/run.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"run.js","sourceRoot":"","sources":["../src/run.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nifrajs/agent-telemetry",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "3.2.0",
|
|
4
4
|
"description": "Agent telemetry plugin - child spans for AI tool calls on /_nifra/tool/* and MCP endpoints.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"bun",
|
|
@@ -38,10 +38,10 @@
|
|
|
38
38
|
"access": "public"
|
|
39
39
|
},
|
|
40
40
|
"dependencies": {
|
|
41
|
-
"@nifrajs/otel": "2.
|
|
41
|
+
"@nifrajs/otel": "3.2.0"
|
|
42
42
|
},
|
|
43
43
|
"peerDependencies": {
|
|
44
|
-
"@nifrajs/core": "^2.
|
|
44
|
+
"@nifrajs/core": "^3.2.0"
|
|
45
45
|
},
|
|
46
46
|
"devDependencies": {
|
|
47
47
|
"@nifrajs/core": "workspace:*"
|
package/src/index.ts
CHANGED
|
@@ -15,6 +15,14 @@ import {
|
|
|
15
15
|
type ObservationAdapter,
|
|
16
16
|
} from "@nifrajs/otel"
|
|
17
17
|
|
|
18
|
+
export {
|
|
19
|
+
type AgentRunEvidence,
|
|
20
|
+
type AgentRunOutcome,
|
|
21
|
+
type AgentRunTrace,
|
|
22
|
+
type TraceAgentRunOptions,
|
|
23
|
+
traceAgentRun,
|
|
24
|
+
} from "./run.ts"
|
|
25
|
+
|
|
18
26
|
// ---------------------------------------------------------------------------
|
|
19
27
|
// Types
|
|
20
28
|
// ---------------------------------------------------------------------------
|
|
@@ -183,3 +191,238 @@ export function consoleAgentExporter(
|
|
|
183
191
|
},
|
|
184
192
|
}
|
|
185
193
|
}
|
|
194
|
+
|
|
195
|
+
// ---------------------------------------------------------------------------
|
|
196
|
+
// Orchestration correlation
|
|
197
|
+
// ---------------------------------------------------------------------------
|
|
198
|
+
|
|
199
|
+
/** Content-free lifecycle kinds emitted by run dispatch and recovery. */
|
|
200
|
+
export type OrchestrationTelemetryKind =
|
|
201
|
+
| "started"
|
|
202
|
+
| "checkpointed"
|
|
203
|
+
| "retrying"
|
|
204
|
+
| "recovered"
|
|
205
|
+
| "cancelled"
|
|
206
|
+
| "completed"
|
|
207
|
+
| "failed"
|
|
208
|
+
| "dead-lettered"
|
|
209
|
+
|
|
210
|
+
/** Allowlisted correlation fields. No index signature is intentional: arbitrary attributes are rejected. */
|
|
211
|
+
export interface OrchestrationTelemetryEvent {
|
|
212
|
+
readonly kind: OrchestrationTelemetryKind
|
|
213
|
+
readonly runId: string
|
|
214
|
+
readonly planDigest: string
|
|
215
|
+
readonly nodeId: string
|
|
216
|
+
readonly attempt: number
|
|
217
|
+
readonly evidenceSeq: number
|
|
218
|
+
readonly at: number
|
|
219
|
+
readonly evidenceId?: string
|
|
220
|
+
readonly replayId?: string
|
|
221
|
+
readonly traceRef?: string
|
|
222
|
+
readonly scheduleToken?: string
|
|
223
|
+
readonly statusCode?: string
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
export interface OrchestrationTelemetryOptions {
|
|
227
|
+
/** No exporter means disabled telemetry and zero spans. */
|
|
228
|
+
readonly exporter?: ObservationAdapter
|
|
229
|
+
readonly adapters?: readonly ObservationAdapter[]
|
|
230
|
+
readonly maxDistinctValues?: number
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
export interface OrchestrationTelemetry {
|
|
234
|
+
readonly enabled: boolean
|
|
235
|
+
readonly dropped: number
|
|
236
|
+
record(event: OrchestrationTelemetryEvent | unknown): boolean
|
|
237
|
+
close(): void
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
const ORCHESTRATION_TOKEN = /^[A-Za-z0-9._:-]{1,128}$/
|
|
241
|
+
const ORCHESTRATION_DIGEST = /^[0-9a-f]{64}$/
|
|
242
|
+
const ORCHESTRATION_KINDS: ReadonlySet<string> = new Set([
|
|
243
|
+
"started",
|
|
244
|
+
"checkpointed",
|
|
245
|
+
"retrying",
|
|
246
|
+
"recovered",
|
|
247
|
+
"cancelled",
|
|
248
|
+
"completed",
|
|
249
|
+
"failed",
|
|
250
|
+
"dead-lettered",
|
|
251
|
+
])
|
|
252
|
+
const ORCHESTRATION_FIELDS = new Set([
|
|
253
|
+
"kind",
|
|
254
|
+
"runId",
|
|
255
|
+
"planDigest",
|
|
256
|
+
"nodeId",
|
|
257
|
+
"attempt",
|
|
258
|
+
"evidenceSeq",
|
|
259
|
+
"at",
|
|
260
|
+
"evidenceId",
|
|
261
|
+
"replayId",
|
|
262
|
+
"traceRef",
|
|
263
|
+
"scheduleToken",
|
|
264
|
+
"statusCode",
|
|
265
|
+
])
|
|
266
|
+
const ORCHESTRATION_FORBIDDEN = new Set([
|
|
267
|
+
"prompt",
|
|
268
|
+
"message",
|
|
269
|
+
"text",
|
|
270
|
+
"input",
|
|
271
|
+
"output",
|
|
272
|
+
"arguments",
|
|
273
|
+
"body",
|
|
274
|
+
"response",
|
|
275
|
+
"secret",
|
|
276
|
+
"credential",
|
|
277
|
+
"diagnostic",
|
|
278
|
+
"stack",
|
|
279
|
+
"content",
|
|
280
|
+
"transcript",
|
|
281
|
+
"artifact",
|
|
282
|
+
"path",
|
|
283
|
+
])
|
|
284
|
+
|
|
285
|
+
function orchestrationRecord(value: unknown): value is Record<string, unknown> {
|
|
286
|
+
return typeof value === "object" && value !== null && !Array.isArray(value)
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
function boundedTelemetryToken(value: unknown): value is string {
|
|
290
|
+
return typeof value === "string" && ORCHESTRATION_TOKEN.test(value)
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
function parseOrchestrationEvent(value: unknown): OrchestrationTelemetryEvent | undefined {
|
|
294
|
+
if (!orchestrationRecord(value)) return undefined
|
|
295
|
+
for (const key of Object.keys(value)) {
|
|
296
|
+
if (ORCHESTRATION_FORBIDDEN.has(key.toLowerCase()) || !ORCHESTRATION_FIELDS.has(key))
|
|
297
|
+
return undefined
|
|
298
|
+
}
|
|
299
|
+
if (
|
|
300
|
+
typeof value.kind !== "string" ||
|
|
301
|
+
!ORCHESTRATION_KINDS.has(value.kind) ||
|
|
302
|
+
!boundedTelemetryToken(value.runId) ||
|
|
303
|
+
!boundedTelemetryToken(value.nodeId) ||
|
|
304
|
+
typeof value.planDigest !== "string" ||
|
|
305
|
+
!ORCHESTRATION_DIGEST.test(value.planDigest) ||
|
|
306
|
+
typeof value.at !== "number" ||
|
|
307
|
+
!Number.isSafeInteger(value.at) ||
|
|
308
|
+
value.at < 0 ||
|
|
309
|
+
typeof value.attempt !== "number" ||
|
|
310
|
+
!Number.isSafeInteger(value.attempt) ||
|
|
311
|
+
value.attempt < 1 ||
|
|
312
|
+
typeof value.evidenceSeq !== "number" ||
|
|
313
|
+
!Number.isSafeInteger(value.evidenceSeq) ||
|
|
314
|
+
value.evidenceSeq < 0
|
|
315
|
+
)
|
|
316
|
+
return undefined
|
|
317
|
+
for (const key of [
|
|
318
|
+
"evidenceId",
|
|
319
|
+
"replayId",
|
|
320
|
+
"traceRef",
|
|
321
|
+
"scheduleToken",
|
|
322
|
+
"statusCode",
|
|
323
|
+
] as const) {
|
|
324
|
+
const candidate = value[key]
|
|
325
|
+
if (candidate !== undefined && !boundedTelemetryToken(candidate)) return undefined
|
|
326
|
+
}
|
|
327
|
+
return Object.freeze({
|
|
328
|
+
kind: value.kind as OrchestrationTelemetryKind,
|
|
329
|
+
runId: value.runId,
|
|
330
|
+
planDigest: value.planDigest,
|
|
331
|
+
nodeId: value.nodeId,
|
|
332
|
+
attempt: value.attempt,
|
|
333
|
+
evidenceSeq: value.evidenceSeq,
|
|
334
|
+
at: value.at,
|
|
335
|
+
...(value.evidenceId === undefined ? {} : { evidenceId: value.evidenceId as string }),
|
|
336
|
+
...(value.replayId === undefined ? {} : { replayId: value.replayId as string }),
|
|
337
|
+
...(value.traceRef === undefined ? {} : { traceRef: value.traceRef as string }),
|
|
338
|
+
...(value.scheduleToken === undefined ? {} : { scheduleToken: value.scheduleToken as string }),
|
|
339
|
+
...(value.statusCode === undefined ? {} : { statusCode: value.statusCode as string }),
|
|
340
|
+
})
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
function orchestrationAttributes(
|
|
344
|
+
event: OrchestrationTelemetryEvent,
|
|
345
|
+
): Record<string, string | number> {
|
|
346
|
+
return {
|
|
347
|
+
"nifra.orchestration.run_id": event.runId,
|
|
348
|
+
"nifra.orchestration.plan_digest": event.planDigest,
|
|
349
|
+
"nifra.orchestration.node_id": event.nodeId,
|
|
350
|
+
"nifra.orchestration.attempt": event.attempt,
|
|
351
|
+
"nifra.orchestration.evidence_seq": event.evidenceSeq,
|
|
352
|
+
"nifra.orchestration.at": event.at,
|
|
353
|
+
...(event.evidenceId === undefined
|
|
354
|
+
? {}
|
|
355
|
+
: { "nifra.orchestration.evidence_id": event.evidenceId }),
|
|
356
|
+
...(event.replayId === undefined ? {} : { "nifra.orchestration.replay_id": event.replayId }),
|
|
357
|
+
...(event.traceRef === undefined ? {} : { "nifra.orchestration.trace_ref": event.traceRef }),
|
|
358
|
+
...(event.scheduleToken === undefined
|
|
359
|
+
? {}
|
|
360
|
+
: { "nifra.orchestration.schedule": event.scheduleToken }),
|
|
361
|
+
...(event.statusCode === undefined
|
|
362
|
+
? {}
|
|
363
|
+
: { "nifra.orchestration.status_code": event.statusCode }),
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
/** Create an opt-in, bounded telemetry bridge for run/retry/recovery correlation. */
|
|
368
|
+
export function orchestrationTelemetry(
|
|
369
|
+
options: OrchestrationTelemetryOptions = {},
|
|
370
|
+
): OrchestrationTelemetry {
|
|
371
|
+
const adapters = [
|
|
372
|
+
...(options.exporter === undefined ? [] : [options.exporter]),
|
|
373
|
+
...(options.adapters ?? []),
|
|
374
|
+
]
|
|
375
|
+
const enabled = adapters.length > 0
|
|
376
|
+
const maxDistinct = options.maxDistinctValues ?? 512
|
|
377
|
+
if (!Number.isSafeInteger(maxDistinct) || maxDistinct < 1 || maxDistinct > 10_000)
|
|
378
|
+
throw new RangeError("orchestration telemetry maxDistinctValues is invalid")
|
|
379
|
+
if (!enabled)
|
|
380
|
+
return {
|
|
381
|
+
enabled: false,
|
|
382
|
+
get dropped() {
|
|
383
|
+
return 0
|
|
384
|
+
},
|
|
385
|
+
record: () => false,
|
|
386
|
+
close: () => undefined,
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
const lifecycle = createObservationLifecycle({ adapters })
|
|
390
|
+
const run = lifecycle.start({ name: "nifra.orchestration.run", attributes: {} })
|
|
391
|
+
const distinct = new Map<string, Set<string>>()
|
|
392
|
+
let droppedCount = 0
|
|
393
|
+
let closed = false
|
|
394
|
+
const record = (raw: OrchestrationTelemetryEvent | unknown): boolean => {
|
|
395
|
+
if (closed) return false
|
|
396
|
+
const event = parseOrchestrationEvent(raw)
|
|
397
|
+
if (event === undefined) {
|
|
398
|
+
droppedCount = Math.min(1_000_000, droppedCount + 1)
|
|
399
|
+
return false
|
|
400
|
+
}
|
|
401
|
+
const attrs = orchestrationAttributes(event)
|
|
402
|
+
for (const [key, value] of Object.entries(attrs)) {
|
|
403
|
+
if (typeof value !== "string") continue
|
|
404
|
+
const values = distinct.get(key) ?? new Set<string>()
|
|
405
|
+
if (!values.has(value) && values.size >= maxDistinct) {
|
|
406
|
+
droppedCount = Math.min(1_000_000, droppedCount + 1)
|
|
407
|
+
return false
|
|
408
|
+
}
|
|
409
|
+
values.add(value)
|
|
410
|
+
distinct.set(key, values)
|
|
411
|
+
}
|
|
412
|
+
const status = event.kind === "failed" || event.kind === "dead-lettered" ? "error" : "ok"
|
|
413
|
+
run.startChild({ name: `nifra.orchestration.${event.kind}`, attributes: attrs }).end({ status })
|
|
414
|
+
return true
|
|
415
|
+
}
|
|
416
|
+
return {
|
|
417
|
+
enabled: true,
|
|
418
|
+
get dropped() {
|
|
419
|
+
return droppedCount
|
|
420
|
+
},
|
|
421
|
+
record,
|
|
422
|
+
close() {
|
|
423
|
+
if (closed) return
|
|
424
|
+
closed = true
|
|
425
|
+
run.end({ status: "ok" })
|
|
426
|
+
},
|
|
427
|
+
}
|
|
428
|
+
}
|
package/src/run.ts
ADDED
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agent-run tracing - one OpenTelemetry span per bounded agent run, one child span per step
|
|
3
|
+
* evidence item, effect ledger heads carried as span attributes.
|
|
4
|
+
*
|
|
5
|
+
* The bridge consumes only the runner's constrained step-evidence contract (`seq`, `kind`,
|
|
6
|
+
* `outcome`, tool name, effect id, error code, ledger head): prompts, tool inputs, and outputs
|
|
7
|
+
* cannot enter an export. The evidence shape is declared structurally so the package stays
|
|
8
|
+
* dependency-free on `@nifrajs/agent`; the returned `telemetry` object satisfies its
|
|
9
|
+
* `AgentTelemetryPort` as-is.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import {
|
|
13
|
+
type ActiveObservation,
|
|
14
|
+
type AttributeValue,
|
|
15
|
+
createObservationLifecycle,
|
|
16
|
+
type ObservationAdapter,
|
|
17
|
+
type ObservationContext,
|
|
18
|
+
type ObservationParent,
|
|
19
|
+
} from "@nifrajs/otel"
|
|
20
|
+
|
|
21
|
+
/** Structural twin of `@nifrajs/agent`'s `AgentStepEvidence`. */
|
|
22
|
+
export interface AgentRunEvidence {
|
|
23
|
+
readonly seq: number
|
|
24
|
+
readonly at: number
|
|
25
|
+
readonly kind: "model" | "tool" | "approval" | "budget" | "state"
|
|
26
|
+
readonly outcome: "started" | "passed" | "failed" | "denied" | "suspended" | "committed"
|
|
27
|
+
readonly name?: string
|
|
28
|
+
readonly effectId?: string
|
|
29
|
+
readonly code?: string
|
|
30
|
+
readonly ledgerHead?: string
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/** Structural slice of the runner's `AgentRunResult` that `end()` reads. */
|
|
34
|
+
export interface AgentRunOutcome {
|
|
35
|
+
readonly status: "completed" | "suspended" | "continue"
|
|
36
|
+
readonly error?: { readonly code: string }
|
|
37
|
+
readonly reason?: string
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export interface TraceAgentRunOptions {
|
|
41
|
+
/** Agent name, recorded as `nifra.agent.name`. */
|
|
42
|
+
readonly agent: string
|
|
43
|
+
/** Turn id, recorded as `nifra.agent.turn_id`. */
|
|
44
|
+
readonly turnId: string
|
|
45
|
+
/** Exporter that receives the run span and its evidence child spans. */
|
|
46
|
+
readonly exporter?: ObservationAdapter
|
|
47
|
+
readonly adapters?: readonly ObservationAdapter[]
|
|
48
|
+
/**
|
|
49
|
+
* Parent span for the run - pass the enclosing request observation's `context` to nest agent
|
|
50
|
+
* runs under the HTTP span. `null` forces a root span; `undefined` falls back to `traceparent`.
|
|
51
|
+
*/
|
|
52
|
+
readonly parent?: ObservationParent | null
|
|
53
|
+
/** Inbound W3C header used when `parent` is undefined. */
|
|
54
|
+
readonly traceparent?: string | null
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export interface AgentRunTrace {
|
|
58
|
+
/** Pass as (or combine into) `ports.telemetry`. Never throws; a failing adapter cannot fail the turn. */
|
|
59
|
+
readonly telemetry: { step(evidence: AgentRunEvidence): void }
|
|
60
|
+
/** The run span's trace identity, forwardable with `traceHeaders`. */
|
|
61
|
+
readonly context: ObservationContext
|
|
62
|
+
/**
|
|
63
|
+
* End the run span. Pass the runner's result to record the final status
|
|
64
|
+
* (`nifra.agent.status`, pending reason, error code); omit it when the run threw.
|
|
65
|
+
* Exactly-once: repeated calls are ignored.
|
|
66
|
+
*/
|
|
67
|
+
end(result?: AgentRunOutcome): void
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function evidenceAttributes(evidence: AgentRunEvidence): Record<string, AttributeValue> {
|
|
71
|
+
const attributes: Record<string, AttributeValue> = {
|
|
72
|
+
"nifra.agent.evidence.seq": evidence.seq,
|
|
73
|
+
"nifra.agent.evidence.outcome": evidence.outcome,
|
|
74
|
+
}
|
|
75
|
+
if (evidence.name !== undefined) attributes["tool.name"] = evidence.name
|
|
76
|
+
if (evidence.effectId !== undefined) attributes["nifra.effect.id"] = evidence.effectId
|
|
77
|
+
if (evidence.code !== undefined) attributes["nifra.agent.error_code"] = evidence.code
|
|
78
|
+
if (evidence.ledgerHead !== undefined) attributes["nifra.ledger.head"] = evidence.ledgerHead
|
|
79
|
+
return attributes
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Open a run span and adapt the runner's step evidence into child spans.
|
|
84
|
+
*
|
|
85
|
+
* const trace = traceAgentRun({ agent: agent.name, turnId, exporter, parent: c.observation?.context })
|
|
86
|
+
* const result = await runAgent(agent, input, { ...ports, telemetry: trace.telemetry }, options)
|
|
87
|
+
* trace.end(result)
|
|
88
|
+
*
|
|
89
|
+
* Model evidence arrives as a started/terminal pair and becomes one timed `nifra.agent.model`
|
|
90
|
+
* span; every other evidence item is terminal-only and becomes an instant child span.
|
|
91
|
+
*/
|
|
92
|
+
export function traceAgentRun(options: TraceAgentRunOptions): AgentRunTrace {
|
|
93
|
+
const adapters = [
|
|
94
|
+
...(options.exporter === undefined ? [] : [options.exporter]),
|
|
95
|
+
...(options.adapters ?? []),
|
|
96
|
+
]
|
|
97
|
+
const lifecycle = createObservationLifecycle({ adapters })
|
|
98
|
+
const run = lifecycle.start({
|
|
99
|
+
name: "nifra.agent.run",
|
|
100
|
+
...(options.parent === undefined ? {} : { parent: options.parent }),
|
|
101
|
+
...(options.traceparent === undefined ? {} : { traceparent: options.traceparent }),
|
|
102
|
+
attributes: {
|
|
103
|
+
"nifra.agent.name": options.agent,
|
|
104
|
+
"nifra.agent.turn_id": options.turnId,
|
|
105
|
+
},
|
|
106
|
+
})
|
|
107
|
+
// The runner is sequential - at most one model call is in flight, so a single slot pairs the
|
|
108
|
+
// `started` item with its terminal twin. Kinds other than "model" emit terminal items only.
|
|
109
|
+
let openModel: ActiveObservation | undefined
|
|
110
|
+
|
|
111
|
+
const step = (evidence: AgentRunEvidence): void => {
|
|
112
|
+
try {
|
|
113
|
+
if (evidence.kind === "model" && evidence.outcome === "started") {
|
|
114
|
+
openModel?.end({ status: "error", attributes: { "nifra.agent.error_code": "unpaired" } })
|
|
115
|
+
openModel = run.startChild({
|
|
116
|
+
name: "nifra.agent.model",
|
|
117
|
+
attributes: evidenceAttributes(evidence),
|
|
118
|
+
})
|
|
119
|
+
return
|
|
120
|
+
}
|
|
121
|
+
const failed = evidence.outcome === "failed" || evidence.outcome === "denied"
|
|
122
|
+
const status = failed ? "error" : "ok"
|
|
123
|
+
if (evidence.kind === "model" && openModel !== undefined) {
|
|
124
|
+
const span = openModel
|
|
125
|
+
openModel = undefined
|
|
126
|
+
span.setAttributes(evidenceAttributes(evidence))
|
|
127
|
+
span.end({ status })
|
|
128
|
+
return
|
|
129
|
+
}
|
|
130
|
+
run
|
|
131
|
+
.startChild({
|
|
132
|
+
name: `nifra.agent.${evidence.kind}`,
|
|
133
|
+
attributes: evidenceAttributes(evidence),
|
|
134
|
+
})
|
|
135
|
+
.end({ status })
|
|
136
|
+
} catch {
|
|
137
|
+
// Telemetry must never fail the turn it observes.
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
const end = (result?: AgentRunOutcome): void => {
|
|
142
|
+
try {
|
|
143
|
+
openModel?.end({ status: "error", attributes: { "nifra.agent.error_code": "run_ended" } })
|
|
144
|
+
openModel = undefined
|
|
145
|
+
if (result === undefined) {
|
|
146
|
+
run.end({ status: "error", attributes: { "nifra.agent.status": "threw" } })
|
|
147
|
+
return
|
|
148
|
+
}
|
|
149
|
+
const attributes: Record<string, AttributeValue> = { "nifra.agent.status": result.status }
|
|
150
|
+
if (result.reason !== undefined) attributes["nifra.agent.pending_reason"] = result.reason
|
|
151
|
+
if (result.error !== undefined) attributes["nifra.agent.error_code"] = result.error.code
|
|
152
|
+
run.end({ status: result.error === undefined ? "ok" : "error", attributes })
|
|
153
|
+
} catch {
|
|
154
|
+
// Same fail-open rule as step().
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
return Object.freeze({ telemetry: Object.freeze({ step }), context: run.context, end })
|
|
159
|
+
}
|