@agentproto/telemetry 0.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/LICENSE +21 -0
- package/README.md +71 -0
- package/dist/index.d.ts +190 -0
- package/dist/index.mjs +219 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +63 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Jeremy (agentik.net)
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# @agentproto/telemetry
|
|
2
|
+
|
|
3
|
+
Vendor-neutral **Telemetry** port for the agentproto runtime kernel, plus
|
|
4
|
+
reference sinks and an OpenTelemetry adapter.
|
|
5
|
+
|
|
6
|
+
A runtime emits a stream of structured `TelemetryEvent`s during each cycle —
|
|
7
|
+
every event carries a `cycleId` and an ISO `at` timestamp, so a sink can rebuild
|
|
8
|
+
OTEL-style spans by grouping on `cycleId`. Events are schema
|
|
9
|
+
`agentproto/telemetry/v1`; **sinks must tolerate unknown kinds** (future kernel
|
|
10
|
+
versions may add event types under the same schema).
|
|
11
|
+
|
|
12
|
+
This package is a **leaf**: it owns the port and depends on no runtime. Runtimes
|
|
13
|
+
depend on it.
|
|
14
|
+
|
|
15
|
+
## The port
|
|
16
|
+
|
|
17
|
+
```ts
|
|
18
|
+
import type { Telemetry, TelemetryEvent } from "@agentproto/telemetry"
|
|
19
|
+
|
|
20
|
+
interface Telemetry {
|
|
21
|
+
emit(event: TelemetryEvent): void
|
|
22
|
+
}
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Reference sinks
|
|
26
|
+
|
|
27
|
+
- `noopTelemetry` — drops everything. Same as leaving telemetry unset.
|
|
28
|
+
- `stderrTelemetry(opts?)` — one human-readable line per event, with
|
|
29
|
+
`include` / `exclude` kind filters and a `prefix`.
|
|
30
|
+
- `arrayTelemetry()` — pushes every event onto `.events`; for tests.
|
|
31
|
+
- `composeTelemetry(...sinks)` — fan-out to many sinks, each isolated (one
|
|
32
|
+
throwing does not block the others).
|
|
33
|
+
|
|
34
|
+
```ts
|
|
35
|
+
import { composeTelemetry, stderrTelemetry } from "@agentproto/telemetry"
|
|
36
|
+
|
|
37
|
+
const telemetry = composeTelemetry(stderrTelemetry({ prefix: "run: " }), otel)
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## OpenTelemetry adapter
|
|
41
|
+
|
|
42
|
+
`otelTelemetry(tracer)` maps the event stream onto OTel spans. It is a **pure
|
|
43
|
+
mapper** — the host supplies the `Tracer` (peer dependency
|
|
44
|
+
`@opentelemetry/api`). No global provider, no exporter wiring.
|
|
45
|
+
|
|
46
|
+
```ts
|
|
47
|
+
import { trace } from "@opentelemetry/api"
|
|
48
|
+
import { otelTelemetry } from "@agentproto/telemetry"
|
|
49
|
+
|
|
50
|
+
const tracer = trace.getTracer("my-runtime")
|
|
51
|
+
const telemetry = otelTelemetry(tracer)
|
|
52
|
+
|
|
53
|
+
// hand `telemetry` to the runtime; each cycle becomes an `agentproto.cycle`
|
|
54
|
+
// span with `agentproto.participant` child spans.
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Mapping:
|
|
58
|
+
|
|
59
|
+
| Event | OTel effect |
|
|
60
|
+
| --- | --- |
|
|
61
|
+
| `cycle.started` | start root span `agentproto.cycle`, held per `cycleId` |
|
|
62
|
+
| `participant.started` | child span `agentproto.participant` (participantId, executorKind) |
|
|
63
|
+
| `participant.finished` | end child span (durationMs, contentLength attrs) |
|
|
64
|
+
| `participant.failed` | `recordException` + `ERROR` status, then end |
|
|
65
|
+
| `substrate.read` / `dispatch.decided` / … | `addEvent` on the cycle span |
|
|
66
|
+
| `cycle.finished` / `cycle.idle` | outcome attrs, end the cycle span |
|
|
67
|
+
| unknown kind | ignored silently (forward-compat) |
|
|
68
|
+
|
|
69
|
+
## License
|
|
70
|
+
|
|
71
|
+
MIT
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
import { Tracer } from '@opentelemetry/api';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Vendor-neutral Telemetry port.
|
|
5
|
+
*
|
|
6
|
+
* This package OWNS the telemetry contract so any runtime can depend on a
|
|
7
|
+
* single interface without pulling in a kernel. It is a LEAF — it imports
|
|
8
|
+
* nothing from a runtime; runtimes depend on IT.
|
|
9
|
+
*
|
|
10
|
+
* The two referenced id types are mirrored locally (plain `string`) so the
|
|
11
|
+
* port has no upstream dependency. A runtime may use branded ids; they widen
|
|
12
|
+
* to `string` and remain assignable here.
|
|
13
|
+
*/
|
|
14
|
+
type ParticipantId = string;
|
|
15
|
+
type TurnId = string;
|
|
16
|
+
/**
|
|
17
|
+
* Discriminated union of every event a kernel emits during a cycle.
|
|
18
|
+
*
|
|
19
|
+
* Every event carries `cycleId` (a per-cycle ULID-ish identifier) and
|
|
20
|
+
* `at` (ISO timestamp), so a sink can rebuild OTEL-style spans by
|
|
21
|
+
* grouping on cycleId.
|
|
22
|
+
*
|
|
23
|
+
* Sinks MUST tolerate unknown kinds — future kernel versions may add
|
|
24
|
+
* event types under the same `agentproto/telemetry/v1` schema.
|
|
25
|
+
*/
|
|
26
|
+
type TelemetryEvent = {
|
|
27
|
+
readonly kind: "cycle.started";
|
|
28
|
+
readonly cycleId: string;
|
|
29
|
+
readonly at: string;
|
|
30
|
+
readonly since?: TurnId;
|
|
31
|
+
} | {
|
|
32
|
+
readonly kind: "substrate.read";
|
|
33
|
+
readonly cycleId: string;
|
|
34
|
+
readonly at: string;
|
|
35
|
+
readonly substrateKind: string;
|
|
36
|
+
readonly turnCount: number;
|
|
37
|
+
readonly durationMs: number;
|
|
38
|
+
} | {
|
|
39
|
+
readonly kind: "dispatch.decided";
|
|
40
|
+
readonly cycleId: string;
|
|
41
|
+
readonly at: string;
|
|
42
|
+
readonly dispatcherKind: string;
|
|
43
|
+
readonly selected: readonly ParticipantId[];
|
|
44
|
+
readonly durationMs: number;
|
|
45
|
+
} | {
|
|
46
|
+
readonly kind: "participant.started";
|
|
47
|
+
readonly cycleId: string;
|
|
48
|
+
readonly at: string;
|
|
49
|
+
readonly participantId: ParticipantId;
|
|
50
|
+
readonly executorKind: string;
|
|
51
|
+
} | {
|
|
52
|
+
readonly kind: "participant.finished";
|
|
53
|
+
readonly cycleId: string;
|
|
54
|
+
readonly at: string;
|
|
55
|
+
readonly participantId: ParticipantId;
|
|
56
|
+
readonly executorKind: string;
|
|
57
|
+
readonly durationMs: number;
|
|
58
|
+
readonly contentLength: number;
|
|
59
|
+
} | {
|
|
60
|
+
readonly kind: "participant.failed";
|
|
61
|
+
readonly cycleId: string;
|
|
62
|
+
readonly at: string;
|
|
63
|
+
readonly participantId: ParticipantId;
|
|
64
|
+
readonly executorKind: string;
|
|
65
|
+
readonly error: string;
|
|
66
|
+
} | {
|
|
67
|
+
readonly kind: "substrate.appended";
|
|
68
|
+
readonly cycleId: string;
|
|
69
|
+
readonly at: string;
|
|
70
|
+
readonly turnId: TurnId;
|
|
71
|
+
readonly participantId: ParticipantId;
|
|
72
|
+
} | {
|
|
73
|
+
readonly kind: "state.written";
|
|
74
|
+
readonly cycleId: string;
|
|
75
|
+
readonly at: string;
|
|
76
|
+
readonly participantId: ParticipantId;
|
|
77
|
+
} | {
|
|
78
|
+
readonly kind: "cycle.idle";
|
|
79
|
+
readonly cycleId: string;
|
|
80
|
+
readonly at: string;
|
|
81
|
+
} | {
|
|
82
|
+
readonly kind: "cycle.finished";
|
|
83
|
+
readonly cycleId: string;
|
|
84
|
+
readonly at: string;
|
|
85
|
+
readonly outcome: "executed" | "idle";
|
|
86
|
+
readonly turnsAppended: number;
|
|
87
|
+
readonly durationMs: number;
|
|
88
|
+
};
|
|
89
|
+
/**
|
|
90
|
+
* Structured-event sink. Wire to log lines, an OTEL exporter, an in-memory
|
|
91
|
+
* array (tests), or anything else. Implementations MUST tolerate unknown
|
|
92
|
+
* event kinds (forward-compat under `agentproto/telemetry/v1`).
|
|
93
|
+
*
|
|
94
|
+
* The port is generic over the event type `E` so a downstream package can
|
|
95
|
+
* carry its OWN discriminated union through the same sink contract (e.g.
|
|
96
|
+
* `Telemetry<EvalEvent>`). The default `E = TelemetryEvent` keeps every
|
|
97
|
+
* existing usage — `Telemetry` written with no type argument — unchanged.
|
|
98
|
+
*/
|
|
99
|
+
interface Telemetry<E = TelemetryEvent> {
|
|
100
|
+
emit(event: E): void;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Reference Telemetry adapters.
|
|
105
|
+
*
|
|
106
|
+
* Wire `RuntimePorts.telemetry` to one of these (or your own
|
|
107
|
+
* implementation) to receive structured events from each cycle.
|
|
108
|
+
* Sinks may also be composed via `composeTelemetry()`.
|
|
109
|
+
*/
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* No-op sink. Equivalent to leaving `telemetry` undefined on RuntimePorts.
|
|
113
|
+
*
|
|
114
|
+
* A factory generic over the event type `E` (default `TelemetryEvent`) so it
|
|
115
|
+
* satisfies `Telemetry<E>` for any downstream union. `noopTelemetry()` with no
|
|
116
|
+
* type argument returns the ordinary `Telemetry<TelemetryEvent>` sink.
|
|
117
|
+
*/
|
|
118
|
+
declare function noopTelemetry<E = TelemetryEvent>(): Telemetry<E>;
|
|
119
|
+
interface StderrTelemetryOptions<E = TelemetryEvent> {
|
|
120
|
+
/** Event kinds to include. Omit to emit all kinds. */
|
|
121
|
+
readonly include?: ReadonlySet<TelemetryEvent["kind"]>;
|
|
122
|
+
/** Event kinds to suppress. Wins over `include`. */
|
|
123
|
+
readonly exclude?: ReadonlySet<TelemetryEvent["kind"]>;
|
|
124
|
+
/** Prefix prepended to every line. Default "agentproto: ". */
|
|
125
|
+
readonly prefix?: string;
|
|
126
|
+
/**
|
|
127
|
+
* Render a line for an event. Required when `E` is not `TelemetryEvent`
|
|
128
|
+
* (the built-in formatter only understands the concrete telemetry union).
|
|
129
|
+
* When omitted, the built-in `TelemetryEvent` formatter is used — valid only
|
|
130
|
+
* for the default `E = TelemetryEvent`.
|
|
131
|
+
*/
|
|
132
|
+
readonly format?: (event: E) => string;
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Human-readable stderr sink — one line per event. Cheaper than wiring
|
|
136
|
+
* a real exporter; good default for `--verbose` mode.
|
|
137
|
+
*
|
|
138
|
+
* Generic over the event type `E`. For a custom `E`, pass `format` — the
|
|
139
|
+
* built-in formatter is `TelemetryEvent`-specific. The `include`/`exclude`
|
|
140
|
+
* filters key on `event.kind`, which every telemetry-shaped event carries.
|
|
141
|
+
*/
|
|
142
|
+
declare function stderrTelemetry<E = TelemetryEvent>(options?: StderrTelemetryOptions<E>): Telemetry<E>;
|
|
143
|
+
/**
|
|
144
|
+
* In-memory sink — every event pushed onto an array. Intended for
|
|
145
|
+
* tests; reach into `events` after `runTurn` to assert. Generic over the
|
|
146
|
+
* event type `E` so a custom union's events collect with full typing.
|
|
147
|
+
*/
|
|
148
|
+
declare function arrayTelemetry<E = TelemetryEvent>(): Telemetry<E> & {
|
|
149
|
+
readonly events: readonly E[];
|
|
150
|
+
};
|
|
151
|
+
/**
|
|
152
|
+
* Fan-out — emits every event to all wrapped sinks in order. Useful
|
|
153
|
+
* for "log to stderr AND export to OTEL" setups. Generic over the event
|
|
154
|
+
* type `E`; every wrapped sink shares the same `E`.
|
|
155
|
+
*/
|
|
156
|
+
declare function composeTelemetry<E = TelemetryEvent>(...sinks: readonly Telemetry<E>[]): Telemetry<E>;
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* OpenTelemetry adapter for the vendor-neutral Telemetry port.
|
|
160
|
+
*
|
|
161
|
+
* `otelTelemetry(tracer)` returns a `Telemetry` sink that maps the kernel's
|
|
162
|
+
* `TelemetryEvent` stream onto OTel spans, grouping on `cycleId`:
|
|
163
|
+
*
|
|
164
|
+
* cycle.started → root span `agentproto.cycle` (held per cycleId)
|
|
165
|
+
* participant.started → child span `agentproto.participant`
|
|
166
|
+
* participant.finished → end child span (durationMs, contentLength attrs)
|
|
167
|
+
* participant.failed → recordException + ERROR status, then end
|
|
168
|
+
* substrate.read → addEvent on the cycle span
|
|
169
|
+
* dispatch.decided → addEvent on the cycle span
|
|
170
|
+
* cycle.finished/idle → outcome attrs, end the cycle span, drop from map
|
|
171
|
+
* unknown kind → ignored silently (forward-compat contract)
|
|
172
|
+
*
|
|
173
|
+
* This is a PURE MAPPER: the host supplies the `Tracer`. No global provider,
|
|
174
|
+
* no exporter, no context propagation beyond parenting child spans under the
|
|
175
|
+
* cycle span.
|
|
176
|
+
*/
|
|
177
|
+
|
|
178
|
+
interface OtelTelemetryOptions {
|
|
179
|
+
/** Span name for the per-cycle root span. Default "agentproto.cycle". */
|
|
180
|
+
readonly cycleSpanName?: string;
|
|
181
|
+
/** Span name for each participant child span. Default "agentproto.participant". */
|
|
182
|
+
readonly participantSpanName?: string;
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* Build a `Telemetry` sink that projects events onto OTel spans via the
|
|
186
|
+
* host-supplied `tracer`.
|
|
187
|
+
*/
|
|
188
|
+
declare function otelTelemetry(tracer: Tracer, options?: OtelTelemetryOptions): Telemetry;
|
|
189
|
+
|
|
190
|
+
export { type OtelTelemetryOptions, type ParticipantId, type StderrTelemetryOptions, type Telemetry, type TelemetryEvent, type TurnId, arrayTelemetry, composeTelemetry, noopTelemetry, otelTelemetry, stderrTelemetry };
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
import { SpanStatusCode, trace, context } from '@opentelemetry/api';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @agentproto/telemetry v0.1.0
|
|
5
|
+
* Vendor-neutral Telemetry port + reference sinks + OpenTelemetry adapter.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
// src/adapters.ts
|
|
9
|
+
function noopTelemetry() {
|
|
10
|
+
return {
|
|
11
|
+
emit() {
|
|
12
|
+
}
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
function stderrTelemetry(options = {}) {
|
|
16
|
+
const prefix = options.prefix ?? "agentproto: ";
|
|
17
|
+
const format = options.format ?? defaultFormat;
|
|
18
|
+
return {
|
|
19
|
+
emit(event) {
|
|
20
|
+
const kind = kindOf(event);
|
|
21
|
+
if (kind !== void 0 && setHas(options.exclude, kind)) return;
|
|
22
|
+
if (kind !== void 0 && options.include && !setHas(options.include, kind))
|
|
23
|
+
return;
|
|
24
|
+
process.stderr.write(`${prefix}${format(event)}
|
|
25
|
+
`);
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
function setHas(set, value) {
|
|
30
|
+
return set !== void 0 && set.has(value);
|
|
31
|
+
}
|
|
32
|
+
function defaultFormat(event) {
|
|
33
|
+
return isTelemetryEvent(event) ? formatEvent(event) : JSON.stringify(event);
|
|
34
|
+
}
|
|
35
|
+
function kindOf(event) {
|
|
36
|
+
if (typeof event !== "object" || event === null) return void 0;
|
|
37
|
+
if (!("kind" in event)) return void 0;
|
|
38
|
+
const kind = event.kind;
|
|
39
|
+
return typeof kind === "string" ? kind : void 0;
|
|
40
|
+
}
|
|
41
|
+
function isTelemetryEvent(event) {
|
|
42
|
+
return kindOf(event) !== void 0;
|
|
43
|
+
}
|
|
44
|
+
function arrayTelemetry() {
|
|
45
|
+
const events = [];
|
|
46
|
+
return {
|
|
47
|
+
events,
|
|
48
|
+
emit(event) {
|
|
49
|
+
events.push(event);
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
function composeTelemetry(...sinks) {
|
|
54
|
+
return {
|
|
55
|
+
emit(event) {
|
|
56
|
+
for (const sink of sinks) {
|
|
57
|
+
try {
|
|
58
|
+
sink.emit(event);
|
|
59
|
+
} catch {
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
function formatEvent(event) {
|
|
66
|
+
switch (event.kind) {
|
|
67
|
+
case "cycle.started":
|
|
68
|
+
return `${event.cycleId} cycle.started${event.since !== void 0 ? ` since=${event.since}` : ""}`;
|
|
69
|
+
case "substrate.read":
|
|
70
|
+
return `${event.cycleId} substrate.read ${event.substrateKind} turns=${event.turnCount} ${event.durationMs}ms`;
|
|
71
|
+
case "dispatch.decided":
|
|
72
|
+
return `${event.cycleId} dispatch.decided ${event.dispatcherKind} selected=[${event.selected.join(",") || "(none)"}] ${event.durationMs}ms`;
|
|
73
|
+
case "participant.started":
|
|
74
|
+
return `${event.cycleId} participant.started ${event.participantId} (${event.executorKind})`;
|
|
75
|
+
case "participant.finished":
|
|
76
|
+
return `${event.cycleId} participant.finished ${event.participantId} ${event.durationMs}ms content=${event.contentLength}b`;
|
|
77
|
+
case "participant.failed":
|
|
78
|
+
return `${event.cycleId} participant.failed ${event.participantId} \u2014 ${event.error}`;
|
|
79
|
+
case "substrate.appended":
|
|
80
|
+
return `${event.cycleId} substrate.appended turn=${event.turnId} by=${event.participantId}`;
|
|
81
|
+
case "state.written":
|
|
82
|
+
return `${event.cycleId} state.written ${event.participantId}`;
|
|
83
|
+
case "cycle.idle":
|
|
84
|
+
return `${event.cycleId} cycle.idle`;
|
|
85
|
+
case "cycle.finished":
|
|
86
|
+
return `${event.cycleId} cycle.finished ${event.outcome} appended=${event.turnsAppended} ${event.durationMs}ms`;
|
|
87
|
+
default: {
|
|
88
|
+
const _exhaustive = event;
|
|
89
|
+
return JSON.stringify(_exhaustive);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
function otelTelemetry(tracer, options = {}) {
|
|
94
|
+
const cycleSpanName = options.cycleSpanName ?? "agentproto.cycle";
|
|
95
|
+
const participantSpanName = options.participantSpanName ?? "agentproto.participant";
|
|
96
|
+
const cycleSpans = /* @__PURE__ */ new Map();
|
|
97
|
+
const participantSpans = /* @__PURE__ */ new Map();
|
|
98
|
+
const startChild = (parent, name) => {
|
|
99
|
+
const parentCtx = trace.setSpan(context.active(), parent);
|
|
100
|
+
return tracer.startSpan(name, void 0, parentCtx);
|
|
101
|
+
};
|
|
102
|
+
const dropParticipant = (cycleId, participantId) => {
|
|
103
|
+
const byParticipant = participantSpans.get(cycleId);
|
|
104
|
+
const span = byParticipant?.get(participantId);
|
|
105
|
+
if (byParticipant && span) {
|
|
106
|
+
byParticipant.delete(participantId);
|
|
107
|
+
if (byParticipant.size === 0) participantSpans.delete(cycleId);
|
|
108
|
+
}
|
|
109
|
+
return span;
|
|
110
|
+
};
|
|
111
|
+
const endCycle = (event) => {
|
|
112
|
+
const span = cycleSpans.get(event.cycleId);
|
|
113
|
+
if (!span) return;
|
|
114
|
+
if (event.kind === "cycle.finished") {
|
|
115
|
+
span.setAttribute("agentproto.cycle.outcome", event.outcome);
|
|
116
|
+
span.setAttribute("agentproto.cycle.turnsAppended", event.turnsAppended);
|
|
117
|
+
span.setAttribute("agentproto.cycle.durationMs", event.durationMs);
|
|
118
|
+
} else {
|
|
119
|
+
span.setAttribute("agentproto.cycle.outcome", "idle");
|
|
120
|
+
}
|
|
121
|
+
const orphans = participantSpans.get(event.cycleId);
|
|
122
|
+
if (orphans) {
|
|
123
|
+
for (const child of orphans.values()) child.end();
|
|
124
|
+
participantSpans.delete(event.cycleId);
|
|
125
|
+
}
|
|
126
|
+
span.end();
|
|
127
|
+
cycleSpans.delete(event.cycleId);
|
|
128
|
+
};
|
|
129
|
+
return {
|
|
130
|
+
emit(event) {
|
|
131
|
+
switch (event.kind) {
|
|
132
|
+
case "cycle.started": {
|
|
133
|
+
const span = tracer.startSpan(cycleSpanName);
|
|
134
|
+
span.setAttribute("agentproto.cycleId", event.cycleId);
|
|
135
|
+
if (event.since !== void 0) {
|
|
136
|
+
span.setAttribute("agentproto.cycle.since", event.since);
|
|
137
|
+
}
|
|
138
|
+
cycleSpans.set(event.cycleId, span);
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
case "participant.started": {
|
|
142
|
+
const cycleSpan = cycleSpans.get(event.cycleId);
|
|
143
|
+
if (!cycleSpan) return;
|
|
144
|
+
const span = startChild(cycleSpan, participantSpanName);
|
|
145
|
+
span.setAttribute("agentproto.participantId", event.participantId);
|
|
146
|
+
span.setAttribute("agentproto.executorKind", event.executorKind);
|
|
147
|
+
let byParticipant = participantSpans.get(event.cycleId);
|
|
148
|
+
if (!byParticipant) {
|
|
149
|
+
byParticipant = /* @__PURE__ */ new Map();
|
|
150
|
+
participantSpans.set(event.cycleId, byParticipant);
|
|
151
|
+
}
|
|
152
|
+
byParticipant.set(event.participantId, span);
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
case "participant.finished": {
|
|
156
|
+
const span = dropParticipant(event.cycleId, event.participantId);
|
|
157
|
+
if (!span) return;
|
|
158
|
+
span.setAttribute("agentproto.participant.durationMs", event.durationMs);
|
|
159
|
+
span.setAttribute("agentproto.participant.contentLength", event.contentLength);
|
|
160
|
+
span.end();
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
case "participant.failed": {
|
|
164
|
+
const span = dropParticipant(event.cycleId, event.participantId);
|
|
165
|
+
if (!span) return;
|
|
166
|
+
span.recordException({ name: "ParticipantExecuteError", message: event.error });
|
|
167
|
+
span.setStatus({ code: SpanStatusCode.ERROR, message: event.error });
|
|
168
|
+
span.end();
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
case "substrate.read": {
|
|
172
|
+
const span = cycleSpans.get(event.cycleId);
|
|
173
|
+
span?.addEvent("substrate.read", {
|
|
174
|
+
"agentproto.substrateKind": event.substrateKind,
|
|
175
|
+
"agentproto.turnCount": event.turnCount,
|
|
176
|
+
"agentproto.durationMs": event.durationMs
|
|
177
|
+
});
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
180
|
+
case "dispatch.decided": {
|
|
181
|
+
const span = cycleSpans.get(event.cycleId);
|
|
182
|
+
span?.addEvent("dispatch.decided", {
|
|
183
|
+
"agentproto.dispatcherKind": event.dispatcherKind,
|
|
184
|
+
"agentproto.selected": event.selected.join(","),
|
|
185
|
+
"agentproto.durationMs": event.durationMs
|
|
186
|
+
});
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
case "substrate.appended": {
|
|
190
|
+
const span = cycleSpans.get(event.cycleId);
|
|
191
|
+
span?.addEvent("substrate.appended", {
|
|
192
|
+
"agentproto.turnId": event.turnId,
|
|
193
|
+
"agentproto.participantId": event.participantId
|
|
194
|
+
});
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
197
|
+
case "state.written": {
|
|
198
|
+
const span = cycleSpans.get(event.cycleId);
|
|
199
|
+
span?.addEvent("state.written", {
|
|
200
|
+
"agentproto.participantId": event.participantId
|
|
201
|
+
});
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
204
|
+
case "cycle.finished":
|
|
205
|
+
case "cycle.idle": {
|
|
206
|
+
endCycle(event);
|
|
207
|
+
return;
|
|
208
|
+
}
|
|
209
|
+
default: {
|
|
210
|
+
return;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
export { arrayTelemetry, composeTelemetry, noopTelemetry, otelTelemetry, stderrTelemetry };
|
|
218
|
+
//# sourceMappingURL=index.mjs.map
|
|
219
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/adapters.ts","../src/otel.ts"],"names":[],"mappings":"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","file":"index.mjs","sourcesContent":["/**\n * Reference Telemetry adapters.\n *\n * Wire `RuntimePorts.telemetry` to one of these (or your own\n * implementation) to receive structured events from each cycle.\n * Sinks may also be composed via `composeTelemetry()`.\n */\n\nimport type { Telemetry, TelemetryEvent } from \"./ports.js\"\n\n/**\n * No-op sink. Equivalent to leaving `telemetry` undefined on RuntimePorts.\n *\n * A factory generic over the event type `E` (default `TelemetryEvent`) so it\n * satisfies `Telemetry<E>` for any downstream union. `noopTelemetry()` with no\n * type argument returns the ordinary `Telemetry<TelemetryEvent>` sink.\n */\nexport function noopTelemetry<E = TelemetryEvent>(): Telemetry<E> {\n return {\n emit() {\n // intentionally blank\n },\n }\n}\n\nexport interface StderrTelemetryOptions<E = TelemetryEvent> {\n /** Event kinds to include. Omit to emit all kinds. */\n readonly include?: ReadonlySet<TelemetryEvent[\"kind\"]>\n /** Event kinds to suppress. Wins over `include`. */\n readonly exclude?: ReadonlySet<TelemetryEvent[\"kind\"]>\n /** Prefix prepended to every line. Default \"agentproto: \". */\n readonly prefix?: string\n /**\n * Render a line for an event. Required when `E` is not `TelemetryEvent`\n * (the built-in formatter only understands the concrete telemetry union).\n * When omitted, the built-in `TelemetryEvent` formatter is used — valid only\n * for the default `E = TelemetryEvent`.\n */\n readonly format?: (event: E) => string\n}\n\n/**\n * Human-readable stderr sink — one line per event. Cheaper than wiring\n * a real exporter; good default for `--verbose` mode.\n *\n * Generic over the event type `E`. For a custom `E`, pass `format` — the\n * built-in formatter is `TelemetryEvent`-specific. The `include`/`exclude`\n * filters key on `event.kind`, which every telemetry-shaped event carries.\n */\nexport function stderrTelemetry<E = TelemetryEvent>(\n options: StderrTelemetryOptions<E> = {}\n): Telemetry<E> {\n const prefix = options.prefix ?? \"agentproto: \"\n const format = options.format ?? defaultFormat\n return {\n emit(event) {\n const kind = kindOf(event)\n if (kind !== undefined && setHas(options.exclude, kind)) return\n if (kind !== undefined && options.include && !setHas(options.include, kind))\n return\n process.stderr.write(`${prefix}${format(event)}\\n`)\n },\n }\n}\n\n/**\n * Membership test that accepts a plain `string` probe against a set of literal\n * kinds — avoids an `as` cast on the discriminant while keeping the public\n * option type (`ReadonlySet<TelemetryEvent[\"kind\"]>`) narrow for callers.\n */\nfunction setHas(\n set: ReadonlySet<string> | undefined,\n value: string,\n): boolean {\n return set !== undefined && set.has(value)\n}\n\n/**\n * The built-in stderr formatter, usable only when `E = TelemetryEvent`.\n * Wrapped so it satisfies the generic `(event: E) => string` default without a\n * cast: it narrows through {@link isTelemetryEvent} and JSON-renders anything\n * else (forward-compat, same contract as {@link formatEvent}'s default arm).\n */\nfunction defaultFormat<E>(event: E): string {\n return isTelemetryEvent(event) ? formatEvent(event) : JSON.stringify(event)\n}\n\n/** Read a `kind` discriminant off an event, if it carries one. */\nfunction kindOf(event: unknown): string | undefined {\n if (typeof event !== \"object\" || event === null) return undefined\n if (!(\"kind\" in event)) return undefined\n const kind: unknown = event.kind\n return typeof kind === \"string\" ? kind : undefined\n}\n\n/** Structural guard: is this a concrete {@link TelemetryEvent}? */\nfunction isTelemetryEvent(event: unknown): event is TelemetryEvent {\n return kindOf(event) !== undefined\n}\n\n/**\n * In-memory sink — every event pushed onto an array. Intended for\n * tests; reach into `events` after `runTurn` to assert. Generic over the\n * event type `E` so a custom union's events collect with full typing.\n */\nexport function arrayTelemetry<E = TelemetryEvent>(): Telemetry<E> & {\n readonly events: readonly E[]\n} {\n const events: E[] = []\n return {\n events,\n emit(event) {\n events.push(event)\n },\n }\n}\n\n/**\n * Fan-out — emits every event to all wrapped sinks in order. Useful\n * for \"log to stderr AND export to OTEL\" setups. Generic over the event\n * type `E`; every wrapped sink shares the same `E`.\n */\nexport function composeTelemetry<E = TelemetryEvent>(\n ...sinks: readonly Telemetry<E>[]\n): Telemetry<E> {\n return {\n emit(event) {\n for (const sink of sinks) {\n try {\n sink.emit(event)\n } catch {\n // Each sink is independently isolated.\n }\n }\n },\n }\n}\n\nfunction formatEvent(event: TelemetryEvent): string {\n switch (event.kind) {\n case \"cycle.started\":\n return `${event.cycleId} cycle.started${event.since !== undefined ? ` since=${event.since}` : \"\"}`\n case \"substrate.read\":\n return `${event.cycleId} substrate.read ${event.substrateKind} turns=${event.turnCount} ${event.durationMs}ms`\n case \"dispatch.decided\":\n return `${event.cycleId} dispatch.decided ${event.dispatcherKind} selected=[${event.selected.join(\",\") || \"(none)\"}] ${event.durationMs}ms`\n case \"participant.started\":\n return `${event.cycleId} participant.started ${event.participantId} (${event.executorKind})`\n case \"participant.finished\":\n return `${event.cycleId} participant.finished ${event.participantId} ${event.durationMs}ms content=${event.contentLength}b`\n case \"participant.failed\":\n return `${event.cycleId} participant.failed ${event.participantId} — ${event.error}`\n case \"substrate.appended\":\n return `${event.cycleId} substrate.appended turn=${event.turnId} by=${event.participantId}`\n case \"state.written\":\n return `${event.cycleId} state.written ${event.participantId}`\n case \"cycle.idle\":\n return `${event.cycleId} cycle.idle`\n case \"cycle.finished\":\n return `${event.cycleId} cycle.finished ${event.outcome} appended=${event.turnsAppended} ${event.durationMs}ms`\n default: {\n // Forward-compat — render unknown kinds as JSON so a future\n // kernel version doesn't blank-line the log.\n const _exhaustive: never = event\n return JSON.stringify(_exhaustive)\n }\n }\n}\n","/**\n * OpenTelemetry adapter for the vendor-neutral Telemetry port.\n *\n * `otelTelemetry(tracer)` returns a `Telemetry` sink that maps the kernel's\n * `TelemetryEvent` stream onto OTel spans, grouping on `cycleId`:\n *\n * cycle.started → root span `agentproto.cycle` (held per cycleId)\n * participant.started → child span `agentproto.participant`\n * participant.finished → end child span (durationMs, contentLength attrs)\n * participant.failed → recordException + ERROR status, then end\n * substrate.read → addEvent on the cycle span\n * dispatch.decided → addEvent on the cycle span\n * cycle.finished/idle → outcome attrs, end the cycle span, drop from map\n * unknown kind → ignored silently (forward-compat contract)\n *\n * This is a PURE MAPPER: the host supplies the `Tracer`. No global provider,\n * no exporter, no context propagation beyond parenting child spans under the\n * cycle span.\n */\n\nimport {\n context,\n SpanStatusCode,\n trace,\n type Span,\n type Tracer,\n} from \"@opentelemetry/api\"\n\nimport type { Telemetry, TelemetryEvent } from \"./ports.js\"\n\nexport interface OtelTelemetryOptions {\n /** Span name for the per-cycle root span. Default \"agentproto.cycle\". */\n readonly cycleSpanName?: string\n /** Span name for each participant child span. Default \"agentproto.participant\". */\n readonly participantSpanName?: string\n}\n\n/**\n * Build a `Telemetry` sink that projects events onto OTel spans via the\n * host-supplied `tracer`.\n */\nexport function otelTelemetry(\n tracer: Tracer,\n options: OtelTelemetryOptions = {}\n): Telemetry {\n const cycleSpanName = options.cycleSpanName ?? \"agentproto.cycle\"\n const participantSpanName =\n options.participantSpanName ?? \"agentproto.participant\"\n\n const cycleSpans = new Map<string, Span>()\n const participantSpans = new Map<string, Map<string, Span>>()\n\n const startChild = (parent: Span, name: string): Span => {\n const parentCtx = trace.setSpan(context.active(), parent)\n return tracer.startSpan(name, undefined, parentCtx)\n }\n\n const dropParticipant = (cycleId: string, participantId: string): Span | undefined => {\n const byParticipant = participantSpans.get(cycleId)\n const span = byParticipant?.get(participantId)\n if (byParticipant && span) {\n byParticipant.delete(participantId)\n if (byParticipant.size === 0) participantSpans.delete(cycleId)\n }\n return span\n }\n\n const endCycle = (event: Extract<TelemetryEvent, { kind: \"cycle.finished\" | \"cycle.idle\" }>): void => {\n const span = cycleSpans.get(event.cycleId)\n if (!span) return\n if (event.kind === \"cycle.finished\") {\n span.setAttribute(\"agentproto.cycle.outcome\", event.outcome)\n span.setAttribute(\"agentproto.cycle.turnsAppended\", event.turnsAppended)\n span.setAttribute(\"agentproto.cycle.durationMs\", event.durationMs)\n } else {\n span.setAttribute(\"agentproto.cycle.outcome\", \"idle\")\n }\n // Any participant spans still open are orphaned by an incomplete cycle;\n // end them so the tracer isn't left with dangling spans.\n const orphans = participantSpans.get(event.cycleId)\n if (orphans) {\n for (const child of orphans.values()) child.end()\n participantSpans.delete(event.cycleId)\n }\n span.end()\n cycleSpans.delete(event.cycleId)\n }\n\n return {\n emit(event: TelemetryEvent): void {\n switch (event.kind) {\n case \"cycle.started\": {\n const span = tracer.startSpan(cycleSpanName)\n span.setAttribute(\"agentproto.cycleId\", event.cycleId)\n if (event.since !== undefined) {\n span.setAttribute(\"agentproto.cycle.since\", event.since)\n }\n cycleSpans.set(event.cycleId, span)\n return\n }\n case \"participant.started\": {\n const cycleSpan = cycleSpans.get(event.cycleId)\n if (!cycleSpan) return\n const span = startChild(cycleSpan, participantSpanName)\n span.setAttribute(\"agentproto.participantId\", event.participantId)\n span.setAttribute(\"agentproto.executorKind\", event.executorKind)\n let byParticipant = participantSpans.get(event.cycleId)\n if (!byParticipant) {\n byParticipant = new Map<string, Span>()\n participantSpans.set(event.cycleId, byParticipant)\n }\n byParticipant.set(event.participantId, span)\n return\n }\n case \"participant.finished\": {\n const span = dropParticipant(event.cycleId, event.participantId)\n if (!span) return\n span.setAttribute(\"agentproto.participant.durationMs\", event.durationMs)\n span.setAttribute(\"agentproto.participant.contentLength\", event.contentLength)\n span.end()\n return\n }\n case \"participant.failed\": {\n const span = dropParticipant(event.cycleId, event.participantId)\n if (!span) return\n span.recordException({ name: \"ParticipantExecuteError\", message: event.error })\n span.setStatus({ code: SpanStatusCode.ERROR, message: event.error })\n span.end()\n return\n }\n case \"substrate.read\": {\n const span = cycleSpans.get(event.cycleId)\n span?.addEvent(\"substrate.read\", {\n \"agentproto.substrateKind\": event.substrateKind,\n \"agentproto.turnCount\": event.turnCount,\n \"agentproto.durationMs\": event.durationMs,\n })\n return\n }\n case \"dispatch.decided\": {\n const span = cycleSpans.get(event.cycleId)\n span?.addEvent(\"dispatch.decided\", {\n \"agentproto.dispatcherKind\": event.dispatcherKind,\n \"agentproto.selected\": event.selected.join(\",\"),\n \"agentproto.durationMs\": event.durationMs,\n })\n return\n }\n case \"substrate.appended\": {\n const span = cycleSpans.get(event.cycleId)\n span?.addEvent(\"substrate.appended\", {\n \"agentproto.turnId\": event.turnId,\n \"agentproto.participantId\": event.participantId,\n })\n return\n }\n case \"state.written\": {\n const span = cycleSpans.get(event.cycleId)\n span?.addEvent(\"state.written\", {\n \"agentproto.participantId\": event.participantId,\n })\n return\n }\n case \"cycle.finished\":\n case \"cycle.idle\": {\n endCycle(event)\n return\n }\n default: {\n // Unknown kind — forward-compat contract: ignore silently.\n return\n }\n }\n },\n }\n}\n"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@agentproto/telemetry",
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "agentproto/telemetry — vendor-neutral Telemetry port + reference sinks (noop, stderr, array, compose) and an OpenTelemetry adapter that maps the kernel's TelemetryEvent stream to OTel spans grouped on cycleId. The adapter takes a host-supplied Tracer; no global provider or exporter wiring.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"agentproto",
|
|
7
|
+
"telemetry",
|
|
8
|
+
"opentelemetry",
|
|
9
|
+
"otel",
|
|
10
|
+
"observability",
|
|
11
|
+
"spans",
|
|
12
|
+
"open-standard",
|
|
13
|
+
"agentic"
|
|
14
|
+
],
|
|
15
|
+
"homepage": "https://agentik.net/docs",
|
|
16
|
+
"repository": {
|
|
17
|
+
"type": "git",
|
|
18
|
+
"url": "https://github.com/agentproto/ts"
|
|
19
|
+
},
|
|
20
|
+
"bugs": {
|
|
21
|
+
"url": "https://github.com/agentproto/ts/issues"
|
|
22
|
+
},
|
|
23
|
+
"license": "MIT",
|
|
24
|
+
"type": "module",
|
|
25
|
+
"main": "dist/index.mjs",
|
|
26
|
+
"module": "dist/index.mjs",
|
|
27
|
+
"types": "dist/index.d.ts",
|
|
28
|
+
"exports": {
|
|
29
|
+
".": {
|
|
30
|
+
"types": "./dist/index.d.ts",
|
|
31
|
+
"import": "./dist/index.mjs",
|
|
32
|
+
"default": "./dist/index.mjs"
|
|
33
|
+
},
|
|
34
|
+
"./package.json": "./package.json"
|
|
35
|
+
},
|
|
36
|
+
"files": [
|
|
37
|
+
"dist",
|
|
38
|
+
"README.md",
|
|
39
|
+
"LICENSE"
|
|
40
|
+
],
|
|
41
|
+
"publishConfig": {
|
|
42
|
+
"access": "public"
|
|
43
|
+
},
|
|
44
|
+
"peerDependencies": {
|
|
45
|
+
"@opentelemetry/api": "^1.9.0"
|
|
46
|
+
},
|
|
47
|
+
"devDependencies": {
|
|
48
|
+
"@opentelemetry/api": "^1.9.0",
|
|
49
|
+
"@types/node": "^25.6.2",
|
|
50
|
+
"tsup": "^8.5.1",
|
|
51
|
+
"typescript": "^5.9.3",
|
|
52
|
+
"vitest": "^3.2.4",
|
|
53
|
+
"@agentproto/tooling": "0.1.0-alpha.0"
|
|
54
|
+
},
|
|
55
|
+
"scripts": {
|
|
56
|
+
"dev": "tsup --watch",
|
|
57
|
+
"build": "tsup",
|
|
58
|
+
"clean": "rm -rf dist",
|
|
59
|
+
"check-types": "tsc --noEmit",
|
|
60
|
+
"test": "vitest run --passWithNoTests",
|
|
61
|
+
"test:watch": "vitest"
|
|
62
|
+
}
|
|
63
|
+
}
|