@bli-cockpit/telemetry-core 0.1.33 → 0.1.35

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.
@@ -105,6 +105,9 @@ export declare const CollectorHeartbeatSchema: z.ZodObject<{
105
105
  hook_timeouts_24h: z.ZodOptional<z.ZodNumber>;
106
106
  hook_printed_24h: z.ZodOptional<z.ZodNumber>;
107
107
  hook_failed_24h: z.ZodOptional<z.ZodNumber>;
108
+ hook_via_daemon_24h: z.ZodOptional<z.ZodNumber>;
109
+ hook_via_direct_24h: z.ZodOptional<z.ZodNumber>;
110
+ hook_skipped_trivial_24h: z.ZodOptional<z.ZodNumber>;
108
111
  hook_stats_reason: z.ZodOptional<z.ZodString>;
109
112
  hook_performance: z.ZodOptional<z.ZodObject<{
110
113
  schema_version: z.ZodLiteral<"memory-hook-performance.v1">;
package/dist/index.d.ts CHANGED
@@ -9,6 +9,7 @@ export * from "./evidence-reconcile.js";
9
9
  export * from "./evidence-upload.js";
10
10
  export * from "./ingest-dto.js";
11
11
  export * from "./local-config.js";
12
+ export * from "./memory-daemon-endpoint.js";
12
13
  export * from "./memory-hook-stats.js";
13
14
  export * from "./memory-hook-performance.js";
14
15
  export * from "./memory-install-receipt.js";
package/dist/index.js CHANGED
@@ -9,6 +9,7 @@ export * from "./evidence-reconcile.js";
9
9
  export * from "./evidence-upload.js";
10
10
  export * from "./ingest-dto.js";
11
11
  export * from "./local-config.js";
12
+ export * from "./memory-daemon-endpoint.js";
12
13
  export * from "./memory-hook-stats.js";
13
14
  export * from "./memory-hook-performance.js";
14
15
  export * from "./memory-install-receipt.js";
@@ -0,0 +1,114 @@
1
+ /**
2
+ * WHERE THE RESIDENT MEMORY DAEMON LISTENS (BLI-3884).
3
+ *
4
+ * `@bli-cockpit/memory-mcp` RUNS the daemon (`bli-memory-mcp daemon`) and its
5
+ * hooks CALL it; `@bli-cockpit/local-collector` only ever ASKS whether it is
6
+ * answering (`cockpit doctor`'s `memory-daemon` row, `cockpit memory status`).
7
+ * The two packages are published separately and never import each other, so
8
+ * the address, the protocol words and the four numbers that govern the
9
+ * handshake live here, in the one package they both depend on — the same rule
10
+ * `memory-hook-stats.ts` next door was written under, and for the same reason:
11
+ * two copies of a wire contract kept in step by comment is the BLI-2541
12
+ * failure.
13
+ *
14
+ * ## The address
15
+ *
16
+ * macOS and Linux: a unix socket in the collector's own state directory,
17
+ * created 0600 inside a 0700 directory, so the endpoint is the OS user's and
18
+ * nobody else's. Windows: a named pipe, `\\.\pipe\bli-memory-<user>` — a
19
+ * Windows pipe has no directory and no mode bits, so the USER NAME is what
20
+ * makes it per-user, and the daemon never puts anything on the wire that a
21
+ * caller has not already got (see the protocol below).
22
+ *
23
+ * ## What may never travel on it
24
+ *
25
+ * The device token. The hook sends `{event, payload, cwd}` and nothing else;
26
+ * the daemon reads the machine's own session file itself. A protocol that
27
+ * carried the credential would put it in every hook process on the machine
28
+ * for no gain — the daemon is already running as the same user that can read
29
+ * the session file.
30
+ */
31
+ /** The wire vocabulary's version, sent on every request and answer. */
32
+ export declare const MEMORY_DAEMON_PROTOCOL_VERSION = "memory-daemon.v1";
33
+ /** The socket file, in the collector's state directory. One spelling, here. */
34
+ export declare const MEMORY_DAEMON_SOCKET_FILE_NAME = "memory-daemon.sock";
35
+ /**
36
+ * The stamp a hook writes when it spawns a daemon, beside the socket. It is
37
+ * the rate limit: a machine where the daemon cannot start must not spawn one
38
+ * per prompt, so a hook that finds no daemon looks here first and only tries
39
+ * again an hour later.
40
+ */
41
+ export declare const MEMORY_DAEMON_SPAWN_STAMP_FILE_NAME = "memory-daemon-spawn.json";
42
+ /** Windows named pipes live in one flat namespace; the user makes it per-user. */
43
+ export declare const MEMORY_DAEMON_PIPE_PREFIX = "\\\\.\\pipe\\bli-memory-";
44
+ /**
45
+ * How long a hook may wait to CONNECT before giving up on the daemon.
46
+ *
47
+ * A connect to a live local socket is sub-millisecond. This budget is not
48
+ * sized for the connect; it is sized so that a daemon which is wedged,
49
+ * half-dead or being replaced costs a person 50 ms and then gets out of the
50
+ * way. The whole call still sits inside the hook's own `HOOK_BUDGETS`
51
+ * deadline, which was started before the connect.
52
+ */
53
+ export declare const MEMORY_DAEMON_CONNECT_TIMEOUT_MS = 50;
54
+ /** At most one spawn attempt per hour per machine. */
55
+ export declare const MEMORY_DAEMON_SPAWN_INTERVAL_MS = 3600000;
56
+ /** The daemon exits after this long with no request. Two hours. */
57
+ export declare const MEMORY_DAEMON_IDLE_EXIT_MS = 7200000;
58
+ /**
59
+ * How long a unix socket PATH may be, in bytes, before the platform refuses to
60
+ * bind it. `sun_path` is 104 on macOS/BSD and 108 on Linux; 100 is the safe
61
+ * floor under both. A real machine is nowhere near it — `/Users/<name>/.local/
62
+ * state/bli-cockpit/memory-daemon.sock` is about 60 characters — but a deeply
63
+ * nested HOME (a container, a temp directory in a test) is, and `bind` answers
64
+ * EINVAL, which reads as a bug rather than as a limit. The endpoint says so by
65
+ * name instead, and every hook simply goes the direct way.
66
+ */
67
+ export declare const MEMORY_DAEMON_UNIX_PATH_MAX = 100;
68
+ /**
69
+ * The largest single line either side will read. A prompt is capped at 1,000
70
+ * characters before it ever reaches the door, and a recall block is a few KB;
71
+ * 1 MB is far above both and bounds a peer that never sends a newline.
72
+ */
73
+ export declare const MEMORY_DAEMON_MAX_LINE_BYTES = 1048576;
74
+ export interface MemoryDaemonEndpoint {
75
+ /** Which kind of endpoint this platform gets. */
76
+ kind: "unix" | "pipe";
77
+ /** What `net.connect` / `server.listen` is handed. */
78
+ address: string;
79
+ /** The spawn rate-limit stamp. Always a real file, on both platforms. */
80
+ stampFile: string;
81
+ /** The directory both live in (the pipe's stamp lives here too). */
82
+ stateDir: string;
83
+ /**
84
+ * True when a unix socket at this address would exceed `sun_path`. Nothing
85
+ * is invented in that case — no second address, no temp directory that the
86
+ * daemon and the probe could disagree about; the daemon refuses by name and
87
+ * every hook keeps working on the direct path.
88
+ */
89
+ addressTooLong: boolean;
90
+ }
91
+ export interface MemoryDaemonEndpointOptions {
92
+ homeDir: string;
93
+ /** Defaults to this process's platform. Injected by the tests. */
94
+ platform?: NodeJS.Platform;
95
+ /** The OS user name. Only Windows uses it; ignored elsewhere. */
96
+ username?: string;
97
+ }
98
+ /**
99
+ * The one place the daemon's address is computed. Both packages call this;
100
+ * nobody joins a socket path by hand.
101
+ */
102
+ export declare function memoryDaemonEndpoint(options: MemoryDaemonEndpointOptions): MemoryDaemonEndpoint;
103
+ /**
104
+ * A Windows user name folded into something a pipe name may hold.
105
+ *
106
+ * Real ones carry spaces, dots and a `DOMAIN\user` prefix, and a pipe name is
107
+ * a path segment — an unfiltered name would either fail to bind or, worse,
108
+ * bind somewhere unintended. Everything outside `[A-Za-z0-9_-]` becomes `-`,
109
+ * the whole thing is lower-cased so two spellings of one account do not get
110
+ * two daemons, and a name that reduces to nothing falls back to `user`, which
111
+ * is still per-user in practice because the pipe is created by that user's
112
+ * own session.
113
+ */
114
+ export declare function pipeUserSegment(username: string | undefined): string;
@@ -0,0 +1,124 @@
1
+ /**
2
+ * WHERE THE RESIDENT MEMORY DAEMON LISTENS (BLI-3884).
3
+ *
4
+ * `@bli-cockpit/memory-mcp` RUNS the daemon (`bli-memory-mcp daemon`) and its
5
+ * hooks CALL it; `@bli-cockpit/local-collector` only ever ASKS whether it is
6
+ * answering (`cockpit doctor`'s `memory-daemon` row, `cockpit memory status`).
7
+ * The two packages are published separately and never import each other, so
8
+ * the address, the protocol words and the four numbers that govern the
9
+ * handshake live here, in the one package they both depend on — the same rule
10
+ * `memory-hook-stats.ts` next door was written under, and for the same reason:
11
+ * two copies of a wire contract kept in step by comment is the BLI-2541
12
+ * failure.
13
+ *
14
+ * ## The address
15
+ *
16
+ * macOS and Linux: a unix socket in the collector's own state directory,
17
+ * created 0600 inside a 0700 directory, so the endpoint is the OS user's and
18
+ * nobody else's. Windows: a named pipe, `\\.\pipe\bli-memory-<user>` — a
19
+ * Windows pipe has no directory and no mode bits, so the USER NAME is what
20
+ * makes it per-user, and the daemon never puts anything on the wire that a
21
+ * caller has not already got (see the protocol below).
22
+ *
23
+ * ## What may never travel on it
24
+ *
25
+ * The device token. The hook sends `{event, payload, cwd}` and nothing else;
26
+ * the daemon reads the machine's own session file itself. A protocol that
27
+ * carried the credential would put it in every hook process on the machine
28
+ * for no gain — the daemon is already running as the same user that can read
29
+ * the session file.
30
+ */
31
+ import { getUserLocalCockpitPaths } from "./paths.js";
32
+ /** The wire vocabulary's version, sent on every request and answer. */
33
+ export const MEMORY_DAEMON_PROTOCOL_VERSION = "memory-daemon.v1";
34
+ /** The socket file, in the collector's state directory. One spelling, here. */
35
+ export const MEMORY_DAEMON_SOCKET_FILE_NAME = "memory-daemon.sock";
36
+ /**
37
+ * The stamp a hook writes when it spawns a daemon, beside the socket. It is
38
+ * the rate limit: a machine where the daemon cannot start must not spawn one
39
+ * per prompt, so a hook that finds no daemon looks here first and only tries
40
+ * again an hour later.
41
+ */
42
+ export const MEMORY_DAEMON_SPAWN_STAMP_FILE_NAME = "memory-daemon-spawn.json";
43
+ /** Windows named pipes live in one flat namespace; the user makes it per-user. */
44
+ export const MEMORY_DAEMON_PIPE_PREFIX = "\\\\.\\pipe\\bli-memory-";
45
+ /**
46
+ * How long a hook may wait to CONNECT before giving up on the daemon.
47
+ *
48
+ * A connect to a live local socket is sub-millisecond. This budget is not
49
+ * sized for the connect; it is sized so that a daemon which is wedged,
50
+ * half-dead or being replaced costs a person 50 ms and then gets out of the
51
+ * way. The whole call still sits inside the hook's own `HOOK_BUDGETS`
52
+ * deadline, which was started before the connect.
53
+ */
54
+ export const MEMORY_DAEMON_CONNECT_TIMEOUT_MS = 50;
55
+ /** At most one spawn attempt per hour per machine. */
56
+ export const MEMORY_DAEMON_SPAWN_INTERVAL_MS = 3_600_000;
57
+ /** The daemon exits after this long with no request. Two hours. */
58
+ export const MEMORY_DAEMON_IDLE_EXIT_MS = 7_200_000;
59
+ /**
60
+ * How long a unix socket PATH may be, in bytes, before the platform refuses to
61
+ * bind it. `sun_path` is 104 on macOS/BSD and 108 on Linux; 100 is the safe
62
+ * floor under both. A real machine is nowhere near it — `/Users/<name>/.local/
63
+ * state/bli-cockpit/memory-daemon.sock` is about 60 characters — but a deeply
64
+ * nested HOME (a container, a temp directory in a test) is, and `bind` answers
65
+ * EINVAL, which reads as a bug rather than as a limit. The endpoint says so by
66
+ * name instead, and every hook simply goes the direct way.
67
+ */
68
+ export const MEMORY_DAEMON_UNIX_PATH_MAX = 100;
69
+ /**
70
+ * The largest single line either side will read. A prompt is capped at 1,000
71
+ * characters before it ever reaches the door, and a recall block is a few KB;
72
+ * 1 MB is far above both and bounds a peer that never sends a newline.
73
+ */
74
+ export const MEMORY_DAEMON_MAX_LINE_BYTES = 1_048_576;
75
+ /**
76
+ * The one place the daemon's address is computed. Both packages call this;
77
+ * nobody joins a socket path by hand.
78
+ */
79
+ export function memoryDaemonEndpoint(options) {
80
+ const paths = getUserLocalCockpitPaths(options.homeDir);
81
+ const platform = options.platform ?? process.platform;
82
+ const separator = paths.state_dir.includes("\\") ? "\\" : "/";
83
+ const stampFile = `${paths.state_dir}${separator}${MEMORY_DAEMON_SPAWN_STAMP_FILE_NAME}`;
84
+ if (platform === "win32") {
85
+ return {
86
+ kind: "pipe",
87
+ address: `${MEMORY_DAEMON_PIPE_PREFIX}${pipeUserSegment(options.username)}`,
88
+ stampFile,
89
+ stateDir: paths.state_dir,
90
+ // A named pipe is not a filesystem path and has no length problem.
91
+ addressTooLong: false,
92
+ };
93
+ }
94
+ const address = `${paths.state_dir}${separator}${MEMORY_DAEMON_SOCKET_FILE_NAME}`;
95
+ return {
96
+ kind: "unix",
97
+ address,
98
+ stampFile,
99
+ stateDir: paths.state_dir,
100
+ // Bytes, not characters, and no `Buffer`: this module is imported by the
101
+ // dashboard as well as by two CLIs.
102
+ addressTooLong: new TextEncoder().encode(address).length > MEMORY_DAEMON_UNIX_PATH_MAX,
103
+ };
104
+ }
105
+ /**
106
+ * A Windows user name folded into something a pipe name may hold.
107
+ *
108
+ * Real ones carry spaces, dots and a `DOMAIN\user` prefix, and a pipe name is
109
+ * a path segment — an unfiltered name would either fail to bind or, worse,
110
+ * bind somewhere unintended. Everything outside `[A-Za-z0-9_-]` becomes `-`,
111
+ * the whole thing is lower-cased so two spellings of one account do not get
112
+ * two daemons, and a name that reduces to nothing falls back to `user`, which
113
+ * is still per-user in practice because the pipe is created by that user's
114
+ * own session.
115
+ */
116
+ export function pipeUserSegment(username) {
117
+ const folded = (username ?? "")
118
+ .trim()
119
+ .toLowerCase()
120
+ .replace(/[^a-z0-9_-]+/gu, "-")
121
+ .replace(/^-+|-+$/gu, "")
122
+ .slice(0, 48);
123
+ return folded.length > 0 ? folded : "user";
124
+ }
@@ -45,6 +45,9 @@ export declare const MemoryHookCountsSchema: z.ZodObject<{
45
45
  timeouts: z.ZodNumber;
46
46
  failed: z.ZodNumber;
47
47
  skipped: z.ZodNumber;
48
+ via_daemon: z.ZodOptional<z.ZodNumber>;
49
+ via_direct: z.ZodOptional<z.ZodNumber>;
50
+ skipped_trivial: z.ZodOptional<z.ZodNumber>;
48
51
  }, z.core.$strict>;
49
52
  export type MemoryHookCounts = z.infer<typeof MemoryHookCountsSchema>;
50
53
  /** `YYYY-MM-DDTHH` in UTC — the bucket key, and the reason the window is exact. */
@@ -62,6 +65,9 @@ export declare const MemoryHookStatsFileSchema: z.ZodObject<{
62
65
  timeouts: z.ZodNumber;
63
66
  failed: z.ZodNumber;
64
67
  skipped: z.ZodNumber;
68
+ via_daemon: z.ZodOptional<z.ZodNumber>;
69
+ via_direct: z.ZodOptional<z.ZodNumber>;
70
+ skipped_trivial: z.ZodOptional<z.ZodNumber>;
65
71
  }, z.core.$strict>>>;
66
72
  }, z.core.$strict>;
67
73
  export type MemoryHookStatsFile = z.infer<typeof MemoryHookStatsFileSchema>;
@@ -74,6 +80,18 @@ export interface MemoryHookWindow {
74
80
  printed: number;
75
81
  timeouts: number;
76
82
  failed: number;
83
+ /**
84
+ * BLI-3884. How many of those runs the resident daemon answered, and how
85
+ * many took the original direct path. Summed only over buckets that CARRY
86
+ * the field, so a window spanning an upgrade reports what was measured
87
+ * rather than crediting the old rows to `direct`.
88
+ */
89
+ viaDaemon: number;
90
+ viaDirect: number;
91
+ /** True when at least one bucket in the window named a route at all. */
92
+ viaMeasured: boolean;
93
+ /** Trivial prompts the hook declined to search for (BLI-3881). A subset of skips. */
94
+ skippedTrivial: number;
77
95
  /** Buckets that were inside the window and had something in them. */
78
96
  hours: number;
79
97
  }
@@ -46,6 +46,36 @@ export const MemoryHookCountsSchema = z
46
46
  timeouts: z.number().int().min(0),
47
47
  failed: z.number().int().min(0),
48
48
  skipped: z.number().int().min(0),
49
+ /**
50
+ * WHICH ROUTE ANSWERED (BLI-3884). `via_daemon` counts runs the resident
51
+ * local daemon answered; `via_direct` counts runs that went the original
52
+ * way — a fresh process, a cold connection, the whole gate.
53
+ *
54
+ * Both are OPTIONAL and both are absent on a machine older than the
55
+ * daemon, which must read as "not measured" and never as "no daemon
56
+ * ever answered". They do not have to sum to `runs`: a run that never
57
+ * reached either route (no payload, no session) has no route to name,
58
+ * and is counted in `skipped` alone.
59
+ */
60
+ via_daemon: z.number().int().min(0).optional(),
61
+ via_direct: z.number().int().min(0).optional(),
62
+ /**
63
+ * BLI-3881 — the SUBSET of `skipped` the hook declined to search for.
64
+ *
65
+ * "ok", "continue", "y", a slash command: a turn whose whole text carries
66
+ * no subject to recall against. Searching one spends a person's whole
67
+ * budget on a query that can only return noise, so the hook returns early
68
+ * and this counts how often — because a hook that stops doing something
69
+ * and says nothing is indistinguishable from a hook that broke.
70
+ *
71
+ * OPTIONAL, and it must stay optional: a file written by a memory-mcp
72
+ * older than this ticket has no such key, and `MemoryHookCountsSchema` is
73
+ * strict, so a required field would make every existing machine's history
74
+ * `hook_stats_unrecognised_shape` overnight. It is counted BESIDE
75
+ * `skipped`, never instead of it, so `runs = printed + empty + timeouts +
76
+ * failed + skipped` still holds.
77
+ */
78
+ skipped_trivial: z.number().int().min(0).optional(),
49
79
  })
