@mono-agent/observability 0.1.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 ADDED
@@ -0,0 +1,62 @@
1
+ # @mono-agent/observability
2
+
3
+ ## Category
4
+
5
+ Category: `observability`
6
+
7
+ ## Responsibility
8
+
9
+ Local JSONL run observability and host trace source discovery. It records runtime events and compact summaries, redacts sensitive payload fields by default, lists recorded runs, reads selected run detail for local operator surfaces, and lets running agent processes register their artifact directories in a file-backed trace registry.
10
+
11
+ ## Install / Usage
12
+
13
+ ```bash
14
+ pnpm --filter @mono-agent/observability run build
15
+ ```
16
+
17
+ ```ts
18
+ import {
19
+ createJsonlRunRecorder,
20
+ registerTraceSource,
21
+ combineRecordedRunEvents,
22
+ listRecordedRuns,
23
+ listTraceRuns,
24
+ readRecordedRun,
25
+ } from "@mono-agent/observability";
26
+ ```
27
+
28
+ ## Public API
29
+
30
+ - `createJsonlRunRecorder`, `JsonlRunRecorder`
31
+ - `listRecordedRuns`, `readRecordedRun`, `classifyRecordedRunEvent`
32
+ - `combineRecordedRunEvents`
33
+ - `registerTraceSource`, `listTraceSources`, `listTraceRuns`, `readTraceRun`
34
+ - `redactJsonValue`
35
+ - `ObservabilityError`, `ObservabilityReadError`
36
+ - Recorder, summary, list, detail, event, trace source, and trace run types
37
+
38
+ ## Timeline Display
39
+
40
+ Raw `.events.jsonl` artifacts stay append-only and one event per line. UI surfaces that need readable timelines can call `combineRecordedRunEvents()` to collapse adjacent assistant `thinking` or visible `text` stream chunks into bounded display rows while preserving raw source index ranges and event counts. Browser bundles can import the helper from `@mono-agent/observability/event-timeline` without pulling in the Node-backed artifact readers.
41
+
42
+ ## Trace Registry
43
+
44
+ The trace registry is a directory of `agent-runtime.trace-source.v1` manifest JSON files. A running host registers one source with a stable `sourceId`, label, artifact directory, process id, status, and heartbeat timestamp. The registry only discovers agents and their artifact locations; run summaries and event JSONL files remain in each source's artifact directory.
45
+
46
+ Running sources become `stale` when their heartbeat is older than the configured stale interval. Stopped and failed sources remain listed so the dashboard can distinguish a clean shutdown from a crashed or misconfigured host. The registry reader validates source/run ids against path traversal, ignores malformed manifests with warnings, and reuses the recorded-run reader's redaction and bounded-read limits.
47
+
48
+ ## Dependency Boundary
49
+
50
+ This package writes and reads local artifact and registry files only. It has no runtime, adapter, UI, database, queue, or network dependency.
51
+
52
+ ## What This Package Does Not Own
53
+
54
+ It does not provide a hosted trace backend, metrics service, durable database, UI, LangSmith export, OpenTelemetry exporter, or model-specific telemetry collector.
55
+
56
+ ## Verification
57
+
58
+ ```bash
59
+ pnpm --filter @mono-agent/observability run build
60
+ pnpm --filter @mono-agent/observability run typecheck
61
+ pnpm --filter @mono-agent/observability run test
62
+ ```
@@ -0,0 +1,50 @@
1
+ import { mkdir } from "node:fs/promises";
2
+ /**
3
+ * Shared, security-critical filesystem and validation helpers for the
4
+ * observability artifact store. These guards are duplicated nowhere else;
5
+ * every recorder/reader/registry module imports from here so the traversal
6
+ * defenses stay identical.
7
+ */
8
+ export declare const DEFAULT_MAX_RUNS = 50;
9
+ export declare const DEFAULT_MAX_EVENTS_PER_RUN = 500;
10
+ export declare const DEFAULT_MAX_STRING_BYTES = 4096;
11
+ /** Thrown to abort with a caller-supplied, code-tagged error. */
12
+ export type Raise = (message: string) => never;
13
+ /** Variant that also forwards the offending field name into the error details. */
14
+ export type RaiseField = (message: string, field: string) => never;
15
+ export declare function isRecord(value: unknown): value is Record<string, unknown>;
16
+ export declare function isErrno(error: unknown, code: string): boolean;
17
+ export declare function errorMessage(error: unknown): string;
18
+ export declare function stringField(record: Record<string, unknown>, key: string): string | undefined;
19
+ /**
20
+ * Collapse a candidate identifier into a path-safe artifact base name. Any
21
+ * character outside `[a-z0-9._-]` is replaced so the result can never contain a
22
+ * path separator; `safeJoin` still enforces containment as a second layer.
23
+ */
24
+ export declare function safeArtifactName(value: string): string;
25
+ /**
26
+ * Reject a run id that could traverse outside the artifact directory. Empty/
27
+ * non-string ids raise via `raiseEmpty`; traversal-shaped ids raise via
28
+ * `raiseTraversal`. The two callbacks let each package keep its distinct error
29
+ * code/message surface (recorded-runs and trace-sources historically differed
30
+ * on the empty-id code), while the guard logic stays identical.
31
+ */
32
+ export declare function normalizeRunId(runId: string, raiseTraversal: Raise, raiseEmpty?: Raise): string;
33
+ /**
34
+ * Resolve `fileName` under `root` and fail closed if the result escapes the
35
+ * (normalized, separator-terminated) root. `raise` carries the package-specific
36
+ * escape message ("escapes artifactDir" vs "escapes registryDir").
37
+ */
38
+ export declare function safeJoin(root: string, fileName: string, raise: Raise): string;
39
+ export declare function positiveInteger(value: number | undefined, fallback: number, field: string, raise: RaiseField): number;
40
+ export declare function minInteger(value: number | undefined, fallback: number, min: number, field: string, raise: RaiseField): number;
41
+ /**
42
+ * Write a serialized artifact atomically via a temp file + rename, so readers
43
+ * (list/read) never observe a half-written file. The registry already did this;
44
+ * the recorder now shares the same primitive for its summary + events files.
45
+ * The temp name carries a per-process sequence (not a timestamp) so concurrent
46
+ * writers in the same millisecond never collide on the temp path.
47
+ */
48
+ export declare function writeJsonAtomic(filePath: string, contents: string): Promise<void>;
49
+ export { mkdir };
50
+ //# sourceMappingURL=artifact-fs.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"artifact-fs.d.ts","sourceRoot":"","sources":["../src/artifact-fs.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAqB,MAAM,kBAAkB,CAAC;AAG5D;;;;;GAKG;AAEH,eAAO,MAAM,gBAAgB,KAAK,CAAC;AACnC,eAAO,MAAM,0BAA0B,MAAM,CAAC;AAC9C,eAAO,MAAM,wBAAwB,OAAQ,CAAC;AAE9C,iEAAiE;AACjE,MAAM,MAAM,KAAK,GAAG,CAAC,OAAO,EAAE,MAAM,KAAK,KAAK,CAAC;AAE/C,kFAAkF;AAClF,MAAM,MAAM,UAAU,GAAG,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,KAAK,CAAC;AAEnE,wBAAgB,QAAQ,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAEzE;AAED,wBAAgB,OAAO,CAAC,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAE7D;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAEnD;AAED,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAG5F;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAEtD;AAED;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,EAAE,cAAc,EAAE,KAAK,EAAE,UAAU,GAAE,KAAsB,GAAG,MAAM,CAS/G;AAED;;;;GAIG;AACH,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,GAAG,MAAM,CAQ7E;AAED,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,UAAU,GAAG,MAAM,CAQrH;AAED,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,UAAU,GAAG,MAAM,CAQ7H;AAID;;;;;;GAMG;AACH,wBAAsB,eAAe,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAKvF;AAED,OAAO,EAAE,KAAK,EAAE,CAAC"}
@@ -0,0 +1,97 @@
1
+ import { mkdir, rename, writeFile } from "node:fs/promises";
2
+ import { join, normalize, resolve, sep } from "node:path";
3
+ /**
4
+ * Shared, security-critical filesystem and validation helpers for the
5
+ * observability artifact store. These guards are duplicated nowhere else;
6
+ * every recorder/reader/registry module imports from here so the traversal
7
+ * defenses stay identical.
8
+ */
9
+ export const DEFAULT_MAX_RUNS = 50;
10
+ export const DEFAULT_MAX_EVENTS_PER_RUN = 500;
11
+ export const DEFAULT_MAX_STRING_BYTES = 4_096;
12
+ export function isRecord(value) {
13
+ return typeof value === "object" && value !== null && !Array.isArray(value);
14
+ }
15
+ export function isErrno(error, code) {
16
+ return isRecord(error) && error.code === code;
17
+ }
18
+ export function errorMessage(error) {
19
+ return error instanceof Error ? error.message : String(error);
20
+ }
21
+ export function stringField(record, key) {
22
+ const value = record[key];
23
+ return typeof value === "string" && value.trim().length > 0 ? value : undefined;
24
+ }
25
+ /**
26
+ * Collapse a candidate identifier into a path-safe artifact base name. Any
27
+ * character outside `[a-z0-9._-]` is replaced so the result can never contain a
28
+ * path separator; `safeJoin` still enforces containment as a second layer.
29
+ */
30
+ export function safeArtifactName(value) {
31
+ return value.trim().toLowerCase().replace(/[^a-z0-9._-]+/gu, "-").replace(/^-+|-+$/gu, "") || "run";
32
+ }
33
+ /**
34
+ * Reject a run id that could traverse outside the artifact directory. Empty/
35
+ * non-string ids raise via `raiseEmpty`; traversal-shaped ids raise via
36
+ * `raiseTraversal`. The two callbacks let each package keep its distinct error
37
+ * code/message surface (recorded-runs and trace-sources historically differed
38
+ * on the empty-id code), while the guard logic stays identical.
39
+ */
40
+ export function normalizeRunId(runId, raiseTraversal, raiseEmpty = raiseTraversal) {
41
+ if (typeof runId !== "string" || runId.trim().length === 0) {
42
+ raiseEmpty("runId must be a non-empty string.");
43
+ }
44
+ const trimmed = runId.trim();
45
+ if (trimmed.includes("/") || trimmed.includes("\\") || trimmed.includes("..")) {
46
+ raiseTraversal("runId cannot contain path separators or '..'.");
47
+ }
48
+ return trimmed;
49
+ }
50
+ /**
51
+ * Resolve `fileName` under `root` and fail closed if the result escapes the
52
+ * (normalized, separator-terminated) root. `raise` carries the package-specific
53
+ * escape message ("escapes artifactDir" vs "escapes registryDir").
54
+ */
55
+ export function safeJoin(root, fileName, raise) {
56
+ const normalizedRoot = normalize(resolve(root));
57
+ const resolved = normalize(join(normalizedRoot, fileName));
58
+ const safeRoot = normalizedRoot.endsWith(sep) ? normalizedRoot : `${normalizedRoot}${sep}`;
59
+ if (!resolved.startsWith(safeRoot)) {
60
+ raise("escape");
61
+ }
62
+ return resolved;
63
+ }
64
+ export function positiveInteger(value, fallback, field, raise) {
65
+ if (value === undefined) {
66
+ return fallback;
67
+ }
68
+ if (!Number.isInteger(value) || value < 1) {
69
+ raise(`${field} must be a positive integer.`, field);
70
+ }
71
+ return value;
72
+ }
73
+ export function minInteger(value, fallback, min, field, raise) {
74
+ if (value === undefined) {
75
+ return fallback;
76
+ }
77
+ if (!Number.isInteger(value) || value < min) {
78
+ raise(`${field} must be an integer of at least ${min}.`, field);
79
+ }
80
+ return value;
81
+ }
82
+ let atomicWriteSequence = 0;
83
+ /**
84
+ * Write a serialized artifact atomically via a temp file + rename, so readers
85
+ * (list/read) never observe a half-written file. The registry already did this;
86
+ * the recorder now shares the same primitive for its summary + events files.
87
+ * The temp name carries a per-process sequence (not a timestamp) so concurrent
88
+ * writers in the same millisecond never collide on the temp path.
89
+ */
90
+ export async function writeJsonAtomic(filePath, contents) {
91
+ atomicWriteSequence += 1;
92
+ const tempPath = `${filePath}.${process.pid}.${atomicWriteSequence}.tmp`;
93
+ await writeFile(tempPath, contents, "utf8");
94
+ await rename(tempPath, filePath);
95
+ }
96
+ export { mkdir };
97
+ //# sourceMappingURL=artifact-fs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"artifact-fs.js","sourceRoot":"","sources":["../src/artifact-fs.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Shared content-shaping helpers for run-event summaries. Both the recorded-run
3
+ * reader and the timeline combiner classify the same assistant message blocks
4
+ * and compact the same way, so the kind-walk and the truncation live here once.
5
+ */
6
+ export declare const SUMMARY_MAX_CHARS = 220;
7
+ export type AssistantContentKind = "thinking" | "text";
8
+ export interface AssistantContentWalk {
9
+ readonly kind: AssistantContentKind;
10
+ /** Concatenated block text, when any block carried text; otherwise undefined. */
11
+ readonly text: string | undefined;
12
+ }
13
+ /** Collapse whitespace and bound a summary string to {@link SUMMARY_MAX_CHARS} characters. */
14
+ export declare function compactString(value: string, maxChars?: number): string;
15
+ /**
16
+ * Walk an assistant `message` object's content blocks and report a single
17
+ * content kind, or `undefined` when the content is empty, mixed
18
+ * (thinking + text), or contains any non-text/thinking block. The walk also
19
+ * collects block text so streaming callers can join adjacent chunks.
20
+ */
21
+ export declare function classifyAssistantContent(message: unknown): AssistantContentWalk | undefined;
22
+ //# sourceMappingURL=content.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"content.d.ts","sourceRoot":"","sources":["../src/content.ts"],"names":[],"mappings":"AAEA;;;;GAIG;AAEH,eAAO,MAAM,iBAAiB,MAAM,CAAC;AAErC,MAAM,MAAM,oBAAoB,GAAG,UAAU,GAAG,MAAM,CAAC;AAEvD,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,IAAI,EAAE,oBAAoB,CAAC;IACpC,iFAAiF;IACjF,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,CAAC;CACnC;AAED,8FAA8F;AAC9F,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,SAAoB,GAAG,MAAM,CAMjF;AAED;;;;;GAKG;AACH,wBAAgB,wBAAwB,CAAC,OAAO,EAAE,OAAO,GAAG,oBAAoB,GAAG,SAAS,CAgC3F"}
@@ -0,0 +1,66 @@
1
+ import { isRecord } from "./artifact-fs.js";
2
+ /**
3
+ * Shared content-shaping helpers for run-event summaries. Both the recorded-run
4
+ * reader and the timeline combiner classify the same assistant message blocks
5
+ * and compact the same way, so the kind-walk and the truncation live here once.
6
+ */
7
+ export const SUMMARY_MAX_CHARS = 220;
8
+ /** Collapse whitespace and bound a summary string to {@link SUMMARY_MAX_CHARS} characters. */
9
+ export function compactString(value, maxChars = SUMMARY_MAX_CHARS) {
10
+ const compact = value.replace(/\s+/gu, " ").trim();
11
+ if (compact.length <= maxChars) {
12
+ return compact;
13
+ }
14
+ return `${compact.slice(0, maxChars)}…`;
15
+ }
16
+ /**
17
+ * Walk an assistant `message` object's content blocks and report a single
18
+ * content kind, or `undefined` when the content is empty, mixed
19
+ * (thinking + text), or contains any non-text/thinking block. The walk also
20
+ * collects block text so streaming callers can join adjacent chunks.
21
+ */
22
+ export function classifyAssistantContent(message) {
23
+ if (!isRecord(message)) {
24
+ return undefined;
25
+ }
26
+ const content = message.content;
27
+ if (!Array.isArray(content) || content.length === 0) {
28
+ return undefined;
29
+ }
30
+ let kind;
31
+ const texts = [];
32
+ for (const block of content) {
33
+ if (!isRecord(block) || (block.type !== "thinking" && block.type !== "text")) {
34
+ return undefined;
35
+ }
36
+ if (kind === undefined) {
37
+ kind = block.type;
38
+ }
39
+ else if (kind !== block.type) {
40
+ return undefined;
41
+ }
42
+ const text = blockText(block, block.type);
43
+ if (text !== undefined) {
44
+ texts.push(text);
45
+ }
46
+ }
47
+ if (kind === undefined) {
48
+ return undefined;
49
+ }
50
+ return {
51
+ kind,
52
+ text: texts.length > 0 ? texts.join("") : undefined,
53
+ };
54
+ }
55
+ function blockText(block, kind) {
56
+ const value = kind === "thinking"
57
+ ? rawStringField(block, "thinking") ?? rawStringField(block, "text") ?? rawStringField(block, "content")
58
+ : rawStringField(block, "text") ?? rawStringField(block, "content");
59
+ return value;
60
+ }
61
+ /** String field accessor that preserves empty/whitespace strings (unlike the trimmed-non-empty variant). */
62
+ function rawStringField(record, key) {
63
+ const value = record[key];
64
+ return typeof value === "string" ? value : undefined;
65
+ }
66
+ //# sourceMappingURL=content.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"content.js","sourceRoot":"","sources":["../src/content.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAE5C;;;;GAIG;AAEH,MAAM,CAAC,MAAM,iBAAiB,GAAG,GAAG,CAAC;AAUrC,8FAA8F;AAC9F,MAAM,UAAU,aAAa,CAAC,KAAa,EAAE,QAAQ,GAAG,iBAAiB;IACvE,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC;IACnD,IAAI,OAAO,CAAC,MAAM,IAAI,QAAQ,EAAE,CAAC;QAC/B,OAAO,OAAO,CAAC;IACjB,CAAC;IACD,OAAO,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,QAAQ,CAAC,GAAG,CAAC;AAC1C,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,wBAAwB,CAAC,OAAgB;IACvD,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC;QACvB,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC;IAChC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACpD,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,IAAI,IAAsC,CAAC;IAC3C,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;QAC5B,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,KAAK,UAAU,IAAI,KAAK,CAAC,IAAI,KAAK,MAAM,CAAC,EAAE,CAAC;YAC7E,OAAO,SAAS,CAAC;QACnB,CAAC;QACD,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;YACvB,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC;QACpB,CAAC;aAAM,IAAI,IAAI,KAAK,KAAK,CAAC,IAAI,EAAE,CAAC;YAC/B,OAAO,SAAS,CAAC;QACnB,CAAC;QACD,MAAM,IAAI,GAAG,SAAS,CAAC,KAAK,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;QAC1C,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;YACvB,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACnB,CAAC;IACH,CAAC;IACD,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;QACvB,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,OAAO;QACL,IAAI;QACJ,IAAI,EAAE,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,SAAS;KACpD,CAAC;AACJ,CAAC;AAED,SAAS,SAAS,CAAC,KAA8B,EAAE,IAA0B;IAC3E,MAAM,KAAK,GAAG,IAAI,KAAK,UAAU;QAC/B,CAAC,CAAC,cAAc,CAAC,KAAK,EAAE,UAAU,CAAC,IAAI,cAAc,CAAC,KAAK,EAAE,MAAM,CAAC,IAAI,cAAc,CAAC,KAAK,EAAE,SAAS,CAAC;QACxG,CAAC,CAAC,cAAc,CAAC,KAAK,EAAE,MAAM,CAAC,IAAI,cAAc,CAAC,KAAK,EAAE,SAAS,CAAC,CAAC;IACtE,OAAO,KAAK,CAAC;AACf,CAAC;AAED,4GAA4G;AAC5G,SAAS,cAAc,CAAC,MAA+B,EAAE,GAAW;IAClE,MAAM,KAAK,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC;IAC1B,OAAO,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC;AACvD,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { RecordedRunEvent, RecordedRunTimelineItem } from "./types.js";
2
+ export declare function combineRecordedRunEvents(events: readonly RecordedRunEvent[]): readonly RecordedRunTimelineItem[];
3
+ //# sourceMappingURL=event-timeline.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"event-timeline.d.ts","sourceRoot":"","sources":["../src/event-timeline.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACV,gBAAgB,EAChB,uBAAuB,EACxB,MAAM,YAAY,CAAC;AAIpB,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,SAAS,gBAAgB,EAAE,GAAG,SAAS,uBAAuB,EAAE,CAiChH"}
@@ -0,0 +1,78 @@
1
+ import { isRecord } from "./artifact-fs.js";
2
+ import { classifyAssistantContent, compactString } from "./content.js";
3
+ export function combineRecordedRunEvents(events) {
4
+ const timeline = [];
5
+ let index = 0;
6
+ while (index < events.length) {
7
+ const current = events[index];
8
+ if (current === undefined) {
9
+ break;
10
+ }
11
+ const currentChunk = assistantStreamChunk(current);
12
+ if (currentChunk === undefined) {
13
+ timeline.push(singleEventItem(current));
14
+ index += 1;
15
+ continue;
16
+ }
17
+ const group = [current];
18
+ const chunks = [currentChunk];
19
+ let nextIndex = index + 1;
20
+ while (nextIndex < events.length) {
21
+ const next = events[nextIndex];
22
+ const nextChunk = next === undefined ? undefined : assistantStreamChunk(next);
23
+ if (next === undefined || nextChunk === undefined || nextChunk.kind !== currentChunk.kind) {
24
+ break;
25
+ }
26
+ group.push(next);
27
+ chunks.push(nextChunk);
28
+ nextIndex += 1;
29
+ }
30
+ timeline.push(group.length === 1 ? singleEventItem(current) : combinedEventItem(group, chunks));
31
+ index = nextIndex;
32
+ }
33
+ return timeline;
34
+ }
35
+ function singleEventItem(event) {
36
+ return {
37
+ ...event,
38
+ sourceEventCount: 1,
39
+ sourceEventStartIndex: event.index,
40
+ sourceEventEndIndex: event.index,
41
+ };
42
+ }
43
+ function combinedEventItem(events, chunks) {
44
+ const first = events[0];
45
+ const firstChunk = chunks[0];
46
+ if (first === undefined || firstChunk === undefined) {
47
+ throw new Error("combinedEventItem requires at least one source event");
48
+ }
49
+ const last = events[events.length - 1] ?? first;
50
+ const kind = firstChunk.kind;
51
+ const summary = compactString(chunks.map((chunk) => chunk.text ?? "").join("") || events.map((event) => event.summary).join(" "));
52
+ return {
53
+ index: first.index,
54
+ ...(first.type === undefined ? {} : { type: first.type }),
55
+ category: kind === "thinking" ? "thinking" : "message",
56
+ ...(first.timestamp === undefined ? {} : { timestamp: first.timestamp }),
57
+ label: kind === "thinking" ? "Assistant thoughts" : "Assistant message",
58
+ summary,
59
+ payload: {
60
+ type: "assistant.timeline.combined",
61
+ contentKind: kind,
62
+ sourceEventCount: events.length,
63
+ sourceEventStartIndex: first.index,
64
+ sourceEventEndIndex: last.index,
65
+ preview: summary,
66
+ },
67
+ sourceEventCount: events.length,
68
+ sourceEventStartIndex: first.index,
69
+ sourceEventEndIndex: last.index,
70
+ };
71
+ }
72
+ function assistantStreamChunk(event) {
73
+ if (event.type !== "assistant" || !isRecord(event.payload)) {
74
+ return undefined;
75
+ }
76
+ return classifyAssistantContent(event.payload.message);
77
+ }
78
+ //# sourceMappingURL=event-timeline.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"event-timeline.js","sourceRoot":"","sources":["../src/event-timeline.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ export { createJsonlRunRecorder, ObservabilityError, } from "./recorder.js";
2
+ export type { ObservabilityErrorCode, ObservabilityErrorDetails, } from "./recorder.js";
3
+ export { combineRecordedRunEvents, } from "./event-timeline.js";
4
+ export { listRecordedRuns, ObservabilityReadError, readRecordedRun, } from "./recorded-runs.js";
5
+ export type { ObservabilityReadErrorCode, ObservabilityReadErrorDetails, } from "./recorded-runs.js";
6
+ export { listTraceRuns, listTraceSources, readTraceRun, registerTraceSource, TraceSourceRegistryError, } from "./trace-sources.js";
7
+ export type { TraceSourceRegistryErrorCode, TraceSourceRegistryErrorDetails, } from "./trace-sources.js";
8
+ export type { JsonlRunReaderOptions, JsonlRunRecorderOptions, RecordedRunDetail, RecordedRunEvent, RecordedRunEventCategory, RecordedRunListItem, RecordedRunListResult, RecordedRunTimelineItem, RunRecorder, RunSummary, RunSummaryStatus, RuntimeEventLike, RuntimeResultLike, RegisterTraceSourceOptions, TraceRunDetail, TraceRunListItem, TraceRunListOptions, TraceRunListResult, TraceSourceHandle, TraceSourceHealth, TraceSourceListItem, TraceSourceListResult, TraceSourceManifest, TraceSourceRegistryOptions, TraceSourceStatus, UpdateTraceSourceOptions, } from "./types.js";
9
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,sBAAsB,EACtB,kBAAkB,GACnB,MAAM,eAAe,CAAC;AACvB,YAAY,EACV,sBAAsB,EACtB,yBAAyB,GAC1B,MAAM,eAAe,CAAC;AACvB,OAAO,EACL,wBAAwB,GACzB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACL,gBAAgB,EAChB,sBAAsB,EACtB,eAAe,GAChB,MAAM,oBAAoB,CAAC;AAC5B,YAAY,EACV,0BAA0B,EAC1B,6BAA6B,GAC9B,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACL,aAAa,EACb,gBAAgB,EAChB,YAAY,EACZ,mBAAmB,EACnB,wBAAwB,GACzB,MAAM,oBAAoB,CAAC;AAC5B,YAAY,EACV,4BAA4B,EAC5B,+BAA+B,GAChC,MAAM,oBAAoB,CAAC;AAC5B,YAAY,EACV,qBAAqB,EACrB,uBAAuB,EACvB,iBAAiB,EACjB,gBAAgB,EAChB,wBAAwB,EACxB,mBAAmB,EACnB,qBAAqB,EACrB,uBAAuB,EACvB,WAAW,EACX,UAAU,EACV,gBAAgB,EAChB,gBAAgB,EAChB,iBAAiB,EACjB,0BAA0B,EAC1B,cAAc,EACd,gBAAgB,EAChB,mBAAmB,EACnB,kBAAkB,EAClB,iBAAiB,EACjB,iBAAiB,EACjB,mBAAmB,EACnB,qBAAqB,EACrB,mBAAmB,EACnB,0BAA0B,EAC1B,iBAAiB,EACjB,wBAAwB,GACzB,MAAM,YAAY,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,5 @@
1
+ export { createJsonlRunRecorder, ObservabilityError, } from "./recorder.js";
2
+ export { combineRecordedRunEvents, } from "./event-timeline.js";
3
+ export { listRecordedRuns, ObservabilityReadError, readRecordedRun, } from "./recorded-runs.js";
4
+ export { listTraceRuns, listTraceSources, readTraceRun, registerTraceSource, TraceSourceRegistryError, } from "./trace-sources.js";
5
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,sBAAsB,EACtB,kBAAkB,GACnB,MAAM,eAAe,CAAC;AAKvB,OAAO,EACL,wBAAwB,GACzB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACL,gBAAgB,EAChB,sBAAsB,EACtB,eAAe,GAChB,MAAM,oBAAoB,CAAC;AAK5B,OAAO,EACL,aAAa,EACb,gBAAgB,EAChB,YAAY,EACZ,mBAAmB,EACnB,wBAAwB,GACzB,MAAM,oBAAoB,CAAC"}
@@ -0,0 +1,14 @@
1
+ import type { JsonlRunReaderOptions, RecordedRunDetail, RecordedRunEventCategory, RecordedRunListResult } from "./types.js";
2
+ export type ObservabilityReadErrorCode = "invalid_reader_options" | "invalid_run_id";
3
+ export type ObservabilityReadErrorDetails = Record<string, unknown> & {
4
+ readonly code: ObservabilityReadErrorCode;
5
+ };
6
+ export declare class ObservabilityReadError extends Error {
7
+ readonly code: ObservabilityReadErrorCode;
8
+ readonly details: ObservabilityReadErrorDetails;
9
+ constructor(code: ObservabilityReadErrorCode, message: string, details?: Record<string, unknown>);
10
+ }
11
+ export declare function listRecordedRuns(options: JsonlRunReaderOptions): Promise<RecordedRunListResult>;
12
+ export declare function readRecordedRun(options: JsonlRunReaderOptions, runId: string): Promise<RecordedRunDetail | undefined>;
13
+ export declare function classifyRecordedRunEvent(event: unknown): RecordedRunEventCategory;
14
+ //# sourceMappingURL=recorded-runs.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"recorded-runs.d.ts","sourceRoot":"","sources":["../src/recorded-runs.ts"],"names":[],"mappings":"AAmBA,OAAO,KAAK,EACV,qBAAqB,EACrB,iBAAiB,EAEjB,wBAAwB,EAExB,qBAAqB,EAGtB,MAAM,YAAY,CAAC;AAmBpB,MAAM,MAAM,0BAA0B,GAAG,wBAAwB,GAAG,gBAAgB,CAAC;AACrF,MAAM,MAAM,6BAA6B,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG;IAAE,QAAQ,CAAC,IAAI,EAAE,0BAA0B,CAAA;CAAE,CAAC;AAEpH,qBAAa,sBAAuB,SAAQ,KAAK;IAC/C,QAAQ,CAAC,IAAI,EAAE,0BAA0B,CAAC;IAC1C,QAAQ,CAAC,OAAO,EAAE,6BAA6B,CAAC;gBAEpC,IAAI,EAAE,0BAA0B,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM;CAMrG;AAED,wBAAsB,gBAAgB,CAAC,OAAO,EAAE,qBAAqB,GAAG,OAAO,CAAC,qBAAqB,CAAC,CAUrG;AAED,wBAAsB,eAAe,CACnC,OAAO,EAAE,qBAAqB,EAC9B,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC,iBAAiB,GAAG,SAAS,CAAC,CAoBxC;AAED,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,OAAO,GAAG,wBAAwB,CAiCjF"}