@vauban-org/agent-sdk 1.2.0 → 1.3.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/CONTRACT.md +595 -7
- package/dist/index.d.ts +4 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/orchestration/ooda/agent.d.ts.map +1 -1
- package/dist/orchestration/ooda/agent.js +36 -0
- package/dist/orchestration/ooda/agent.js.map +1 -1
- package/dist/orchestration/ooda/types.d.ts +11 -0
- package/dist/orchestration/ooda/types.d.ts.map +1 -1
- package/dist/skills/_secrets.d.ts +16 -0
- package/dist/skills/_secrets.d.ts.map +1 -0
- package/dist/skills/_secrets.js +20 -0
- package/dist/skills/_secrets.js.map +1 -0
- package/dist/skills/alpaca-quote.d.ts +2 -2
- package/dist/skills/alpaca-quote.d.ts.map +1 -1
- package/dist/skills/alpaca-quote.js +51 -20
- package/dist/skills/alpaca-quote.js.map +1 -1
- package/dist/skills/send-email.d.ts +2 -2
- package/dist/skills/send-email.d.ts.map +1 -1
- package/dist/skills/send-email.js +1 -12
- package/dist/skills/send-email.js.map +1 -1
- package/dist/skills/slack-notify.d.ts.map +1 -1
- package/dist/skills/slack-notify.js +52 -21
- package/dist/skills/slack-notify.js.map +1 -1
- package/dist/skills/telegram-notify.d.ts.map +1 -1
- package/dist/skills/telegram-notify.js +48 -19
- package/dist/skills/telegram-notify.js.map +1 -1
- package/dist/skills/web-search.d.ts.map +1 -1
- package/dist/skills/web-search.js +85 -40
- package/dist/skills/web-search.js.map +1 -1
- package/dist/telemetry/bus.d.ts +54 -0
- package/dist/telemetry/bus.d.ts.map +1 -0
- package/dist/telemetry/bus.js +159 -0
- package/dist/telemetry/bus.js.map +1 -0
- package/dist/telemetry/index.d.ts +35 -0
- package/dist/telemetry/index.d.ts.map +1 -0
- package/dist/telemetry/index.js +30 -0
- package/dist/telemetry/index.js.map +1 -0
- package/dist/telemetry/port.d.ts +121 -0
- package/dist/telemetry/port.d.ts.map +1 -0
- package/dist/telemetry/port.js +48 -0
- package/dist/telemetry/port.js.map +1 -0
- package/dist/telemetry/sinks/otlp.d.ts +45 -0
- package/dist/telemetry/sinks/otlp.d.ts.map +1 -0
- package/dist/telemetry/sinks/otlp.js +195 -0
- package/dist/telemetry/sinks/otlp.js.map +1 -0
- package/dist/telemetry/sinks/sqlite.d.ts +32 -0
- package/dist/telemetry/sinks/sqlite.d.ts.map +1 -0
- package/dist/telemetry/sinks/sqlite.js +170 -0
- package/dist/telemetry/sinks/sqlite.js.map +1 -0
- package/dist/telemetry/sinks/stdout.d.ts +22 -0
- package/dist/telemetry/sinks/stdout.d.ts.map +1 -0
- package/dist/telemetry/sinks/stdout.js +38 -0
- package/dist/telemetry/sinks/stdout.js.map +1 -0
- package/docs/telemetry/migration.md +155 -0
- package/docs/telemetry/overview.md +154 -0
- package/docs/telemetry/privacy.md +127 -0
- package/docs/telemetry/sinks/cc.md +155 -0
- package/docs/telemetry/sinks/otlp.md +146 -0
- package/docs/telemetry/sinks/sqlite.md +126 -0
- package/docs/telemetry/sinks/stdout.md +82 -0
- package/package.json +18 -19
- package/src/index.ts +30 -1
- package/src/orchestration/ooda/agent.ts +50 -0
- package/src/orchestration/ooda/types.ts +12 -0
- package/src/skills/_secrets.ts +25 -0
- package/src/skills/alpaca-quote.ts +68 -23
- package/src/skills/send-email.ts +1 -12
- package/src/skills/slack-notify.ts +73 -30
- package/src/skills/telegram-notify.ts +70 -24
- package/src/skills/web-search.ts +132 -50
- package/src/telemetry/bus.test.ts +231 -0
- package/src/telemetry/bus.ts +241 -0
- package/src/telemetry/index.ts +49 -0
- package/src/telemetry/port.ts +160 -0
- package/src/telemetry/sinks/otlp.test.ts +146 -0
- package/src/telemetry/sinks/otlp.ts +250 -0
- package/src/telemetry/sinks/sqlite.test.ts +121 -0
- package/src/telemetry/sinks/sqlite.ts +260 -0
- package/src/telemetry/sinks/stdout.test.ts +109 -0
- package/src/telemetry/sinks/stdout.ts +59 -0
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TelemetryBus — fanout to multiple {@link TelemetrySink}s, non-blocking.
|
|
3
|
+
*
|
|
4
|
+
* Invariants :
|
|
5
|
+
* 1. Sink failures are ISOLATED — one sink throwing never affects the others
|
|
6
|
+
* nor the host agent loop. Failures are logged via the bus logger.
|
|
7
|
+
* 2. The bus itself implements {@link TelemetrySink}, so it can be passed
|
|
8
|
+
* transparently to consumers expecting a single sink (composition).
|
|
9
|
+
* 3. When `nonBlocking: true` (default), `start`/`step`/`finish` return
|
|
10
|
+
* immediately and dispatch in background. Errors surface via the logger.
|
|
11
|
+
* 4. Backpressure : if internal queue > `maxQueueDepth`, oldest events are
|
|
12
|
+
* dropped and counted.
|
|
13
|
+
*
|
|
14
|
+
* Ref: command-center:sprint-693:telemetry-bus
|
|
15
|
+
*/
|
|
16
|
+
import { NOOP_TELEMETRY_SINK, } from "./port.js";
|
|
17
|
+
const NOOP_LOGGER = {
|
|
18
|
+
warn: () => {
|
|
19
|
+
/* no-op */
|
|
20
|
+
},
|
|
21
|
+
error: () => {
|
|
22
|
+
/* no-op */
|
|
23
|
+
},
|
|
24
|
+
};
|
|
25
|
+
class TelemetryBusImpl {
|
|
26
|
+
name = "telemetry-bus";
|
|
27
|
+
_sinks;
|
|
28
|
+
_logger;
|
|
29
|
+
_nonBlocking;
|
|
30
|
+
_maxQueueDepth;
|
|
31
|
+
/** Per-sink queue of pending events (FIFO). */
|
|
32
|
+
_queues;
|
|
33
|
+
/** Per-sink drain promise (single-flight). */
|
|
34
|
+
_draining;
|
|
35
|
+
_dropped = 0;
|
|
36
|
+
_sinkErrors = 0;
|
|
37
|
+
_dispatched = 0;
|
|
38
|
+
constructor(opts) {
|
|
39
|
+
this._sinks = opts.sinks;
|
|
40
|
+
this._logger = opts.logger ?? NOOP_LOGGER;
|
|
41
|
+
this._nonBlocking = opts.nonBlocking ?? true;
|
|
42
|
+
this._maxQueueDepth = opts.maxQueueDepth ?? 1000;
|
|
43
|
+
this._queues = this._sinks.map(() => []);
|
|
44
|
+
this._draining = this._sinks.map(() => null);
|
|
45
|
+
}
|
|
46
|
+
get counters() {
|
|
47
|
+
return {
|
|
48
|
+
dropped: this._dropped,
|
|
49
|
+
sinkErrors: this._sinkErrors,
|
|
50
|
+
dispatched: this._dispatched,
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
async start(event) {
|
|
54
|
+
return this._fanout({
|
|
55
|
+
kind: "start",
|
|
56
|
+
exec: (s) => s.start(event),
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
async step(runId, delta) {
|
|
60
|
+
return this._fanout({
|
|
61
|
+
kind: "step",
|
|
62
|
+
exec: (s) => s.step(runId, delta),
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
async finish(runId, event) {
|
|
66
|
+
return this._fanout({
|
|
67
|
+
kind: "finish",
|
|
68
|
+
exec: (s) => s.finish(runId, event),
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
async flush() {
|
|
72
|
+
await Promise.all([
|
|
73
|
+
...this._draining.map((p) => p ?? Promise.resolve()),
|
|
74
|
+
...this._sinks.map((s) => s.flush?.().catch((err) => {
|
|
75
|
+
this._sinkErrors += 1;
|
|
76
|
+
this._logger.warn({ sink: s.name, err: errorMessage(err) }, "telemetry-bus: flush failed");
|
|
77
|
+
})),
|
|
78
|
+
]);
|
|
79
|
+
}
|
|
80
|
+
// ── internal ───────────────────────────────────────────────────────────────
|
|
81
|
+
async _fanout(evt) {
|
|
82
|
+
if (this._nonBlocking) {
|
|
83
|
+
// Enqueue on each sink, kick drain in background.
|
|
84
|
+
this._sinks.forEach((_, i) => this._enqueue(i, evt));
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
// Blocking mode (tests) — dispatch sequentially and surface errors via log.
|
|
88
|
+
await Promise.all(this._sinks.map((sink) => this._dispatchOne(sink, evt)));
|
|
89
|
+
}
|
|
90
|
+
_enqueue(sinkIdx, evt) {
|
|
91
|
+
const queue = this._queues[sinkIdx];
|
|
92
|
+
if (queue.length >= this._maxQueueDepth) {
|
|
93
|
+
queue.shift(); // drop oldest
|
|
94
|
+
this._dropped += 1;
|
|
95
|
+
const sink = this._sinks[sinkIdx];
|
|
96
|
+
this._logger.warn({ sink: sink.name, depth: queue.length, dropped: this._dropped }, "telemetry-bus: queue overflow, oldest dropped");
|
|
97
|
+
}
|
|
98
|
+
queue.push(evt);
|
|
99
|
+
void this._drain(sinkIdx);
|
|
100
|
+
}
|
|
101
|
+
async _drain(sinkIdx) {
|
|
102
|
+
if (this._draining[sinkIdx])
|
|
103
|
+
return;
|
|
104
|
+
const sink = this._sinks[sinkIdx];
|
|
105
|
+
const queue = this._queues[sinkIdx];
|
|
106
|
+
const promise = (async () => {
|
|
107
|
+
while (queue.length > 0) {
|
|
108
|
+
const evt = queue.shift();
|
|
109
|
+
await this._dispatchOne(sink, evt);
|
|
110
|
+
}
|
|
111
|
+
})();
|
|
112
|
+
this._draining[sinkIdx] = promise;
|
|
113
|
+
try {
|
|
114
|
+
await promise;
|
|
115
|
+
}
|
|
116
|
+
finally {
|
|
117
|
+
this._draining[sinkIdx] = null;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
async _dispatchOne(sink, evt) {
|
|
121
|
+
try {
|
|
122
|
+
await evt.exec(sink);
|
|
123
|
+
this._dispatched += 1;
|
|
124
|
+
}
|
|
125
|
+
catch (err) {
|
|
126
|
+
this._sinkErrors += 1;
|
|
127
|
+
this._logger.warn({
|
|
128
|
+
sink: sink.name,
|
|
129
|
+
kind: evt.kind,
|
|
130
|
+
err: errorMessage(err),
|
|
131
|
+
}, "telemetry-bus: sink dispatch failed (isolated)");
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
// ─── Factory ─────────────────────────────────────────────────────────────────
|
|
136
|
+
/**
|
|
137
|
+
* Build a TelemetryBus that fans events to all sinks non-blockingly.
|
|
138
|
+
*
|
|
139
|
+
* If `sinks` is empty, returns {@link NOOP_TELEMETRY_SINK} so callers never
|
|
140
|
+
* need to null-check.
|
|
141
|
+
*/
|
|
142
|
+
export function createTelemetryBus(opts) {
|
|
143
|
+
if (opts.sinks.length === 0) {
|
|
144
|
+
// Wrap NOOP with stub counters + flush for type consistency.
|
|
145
|
+
return {
|
|
146
|
+
...NOOP_TELEMETRY_SINK,
|
|
147
|
+
counters: { dropped: 0, sinkErrors: 0, dispatched: 0 },
|
|
148
|
+
async flush() {
|
|
149
|
+
/* no-op */
|
|
150
|
+
},
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
return new TelemetryBusImpl(opts);
|
|
154
|
+
}
|
|
155
|
+
// ─── Helpers ─────────────────────────────────────────────────────────────────
|
|
156
|
+
function errorMessage(err) {
|
|
157
|
+
return err instanceof Error ? err.message : String(err);
|
|
158
|
+
}
|
|
159
|
+
//# sourceMappingURL=bus.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bus.js","sourceRoot":"","sources":["../../src/telemetry/bus.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Public telemetry API for agent-sdk consumers.
|
|
3
|
+
*
|
|
4
|
+
* Per ADR-ECO-039. Sovereign multi-sink observability for OODA agents.
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
* ```ts
|
|
8
|
+
* import {
|
|
9
|
+
* createOODAAgent,
|
|
10
|
+
* stdoutTelemetrySink,
|
|
11
|
+
* localSqliteTelemetrySink,
|
|
12
|
+
* } from "@vauban-org/agent-sdk";
|
|
13
|
+
*
|
|
14
|
+
* createOODAAgent({
|
|
15
|
+
* agentId: "my-agent",
|
|
16
|
+
* telemetry: {
|
|
17
|
+
* sinks: [
|
|
18
|
+
* stdoutTelemetrySink(),
|
|
19
|
+
* localSqliteTelemetrySink(),
|
|
20
|
+
* ],
|
|
21
|
+
* },
|
|
22
|
+
* });
|
|
23
|
+
* ```
|
|
24
|
+
*/
|
|
25
|
+
export { NOOP_TELEMETRY_SINK, TelemetrySinkError } from "./port.js";
|
|
26
|
+
export type { TelemetryRunStatus, TelemetryRunFinish, TelemetryRunStart, TelemetryRunStep, TelemetrySink, } from "./port.js";
|
|
27
|
+
export { createTelemetryBus } from "./bus.js";
|
|
28
|
+
export type { TelemetryBusOptions, TelemetryCounters, TelemetryLogger, } from "./bus.js";
|
|
29
|
+
export { stdoutTelemetrySink } from "./sinks/stdout.js";
|
|
30
|
+
export type { StdoutTelemetrySinkOptions } from "./sinks/stdout.js";
|
|
31
|
+
export { localSqliteTelemetrySink } from "./sinks/sqlite.js";
|
|
32
|
+
export type { LocalSqliteTelemetrySinkOptions } from "./sinks/sqlite.js";
|
|
33
|
+
export { otlpTelemetrySink } from "./sinks/otlp.js";
|
|
34
|
+
export type { OtlpTelemetrySinkOptions } from "./sinks/otlp.js";
|
|
35
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/telemetry/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,OAAO,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,MAAM,WAAW,CAAC;AACpE,YAAY,EACV,kBAAkB,EAClB,kBAAkB,EAClB,iBAAiB,EACjB,gBAAgB,EAChB,aAAa,GACd,MAAM,WAAW,CAAC;AAEnB,OAAO,EAAE,kBAAkB,EAAE,MAAM,UAAU,CAAC;AAC9C,YAAY,EACV,mBAAmB,EACnB,iBAAiB,EACjB,eAAe,GAChB,MAAM,UAAU,CAAC;AAElB,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AACxD,YAAY,EAAE,0BAA0B,EAAE,MAAM,mBAAmB,CAAC;AAEpE,OAAO,EAAE,wBAAwB,EAAE,MAAM,mBAAmB,CAAC;AAC7D,YAAY,EAAE,+BAA+B,EAAE,MAAM,mBAAmB,CAAC;AAEzE,OAAO,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AACpD,YAAY,EAAE,wBAAwB,EAAE,MAAM,iBAAiB,CAAC"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Public telemetry API for agent-sdk consumers.
|
|
3
|
+
*
|
|
4
|
+
* Per ADR-ECO-039. Sovereign multi-sink observability for OODA agents.
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
* ```ts
|
|
8
|
+
* import {
|
|
9
|
+
* createOODAAgent,
|
|
10
|
+
* stdoutTelemetrySink,
|
|
11
|
+
* localSqliteTelemetrySink,
|
|
12
|
+
* } from "@vauban-org/agent-sdk";
|
|
13
|
+
*
|
|
14
|
+
* createOODAAgent({
|
|
15
|
+
* agentId: "my-agent",
|
|
16
|
+
* telemetry: {
|
|
17
|
+
* sinks: [
|
|
18
|
+
* stdoutTelemetrySink(),
|
|
19
|
+
* localSqliteTelemetrySink(),
|
|
20
|
+
* ],
|
|
21
|
+
* },
|
|
22
|
+
* });
|
|
23
|
+
* ```
|
|
24
|
+
*/
|
|
25
|
+
export { NOOP_TELEMETRY_SINK, TelemetrySinkError } from "./port.js";
|
|
26
|
+
export { createTelemetryBus } from "./bus.js";
|
|
27
|
+
export { stdoutTelemetrySink } from "./sinks/stdout.js";
|
|
28
|
+
export { localSqliteTelemetrySink } from "./sinks/sqlite.js";
|
|
29
|
+
export { otlpTelemetrySink } from "./sinks/otlp.js";
|
|
30
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/telemetry/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,OAAO,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,MAAM,WAAW,CAAC;AASpE,OAAO,EAAE,kBAAkB,EAAE,MAAM,UAAU,CAAC;AAO9C,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAGxD,OAAO,EAAE,wBAAwB,EAAE,MAAM,mBAAmB,CAAC;AAG7D,OAAO,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC"}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TelemetryPort — sovereign multi-sink observability for agent runs.
|
|
3
|
+
*
|
|
4
|
+
* Per ADR-ECO-039 (accepted 2026-05-16). Sink-based interface replaces the
|
|
5
|
+
* implicit coupling between agent-sdk and the CC `agent_run` table (legacy
|
|
6
|
+
* `AgentRunTracker`). Callers configure 0..N sinks; the {@link TelemetryBus}
|
|
7
|
+
* fans events out non-blockingly.
|
|
8
|
+
*
|
|
9
|
+
* Three modes :
|
|
10
|
+
* 1. Standalone : `[stdoutTelemetrySink(), localSqliteTelemetrySink()]`
|
|
11
|
+
* 2. + CC SaaS : add `commandCenterTelemetrySink({apiKey})`
|
|
12
|
+
* 3. Tiered : same code, API key changes entitlement server-side
|
|
13
|
+
*
|
|
14
|
+
* Ref: command-center:sprint-693:port-interface
|
|
15
|
+
*/
|
|
16
|
+
export interface TelemetryRunStart {
|
|
17
|
+
/** Caller-generated run id (matches the OODA loop's runId / trace). */
|
|
18
|
+
runId: string;
|
|
19
|
+
agentId: string;
|
|
20
|
+
agentVersion: string;
|
|
21
|
+
model: string;
|
|
22
|
+
provider: string;
|
|
23
|
+
/** Optional tenant scoping — required by the CC sink. */
|
|
24
|
+
tenantId?: string;
|
|
25
|
+
/** OTel trace id for replay linkage. */
|
|
26
|
+
traceId?: string;
|
|
27
|
+
/** ISO8601 timestamp of cycle start. */
|
|
28
|
+
startedAt: string;
|
|
29
|
+
}
|
|
30
|
+
export interface TelemetryRunStep {
|
|
31
|
+
/** Monotonic step index within the run, starting at 0. */
|
|
32
|
+
stepIndex: number;
|
|
33
|
+
/** OODA phase or step kind ("observe" | "orient" | "decide" | "act" | ...). */
|
|
34
|
+
kind: string;
|
|
35
|
+
/** Status of THIS step (not the whole run). */
|
|
36
|
+
status: "completed" | "failed" | "skipped";
|
|
37
|
+
inputTokens: number;
|
|
38
|
+
outputTokens: number;
|
|
39
|
+
toolCalls?: number;
|
|
40
|
+
/**
|
|
41
|
+
* USD cost delta. MUST be >= 0. Use 0 for non-LLM steps.
|
|
42
|
+
* Fixed(6) precision recommended.
|
|
43
|
+
*/
|
|
44
|
+
costUsd: number;
|
|
45
|
+
/** Optional duration in ms — useful for OTLP span span_start/span_end. */
|
|
46
|
+
durationMs?: number;
|
|
47
|
+
/** Optional metadata. Hashed at sink boundary unless `includePayloads`. */
|
|
48
|
+
metadata?: Record<string, unknown>;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Run final status.
|
|
52
|
+
*
|
|
53
|
+
* NEW (sprint-693) : "skipped" — added so that session_guard /
|
|
54
|
+
* risk_guard / heap_exceeded short-circuits are observably distinct
|
|
55
|
+
* from "success" or "failed".
|
|
56
|
+
*/
|
|
57
|
+
export type TelemetryRunStatus = "success" | "failed" | "skipped" | "timeout" | "incoherent";
|
|
58
|
+
export interface TelemetryRunFinish {
|
|
59
|
+
status: TelemetryRunStatus;
|
|
60
|
+
/** Free-text reason — e.g. "session_guard:business-hours". */
|
|
61
|
+
stopReason?: string;
|
|
62
|
+
/** Only set when status === "failed" | "incoherent". */
|
|
63
|
+
errorMessage?: string;
|
|
64
|
+
/** ISO8601 timestamp of cycle finish. */
|
|
65
|
+
finishedAt: string;
|
|
66
|
+
/** Optional cumulative totals — sinks may compute or trust caller. */
|
|
67
|
+
totalInputTokens?: number;
|
|
68
|
+
totalOutputTokens?: number;
|
|
69
|
+
totalCostUsd?: number;
|
|
70
|
+
totalToolCalls?: number;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* TelemetrySink — pluggable destination for run lifecycle events.
|
|
74
|
+
*
|
|
75
|
+
* Contract :
|
|
76
|
+
* - `start` is called ONCE per run before any `step`.
|
|
77
|
+
* - `step` MAY be called 0..N times after start; the bus serialises per-run.
|
|
78
|
+
* - `finish` is called ONCE after the last `step` (or directly after `start`
|
|
79
|
+
* for skipped runs).
|
|
80
|
+
* - All methods MUST be idempotent w.r.t. their (runId, stepIndex) pair —
|
|
81
|
+
* the bus may retry on transient sink failures.
|
|
82
|
+
* - Implementations MUST NOT throw on transient errors ; return a rejected
|
|
83
|
+
* Promise instead so the bus can isolate the failure.
|
|
84
|
+
*
|
|
85
|
+
* Implementations should NOT block the agent loop. Long-running I/O should
|
|
86
|
+
* be batched/queued internally.
|
|
87
|
+
*/
|
|
88
|
+
export interface TelemetrySink {
|
|
89
|
+
/** Unique sink identifier — used for audit log + Prometheus labels. */
|
|
90
|
+
readonly name: string;
|
|
91
|
+
/**
|
|
92
|
+
* Record run start. Returns a sink-local reference (e.g. DB UUID, file
|
|
93
|
+
* offset, span id) the bus may pass back to `step` / `finish` for
|
|
94
|
+
* correlation. Return value is opaque to the bus.
|
|
95
|
+
*/
|
|
96
|
+
start(event: TelemetryRunStart): Promise<string | void>;
|
|
97
|
+
/** Record a single OODA step delta. */
|
|
98
|
+
step(runId: string, delta: TelemetryRunStep): Promise<void>;
|
|
99
|
+
/** Record run finish. */
|
|
100
|
+
finish(runId: string, event: TelemetryRunFinish): Promise<void>;
|
|
101
|
+
/**
|
|
102
|
+
* Flush pending I/O (best-effort). Called on agent shutdown.
|
|
103
|
+
* Implementations without a buffer can no-op.
|
|
104
|
+
*/
|
|
105
|
+
flush?(): Promise<void>;
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* No-op sink. The bus uses this when no sinks are configured, so callers
|
|
109
|
+
* never need to null-check.
|
|
110
|
+
*/
|
|
111
|
+
export declare const NOOP_TELEMETRY_SINK: TelemetrySink;
|
|
112
|
+
/**
|
|
113
|
+
* Thrown only at the BUS boundary when ALL sinks fail. Individual sink
|
|
114
|
+
* failures are warned and isolated (see {@link TelemetryBus}).
|
|
115
|
+
*/
|
|
116
|
+
export declare class TelemetrySinkError extends Error {
|
|
117
|
+
readonly sinkName: string;
|
|
118
|
+
readonly cause?: unknown | undefined;
|
|
119
|
+
constructor(message: string, sinkName: string, cause?: unknown | undefined);
|
|
120
|
+
}
|
|
121
|
+
//# sourceMappingURL=port.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"port.d.ts","sourceRoot":"","sources":["../../src/telemetry/port.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAIH,MAAM,WAAW,iBAAiB;IAChC,uEAAuE;IACvE,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,MAAM,CAAC;IACrB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,yDAAyD;IACzD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,wCAAwC;IACxC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,wCAAwC;IACxC,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,gBAAgB;IAC/B,0DAA0D;IAC1D,SAAS,EAAE,MAAM,CAAC;IAClB,+EAA+E;IAC/E,IAAI,EAAE,MAAM,CAAC;IACb,+CAA+C;IAC/C,MAAM,EAAE,WAAW,GAAG,QAAQ,GAAG,SAAS,CAAC;IAC3C,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;OAGG;IACH,OAAO,EAAE,MAAM,CAAC;IAChB,0EAA0E;IAC1E,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,2EAA2E;IAC3E,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACpC;AAED;;;;;;GAMG;AACH,MAAM,MAAM,kBAAkB,GAC1B,SAAS,GACT,QAAQ,GACR,SAAS,GACT,SAAS,GACT,YAAY,CAAC;AAEjB,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,kBAAkB,CAAC;IAC3B,8DAA8D;IAC9D,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,wDAAwD;IACxD,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,yCAAyC;IACzC,UAAU,EAAE,MAAM,CAAC;IACnB,sEAAsE;IACtE,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAID;;;;;;;;;;;;;;;GAeG;AACH,MAAM,WAAW,aAAa;IAC5B,uEAAuE;IACvE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAEtB;;;;OAIG;IACH,KAAK,CAAC,KAAK,EAAE,iBAAiB,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IAExD,uCAAuC;IACvC,IAAI,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAE5D,yBAAyB;IACzB,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,kBAAkB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAEhE;;;OAGG;IACH,KAAK,CAAC,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACzB;AAID;;;GAGG;AACH,eAAO,MAAM,mBAAmB,EAAE,aAWjC,CAAC;AAIF;;;GAGG;AACH,qBAAa,kBAAmB,SAAQ,KAAK;aAGzB,QAAQ,EAAE,MAAM;aAChB,KAAK,CAAC,EAAE,OAAO;gBAF/B,OAAO,EAAE,MAAM,EACC,QAAQ,EAAE,MAAM,EAChB,KAAK,CAAC,EAAE,OAAO,YAAA;CAKlC"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TelemetryPort — sovereign multi-sink observability for agent runs.
|
|
3
|
+
*
|
|
4
|
+
* Per ADR-ECO-039 (accepted 2026-05-16). Sink-based interface replaces the
|
|
5
|
+
* implicit coupling between agent-sdk and the CC `agent_run` table (legacy
|
|
6
|
+
* `AgentRunTracker`). Callers configure 0..N sinks; the {@link TelemetryBus}
|
|
7
|
+
* fans events out non-blockingly.
|
|
8
|
+
*
|
|
9
|
+
* Three modes :
|
|
10
|
+
* 1. Standalone : `[stdoutTelemetrySink(), localSqliteTelemetrySink()]`
|
|
11
|
+
* 2. + CC SaaS : add `commandCenterTelemetrySink({apiKey})`
|
|
12
|
+
* 3. Tiered : same code, API key changes entitlement server-side
|
|
13
|
+
*
|
|
14
|
+
* Ref: command-center:sprint-693:port-interface
|
|
15
|
+
*/
|
|
16
|
+
// ─── NOOP — default when no sinks configured ─────────────────────────────────
|
|
17
|
+
/**
|
|
18
|
+
* No-op sink. The bus uses this when no sinks are configured, so callers
|
|
19
|
+
* never need to null-check.
|
|
20
|
+
*/
|
|
21
|
+
export const NOOP_TELEMETRY_SINK = {
|
|
22
|
+
name: "noop",
|
|
23
|
+
async start() {
|
|
24
|
+
/* no-op */
|
|
25
|
+
},
|
|
26
|
+
async step() {
|
|
27
|
+
/* no-op */
|
|
28
|
+
},
|
|
29
|
+
async finish() {
|
|
30
|
+
/* no-op */
|
|
31
|
+
},
|
|
32
|
+
};
|
|
33
|
+
// ─── Error type ──────────────────────────────────────────────────────────────
|
|
34
|
+
/**
|
|
35
|
+
* Thrown only at the BUS boundary when ALL sinks fail. Individual sink
|
|
36
|
+
* failures are warned and isolated (see {@link TelemetryBus}).
|
|
37
|
+
*/
|
|
38
|
+
export class TelemetrySinkError extends Error {
|
|
39
|
+
sinkName;
|
|
40
|
+
cause;
|
|
41
|
+
constructor(message, sinkName, cause) {
|
|
42
|
+
super(`[telemetry:${sinkName}] ${message}`);
|
|
43
|
+
this.sinkName = sinkName;
|
|
44
|
+
this.cause = cause;
|
|
45
|
+
this.name = "TelemetrySinkError";
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
//# sourceMappingURL=port.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"port.js","sourceRoot":"","sources":["../../src/telemetry/port.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AA+GH,gFAAgF;AAEhF;;;GAGG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAkB;IAChD,IAAI,EAAE,MAAM;IACZ,KAAK,CAAC,KAAK;QACT,WAAW;IACb,CAAC;IACD,KAAK,CAAC,IAAI;QACR,WAAW;IACb,CAAC;IACD,KAAK,CAAC,MAAM;QACV,WAAW;IACb,CAAC;CACF,CAAC;AAEF,gFAAgF;AAEhF;;;GAGG;AACH,MAAM,OAAO,kBAAmB,SAAQ,KAAK;IAGzB;IACA;IAHlB,YACE,OAAe,EACC,QAAgB,EAChB,KAAe;QAE/B,KAAK,CAAC,cAAc,QAAQ,KAAK,OAAO,EAAE,CAAC,CAAC;QAH5B,aAAQ,GAAR,QAAQ,CAAQ;QAChB,UAAK,GAAL,KAAK,CAAU;QAG/B,IAAI,CAAC,IAAI,GAAG,oBAAoB,CAAC;IACnC,CAAC;CACF"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* otlpTelemetrySink — OTLP/HTTP exporter for agent runs.
|
|
3
|
+
*
|
|
4
|
+
* Maps {@link TelemetryRunStart} / Step / Finish to OpenTelemetry spans using
|
|
5
|
+
* GenAI semantic conventions. Sends as protobuf-over-HTTP to any compliant
|
|
6
|
+
* OTLP receiver (Tempo, Jaeger, Langfuse self-host, Honeycomb…).
|
|
7
|
+
*
|
|
8
|
+
* Lightweight implementation : JSON over HTTP `application/json` rather than
|
|
9
|
+
* protobuf to avoid pulling in `@opentelemetry/exporter-trace-otlp-proto`.
|
|
10
|
+
* The OTel spec allows JSON-encoded OTLP as a first-class transport (since
|
|
11
|
+
* v1.0). This keeps the SDK zero-dep — no `@opentelemetry/*` runtime needed.
|
|
12
|
+
*
|
|
13
|
+
* Ref: command-center:sprint-693:sink-otlp
|
|
14
|
+
*/
|
|
15
|
+
import type { TelemetrySink } from "../port.js";
|
|
16
|
+
export interface OtlpTelemetrySinkOptions {
|
|
17
|
+
/**
|
|
18
|
+
* OTLP/HTTP endpoint base URL (no trailing slash).
|
|
19
|
+
* Example : `https://langfuse.vauban.tech/api/public/otel`
|
|
20
|
+
* Path `/v1/traces` is appended automatically.
|
|
21
|
+
*/
|
|
22
|
+
url: string;
|
|
23
|
+
/** Static headers (Authorization, x-tenant-id, …). */
|
|
24
|
+
headers?: Record<string, string>;
|
|
25
|
+
/**
|
|
26
|
+
* Service name attached to every span. Defaults to `"vauban-agent-sdk"`.
|
|
27
|
+
* Override per-deployment to disambiguate in your tracing backend.
|
|
28
|
+
*/
|
|
29
|
+
serviceName?: string;
|
|
30
|
+
/**
|
|
31
|
+
* fetch implementation override (for tests). Defaults to global `fetch`.
|
|
32
|
+
*/
|
|
33
|
+
fetchImpl?: typeof fetch;
|
|
34
|
+
/**
|
|
35
|
+
* Request timeout in ms. Defaults to 5000.
|
|
36
|
+
*/
|
|
37
|
+
timeoutMs?: number;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Create an OTLP/HTTP sink. The sink batches at the bus level (FIFO), so
|
|
41
|
+
* one HTTP request is sent per event in this minimal implementation.
|
|
42
|
+
* High-volume deployments should wrap with an upstream batcher.
|
|
43
|
+
*/
|
|
44
|
+
export declare function otlpTelemetrySink(opts: OtlpTelemetrySinkOptions): TelemetrySink;
|
|
45
|
+
//# sourceMappingURL=otlp.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"otlp.d.ts","sourceRoot":"","sources":["../../../src/telemetry/sinks/otlp.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,KAAK,EAIV,aAAa,EACd,MAAM,YAAY,CAAC;AAEpB,MAAM,WAAW,wBAAwB;IACvC;;;;OAIG;IACH,GAAG,EAAE,MAAM,CAAC;IACZ,sDAAsD;IACtD,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC;;;OAGG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;OAEG;IACH,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;IACzB;;OAEG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAqDD;;;;GAIG;AACH,wBAAgB,iBAAiB,CAC/B,IAAI,EAAE,wBAAwB,GAC7B,aAAa,CAiJf"}
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* otlpTelemetrySink — OTLP/HTTP exporter for agent runs.
|
|
3
|
+
*
|
|
4
|
+
* Maps {@link TelemetryRunStart} / Step / Finish to OpenTelemetry spans using
|
|
5
|
+
* GenAI semantic conventions. Sends as protobuf-over-HTTP to any compliant
|
|
6
|
+
* OTLP receiver (Tempo, Jaeger, Langfuse self-host, Honeycomb…).
|
|
7
|
+
*
|
|
8
|
+
* Lightweight implementation : JSON over HTTP `application/json` rather than
|
|
9
|
+
* protobuf to avoid pulling in `@opentelemetry/exporter-trace-otlp-proto`.
|
|
10
|
+
* The OTel spec allows JSON-encoded OTLP as a first-class transport (since
|
|
11
|
+
* v1.0). This keeps the SDK zero-dep — no `@opentelemetry/*` runtime needed.
|
|
12
|
+
*
|
|
13
|
+
* Ref: command-center:sprint-693:sink-otlp
|
|
14
|
+
*/
|
|
15
|
+
// ─── Helpers ─────────────────────────────────────────────────────────────────
|
|
16
|
+
const HEX = "0123456789abcdef";
|
|
17
|
+
function randHex(bytes) {
|
|
18
|
+
let s = "";
|
|
19
|
+
for (let i = 0; i < bytes * 2; i++) {
|
|
20
|
+
s += HEX[Math.floor(Math.random() * 16)];
|
|
21
|
+
}
|
|
22
|
+
return s;
|
|
23
|
+
}
|
|
24
|
+
function isoToUnixNano(iso) {
|
|
25
|
+
const ms = Date.parse(iso);
|
|
26
|
+
if (Number.isNaN(ms))
|
|
27
|
+
return `${Date.now() * 1_000_000}`;
|
|
28
|
+
return `${ms * 1_000_000}`;
|
|
29
|
+
}
|
|
30
|
+
function toKeyValue(attrs) {
|
|
31
|
+
const out = [];
|
|
32
|
+
for (const [k, v] of Object.entries(attrs)) {
|
|
33
|
+
if (v === null || v === undefined)
|
|
34
|
+
continue;
|
|
35
|
+
if (typeof v === "number") {
|
|
36
|
+
out.push({
|
|
37
|
+
key: k,
|
|
38
|
+
value: Number.isInteger(v)
|
|
39
|
+
? { intValue: String(v) }
|
|
40
|
+
: { doubleValue: v },
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
else {
|
|
44
|
+
out.push({ key: k, value: { stringValue: String(v) } });
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
return out;
|
|
48
|
+
}
|
|
49
|
+
// ─── Factory ─────────────────────────────────────────────────────────────────
|
|
50
|
+
/**
|
|
51
|
+
* Create an OTLP/HTTP sink. The sink batches at the bus level (FIFO), so
|
|
52
|
+
* one HTTP request is sent per event in this minimal implementation.
|
|
53
|
+
* High-volume deployments should wrap with an upstream batcher.
|
|
54
|
+
*/
|
|
55
|
+
export function otlpTelemetrySink(opts) {
|
|
56
|
+
const baseUrl = opts.url.replace(/\/+$/, "");
|
|
57
|
+
const tracesUrl = `${baseUrl}/v1/traces`;
|
|
58
|
+
const headers = {
|
|
59
|
+
"Content-Type": "application/json",
|
|
60
|
+
...(opts.headers ?? {}),
|
|
61
|
+
};
|
|
62
|
+
const serviceName = opts.serviceName ?? "vauban-agent-sdk";
|
|
63
|
+
const fetchImpl = opts.fetchImpl ?? fetch;
|
|
64
|
+
const timeoutMs = opts.timeoutMs ?? 5000;
|
|
65
|
+
const runStates = new Map();
|
|
66
|
+
async function postSpan(span) {
|
|
67
|
+
const body = {
|
|
68
|
+
resourceSpans: [
|
|
69
|
+
{
|
|
70
|
+
resource: {
|
|
71
|
+
attributes: toKeyValue({
|
|
72
|
+
"service.name": serviceName,
|
|
73
|
+
"telemetry.sdk.name": "vauban-agent-sdk",
|
|
74
|
+
"telemetry.sdk.language": "nodejs",
|
|
75
|
+
}),
|
|
76
|
+
},
|
|
77
|
+
scopeSpans: [
|
|
78
|
+
{
|
|
79
|
+
scope: { name: "vauban.agent.ooda", version: "1.3.0" },
|
|
80
|
+
spans: [span],
|
|
81
|
+
},
|
|
82
|
+
],
|
|
83
|
+
},
|
|
84
|
+
],
|
|
85
|
+
};
|
|
86
|
+
const ctrl = new AbortController();
|
|
87
|
+
const t = setTimeout(() => ctrl.abort(), timeoutMs);
|
|
88
|
+
try {
|
|
89
|
+
const res = await fetchImpl(tracesUrl, {
|
|
90
|
+
method: "POST",
|
|
91
|
+
headers,
|
|
92
|
+
body: JSON.stringify(body),
|
|
93
|
+
signal: ctrl.signal,
|
|
94
|
+
});
|
|
95
|
+
if (!res.ok) {
|
|
96
|
+
const text = await res.text().catch(() => "");
|
|
97
|
+
throw new Error(`OTLP ${res.status}: ${text.slice(0, 200)}`);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
finally {
|
|
101
|
+
clearTimeout(t);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
return {
|
|
105
|
+
name: "otlp",
|
|
106
|
+
async start(event) {
|
|
107
|
+
const traceId = event.traceId ?? randHex(16);
|
|
108
|
+
const spanId = randHex(8);
|
|
109
|
+
runStates.set(event.runId, {
|
|
110
|
+
traceId,
|
|
111
|
+
spanId,
|
|
112
|
+
startUnixNano: isoToUnixNano(event.startedAt),
|
|
113
|
+
attributes: {
|
|
114
|
+
"vauban.run_id": event.runId,
|
|
115
|
+
"vauban.agent.id": event.agentId,
|
|
116
|
+
"vauban.agent.version": event.agentVersion,
|
|
117
|
+
"gen_ai.system": event.provider,
|
|
118
|
+
"gen_ai.request.model": event.model,
|
|
119
|
+
...(event.tenantId ? { "vauban.tenant.id": event.tenantId } : {}),
|
|
120
|
+
},
|
|
121
|
+
steps: 0,
|
|
122
|
+
});
|
|
123
|
+
// Don't post yet — wait for finish to send full span (start+end).
|
|
124
|
+
},
|
|
125
|
+
async step(runId, delta) {
|
|
126
|
+
const state = runStates.get(runId);
|
|
127
|
+
if (!state)
|
|
128
|
+
return;
|
|
129
|
+
state.steps += 1;
|
|
130
|
+
// Emit a child span per step for granular tracing.
|
|
131
|
+
const stepStart = isoToUnixNano(new Date().toISOString());
|
|
132
|
+
const durationNs = (delta.durationMs ?? 0) * 1_000_000;
|
|
133
|
+
const stepSpan = {
|
|
134
|
+
traceId: state.traceId,
|
|
135
|
+
spanId: randHex(8),
|
|
136
|
+
parentSpanId: state.spanId,
|
|
137
|
+
name: `ooda.${delta.kind}`,
|
|
138
|
+
kind: 1, // SPAN_KIND_INTERNAL
|
|
139
|
+
startTimeUnixNano: stepStart,
|
|
140
|
+
endTimeUnixNano: String(BigInt(stepStart) + BigInt(durationNs)),
|
|
141
|
+
attributes: toKeyValue({
|
|
142
|
+
"vauban.step.index": delta.stepIndex,
|
|
143
|
+
"vauban.step.kind": delta.kind,
|
|
144
|
+
"vauban.step.status": delta.status,
|
|
145
|
+
"gen_ai.usage.input_tokens": delta.inputTokens,
|
|
146
|
+
"gen_ai.usage.output_tokens": delta.outputTokens,
|
|
147
|
+
"vauban.step.tool_calls": delta.toolCalls ?? 0,
|
|
148
|
+
"vauban.step.cost_usd": delta.costUsd,
|
|
149
|
+
}),
|
|
150
|
+
status: {
|
|
151
|
+
code: delta.status === "failed" ? 2 : 1, // ERROR=2, OK=1
|
|
152
|
+
},
|
|
153
|
+
};
|
|
154
|
+
await postSpan(stepSpan);
|
|
155
|
+
},
|
|
156
|
+
async finish(runId, event) {
|
|
157
|
+
const state = runStates.get(runId);
|
|
158
|
+
if (!state)
|
|
159
|
+
return;
|
|
160
|
+
runStates.delete(runId);
|
|
161
|
+
const endNano = isoToUnixNano(event.finishedAt);
|
|
162
|
+
const span = {
|
|
163
|
+
traceId: state.traceId,
|
|
164
|
+
spanId: state.spanId,
|
|
165
|
+
name: `ooda.cycle.${state.attributes["vauban.agent.id"] ?? "agent"}`,
|
|
166
|
+
kind: 1,
|
|
167
|
+
startTimeUnixNano: state.startUnixNano,
|
|
168
|
+
endTimeUnixNano: endNano,
|
|
169
|
+
attributes: toKeyValue({
|
|
170
|
+
...state.attributes,
|
|
171
|
+
"vauban.run.status": event.status,
|
|
172
|
+
...(event.stopReason
|
|
173
|
+
? { "vauban.run.stop_reason": event.stopReason }
|
|
174
|
+
: {}),
|
|
175
|
+
...(event.totalInputTokens !== undefined
|
|
176
|
+
? { "gen_ai.usage.input_tokens": event.totalInputTokens }
|
|
177
|
+
: {}),
|
|
178
|
+
...(event.totalOutputTokens !== undefined
|
|
179
|
+
? { "gen_ai.usage.output_tokens": event.totalOutputTokens }
|
|
180
|
+
: {}),
|
|
181
|
+
...(event.totalCostUsd !== undefined
|
|
182
|
+
? { "vauban.run.cost_usd": event.totalCostUsd }
|
|
183
|
+
: {}),
|
|
184
|
+
"vauban.run.steps": state.steps,
|
|
185
|
+
}),
|
|
186
|
+
status: {
|
|
187
|
+
code: event.status === "failed" || event.status === "incoherent" ? 2 : 1,
|
|
188
|
+
...(event.errorMessage ? { message: event.errorMessage } : {}),
|
|
189
|
+
},
|
|
190
|
+
};
|
|
191
|
+
await postSpan(span);
|
|
192
|
+
},
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
//# sourceMappingURL=otlp.js.map
|