50
80
  .strict();
51
81
  /** `YYYY-MM-DDTHH` in UTC — the bucket key, and the reason the window is exact. */
@@ -69,7 +99,7 @@ export function memoryHookHourBucket(at) {
69
99
  return at.toISOString().slice(0, 13);
70
100
  }
71
101
  export function emptyMemoryHookCounts() {
72
- return { runs: 0, printed: 0, empty: 0, timeouts: 0, failed: 0, skipped: 0 };
102
+ return { runs: 0, printed: 0, empty: 0, timeouts: 0, failed: 0, skipped: 0, skipped_trivial: 0 };
73
103
  }
74
104
  /**
75
105
  * Sum one event's buckets over the last `hours` hours, ending at `now`.
@@ -83,7 +113,17 @@ export function summariseMemoryHookWindow(file, event, options) {
83
113
  const hours = options.hours ?? 24;
84
114
  const earliest = memoryHookHourBucket(new Date(options.now.getTime() - (hours - 1) * 3_600_000));
85
115
  const latest = memoryHookHourBucket(options.now);
86
- const total = { runs: 0, printed: 0, timeouts: 0, failed: 0, hours: 0 };
116
+ const total = {
117
+ runs: 0,
118
+ printed: 0,
119
+ timeouts: 0,
120
+ failed: 0,
121
+ viaDaemon: 0,
122
+ viaDirect: 0,
123
+ viaMeasured: false,
124
+ skippedTrivial: 0,
125
+ hours: 0,
126
+ };
87
127
  for (const [bucket, events] of Object.entries(file.buckets)) {
88
128
  // Lexical order is chronological for this key, which is the only reason a
89
129
  // string comparison is allowed to stand in for a date one here.
@@ -96,6 +136,14 @@ export function summariseMemoryHookWindow(file, event, options) {
96
136
  total.printed += counts.printed;
97
137
  total.timeouts += counts.timeouts;
98
138
  total.failed += counts.failed;
139
+ if (counts.via_daemon !== undefined || counts.via_direct !== undefined) {
140
+ total.viaMeasured = true;
141
+ total.viaDaemon += counts.via_daemon ?? 0;
142
+ total.viaDirect += counts.via_direct ?? 0;
143
+ }
144
+ // Absent means an older writer never counted one, which sums as zero and
145
+ // is honest: that machine genuinely skipped none, because it could not.
146
+ total.skippedTrivial += counts.skipped_trivial ?? 0;
99
147
  total.hours += 1;
100
148
  }
101
149
  return total;
@@ -113,6 +113,9 @@ export declare const MemoryInstallReceiptSchema: z.ZodObject<{
113
113
  hook_timeouts_24h: z.ZodOptional<z.ZodNumber>;
114
114
  hook_printed_24h: z.ZodOptional<z.ZodNumber>;
115
115
  hook_failed_24h: z.ZodOptional<z.ZodNumber>;
116
+ hook_via_daemon_24h: z.ZodOptional<z.ZodNumber>;
117
+ hook_via_direct_24h: z.ZodOptional<z.ZodNumber>;
118
+ hook_skipped_trivial_24h: z.ZodOptional<z.ZodNumber>;
116
119
  hook_stats_reason: z.ZodOptional<z.ZodString>;
117
120
  hook_performance: z.ZodOptional<z.ZodObject<{
118
121
  schema_version: z.ZodLiteral<"memory-hook-performance.v1">;
@@ -132,6 +132,28 @@ export const MemoryInstallReceiptSchema = z
132
132
  hook_timeouts_24h: z.number().int().min(0).optional(),
133
133
  hook_printed_24h: z.number().int().min(0).optional(),
134
134
  hook_failed_24h: z.number().int().min(0).optional(),
135
+ /**
136
+ * WHICH ROUTE ANSWERED THE RECALL (BLI-3884).
137
+ *
138
+ * `hook_via_daemon_24h` counts prompt hooks the machine's resident local
139
+ * daemon answered from a warm connection; `hook_via_direct_24h` counts
140
+ * the ones that spawned, connected and proved the token themselves. Both
141
+ * optional, and BOTH ABSENT on a machine older than the daemon — which
142
+ * reads as "not measured", never as "the daemon answered nothing". They
143
+ * do not have to sum to `hook_runs_24h`: a run that never reached either
144
+ * route (no payload, no session) names no route at all.
145
+ */
146
+ hook_via_daemon_24h: z.number().int().min(0).optional(),
147
+ hook_via_direct_24h: z.number().int().min(0).optional(),
148
+ /**
149
+ * BLI-3881: runs the hook DECLINED to search for — "ok", "continue", a
150
+ * slash command. It is a subset of the skips inside `hook_runs_24h`, and
151
+ * it exists because a hook that quietly stops searching looks exactly like
152
+ * a hook that broke. Optional like the four above: absent means the
153
+ * machine is on a memory-mcp older than the rule, never that it skipped
154
+ * none.
155
+ */
156
+ hook_skipped_trivial_24h: z.number().int().min(0).optional(),
135
157
  /** Named when the counts are absent because the file could not be read. */
136
158
  hook_stats_reason: ReasonLabelSchema.optional(),
137
159
  /** Observed ordinary invocations, not legacy floors or a full host trace. */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bli-cockpit/telemetry-core",
3
- "version": "0.1.33",
3
+ "version": "0.1.35",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",