@axiom-lattice/protocols 4.11.0 → 6.0.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@axiom-lattice/protocols",
3
- "version": "4.11.0",
3
+ "version": "6.0.0",
4
4
  "description": "Unified protocol type definitions for Axiom Lattice framework",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Normalized human-in-the-loop interrupt exposed by the Agent Server surface.
3
+ */
4
+ export interface AgentInterrupt {
5
+ /** Stable id derived from the LangGraph interrupt id or task/index. */
6
+ interruptId: string;
7
+ /** Normalized interrupt kind. */
8
+ type: "clarify" | "task_review" | "custom";
9
+ /** Thread whose graph is interrupted. */
10
+ threadId: string;
11
+ /** Task id that owns the interrupt, when available. */
12
+ taskId?: string;
13
+ /** Raw or normalized interrupt payload. */
14
+ payload: unknown;
15
+ }
@@ -14,7 +14,6 @@ import { BaseLatticeProtocol } from "./BaseLatticeProtocol";
14
14
  export enum AgentType {
15
15
  REACT = "react",
16
16
  DEEP_AGENT = "deep_agent",
17
- TEAM = "team",
18
17
  PROCESSING = "processing",
19
18
  /** Remote A2A agent — delegates to an external A2A-compatible server */
20
19
  A2A_REMOTE = "a2a_remote",
@@ -203,52 +202,6 @@ export interface ProcessingAgentConfig extends BaseAgentConfig {
203
202
  internalSubAgents?: AgentConfig[];
204
203
  }
205
204
 
206
- /**
207
- * Team teammate configuration -- describes an available teammate.
208
- */
209
- export interface TeamTeammateConfig {
210
- /** Unique name for this teammate (used as agent ID) */
211
- name: string;
212
- /** Role category (e.g. "research", "writing", "review") */
213
- role: string;
214
- /** Human-readable description of what this teammate does */
215
- description: string;
216
- /** Tool keys this teammate has access to */
217
- tools?: string[];
218
- /** Custom system prompt for this teammate */
219
- prompt?: string;
220
- /** Model key override for this teammate */
221
- modelKey?: string;
222
- }
223
-
224
- /**
225
- * TEAM agent configuration -- a team lead that dynamically creates teammates.
226
- * Teammates are created on-the-fly from create_team tool input (name, role, description).
227
- */
228
- export interface TeamAgentConfig extends BaseAgentConfig {
229
- type: AgentType.TEAM;
230
- /** Tool keys available to the team lead */
231
- tools?: string[];
232
- /** Maximum number of teammates running concurrently */
233
- maxConcurrency?: number;
234
- /**
235
- * Schedule lattice key for polling task list / mailbox.
236
- * When set, teammates use ScheduleLattice for periodic polling instead of event-driven wait.
237
- */
238
- scheduleLatticeKey?: string;
239
- /** Poll interval in ms when using schedule lattice (default: 5000) */
240
- pollIntervalMs?: number;
241
- }
242
-
243
- /**
244
- * Type guard to check if config is TeamAgentConfig
245
- */
246
- export function isTeamAgentConfig(
247
- config: AgentConfig
248
- ): config is TeamAgentConfig {
249
- return config.type === AgentType.TEAM;
250
- }
251
-
252
205
  // ─── A2A_REMOTE Agent ──────────────────────────────────────────────────────
253
206
 
254
207
  export interface LocalRuntimeConfig {
@@ -361,7 +314,6 @@ export function isWorkflowAgentConfig(
361
314
  export type AgentConfig =
362
315
  | ReactAgentConfig
363
316
  | DeepAgentConfig
364
- | TeamAgentConfig
365
317
  | ProcessingAgentConfig
366
318
  | A2ARemoteAgentConfig
367
319
  | WorkflowAgentConfig
@@ -372,7 +324,6 @@ export type AgentConfig =
372
324
  export type AgentConfigWithTools =
373
325
  | ReactAgentConfig
374
326
  | DeepAgentConfig
375
- | TeamAgentConfig
376
327
  | ProcessingAgentConfig
377
328
  | A2ARemoteAgentConfig
378
329
  | WorkflowAgentConfig
@@ -104,6 +104,8 @@ export type AgentWebAppErrorCode =
104
104
  | "THREAD_NOT_FOUND"
105
105
  | "STREAM_CONFLICT"
106
106
  | "STREAM_FAILED"
107
+ | "RUN_CANCEL_UNAVAILABLE"
108
+ | "RUN_CANCEL_STORE_UNAVAILABLE"
107
109
  | "INVALID_REQUEST"
108
110
  | "INTERNAL_ERROR";
109
111
 
@@ -87,6 +87,16 @@ export type MessageMiddleware = (
87
87
  export interface ChannelAdapter<TConfig = unknown> {
88
88
  readonly channel: string;
89
89
  readonly configSchema: z.ZodSchema<TConfig>;
90
+ /**
91
+ * Whether `sendReply` is safe to call from any Gateway replica.
92
+ *
93
+ * @remarks
94
+ * Only adapters whose reply transport is stateless/HTTP (for example Lark)
95
+ * may set this. Connection-bound adapters (WeCom/WeChat/Room) leave it
96
+ * undefined: their replies depend on process-local sockets or context tokens,
97
+ * so the durable cross-replica reply path must not claim their jobs.
98
+ */
99
+ readonly durableReply?: boolean;
90
100
  receive(
91
101
  rawPayload: unknown,
92
102
  installation: ChannelInstallation,
@@ -0,0 +1,184 @@
1
+ /** Lifecycle of one durable channel reply delivery attempt. */
2
+ export type ChannelReplyJobStatus = "pending" | "delivered" | "failed";
3
+
4
+ /**
5
+ * A durable, cross-replica channel reply job.
6
+ *
7
+ * @remarks
8
+ * The row is keyed by `runId` but always read/written inside a tenant scope.
9
+ * Claiming is a lease-based CAS: only a pending, due row with no live lease can
10
+ * be claimed, and terminal writes must present the current lease owner.
11
+ */
12
+ export interface ChannelReplyJob {
13
+ runId: string;
14
+ tenantId: string;
15
+ assistantId: string;
16
+ threadId: string;
17
+ adapterChannel: string;
18
+ channelInstallationId: string;
19
+ replyTarget: Record<string, unknown>;
20
+ inputMessageId: string;
21
+ /**
22
+ * Legacy chunk-buffer cursor captured before the run was created.
23
+ *
24
+ * @remarks Retained for schema compatibility. The durable dispatcher now
25
+ * reads final text from the thread checkpoint and does not use this field.
26
+ */
27
+ streamCursor?: string | null;
28
+ status: ChannelReplyJobStatus;
29
+ attempts: number;
30
+ /** Epoch milliseconds. */
31
+ availableAt: number;
32
+ leaseOwner: string | null;
33
+ /** Epoch milliseconds. */
34
+ leaseExpiresAt: number | null;
35
+ lastError: string | null;
36
+ outputMessageId: string | null;
37
+ /** Epoch milliseconds. */
38
+ createdAt: number;
39
+ /** Epoch milliseconds. */
40
+ updatedAt: number;
41
+ /** Epoch milliseconds. */
42
+ deliveredAt: number | null;
43
+ }
44
+
45
+ /** Fields accepted when enqueuing a reply job. Status/lease/output fields are assigned by the store. */
46
+ export interface CreateChannelReplyJobInput {
47
+ runId: string;
48
+ tenantId: string;
49
+ assistantId: string;
50
+ threadId: string;
51
+ adapterChannel: string;
52
+ channelInstallationId: string;
53
+ replyTarget: Record<string, unknown>;
54
+ inputMessageId: string;
55
+ /**
56
+ * Legacy chunk-buffer cursor captured before the run was created.
57
+ *
58
+ * @remarks Retained for schema compatibility. The durable dispatcher now
59
+ * reads final text from the thread checkpoint and does not use this field.
60
+ */
61
+ streamCursor?: string | null;
62
+ /** Earliest epoch-ms time the job may be claimed. */
63
+ availableAt: number;
64
+ }
65
+
66
+ /** Persistence boundary for durable channel reply jobs. */
67
+ export interface ChannelReplyJobStore {
68
+ /** Creates a pending job. */
69
+ create(input: CreateChannelReplyJobInput): Promise<ChannelReplyJob>;
70
+ /** Reads one job within a tenant scope. */
71
+ get(tenantId: string, runId: string): Promise<ChannelReplyJob | null>;
72
+ /** Lists claimable pending jobs that are due at `now`, oldest first. */
73
+ listPending(now: number, limit?: number): Promise<ChannelReplyJob[]>;
74
+ /**
75
+ * Atomically leases a pending, due job that is unleased or has an expired
76
+ * lease. Returns the claimed job, or `null` when another owner won.
77
+ */
78
+ claim(
79
+ tenantId: string,
80
+ runId: string,
81
+ owner: string,
82
+ leaseMs: number,
83
+ now?: number,
84
+ ): Promise<ChannelReplyJob | null>;
85
+ /** Marks a job delivered when `owner` still holds the lease. */
86
+ markDelivered(
87
+ tenantId: string,
88
+ runId: string,
89
+ owner: string,
90
+ outputMessageId?: string | null,
91
+ now?: number,
92
+ ): Promise<ChannelReplyJob | null>;
93
+ /** Returns a job to pending with an incremented attempt count. */
94
+ reschedule(
95
+ tenantId: string,
96
+ runId: string,
97
+ owner: string,
98
+ error: string,
99
+ availableAt: number,
100
+ now?: number,
101
+ ): Promise<ChannelReplyJob | null>;
102
+ /** Marks a job terminally failed. */
103
+ markFailed(
104
+ tenantId: string,
105
+ runId: string,
106
+ owner: string,
107
+ error: string,
108
+ now?: number,
109
+ ): Promise<ChannelReplyJob | null>;
110
+ /** Deletes one job. Returns whether a row was removed. */
111
+ delete(tenantId: string, runId: string): Promise<boolean>;
112
+ /**
113
+ * Deletes up to `limit` terminal (`delivered`/`failed`) jobs whose last update
114
+ * is strictly older than `before`.
115
+ *
116
+ * @param before Epoch milliseconds retention cutoff.
117
+ * @param limit Maximum rows to prune in this batch (normalized 1..200).
118
+ * @returns Number of rows removed.
119
+ */
120
+ deleteTerminalBefore(before: number, limit?: number): Promise<number>;
121
+ }
122
+
123
+ /** Smallest accepted page size for {@link ChannelReplyJobStore.listPending}. */
124
+ export const CHANNEL_REPLY_JOB_LIST_LIMIT_MIN = 1;
125
+ /** Largest accepted page size for {@link ChannelReplyJobStore.listPending}. */
126
+ export const CHANNEL_REPLY_JOB_LIST_LIMIT_MAX = 200;
127
+ /** Default page size for {@link ChannelReplyJobStore.listPending}. */
128
+ export const CHANNEL_REPLY_JOB_LIST_LIMIT_DEFAULT = 100;
129
+
130
+ /**
131
+ * Normalizes a caller-supplied page size into the shared 1..200 range.
132
+ *
133
+ * @remarks Zero and negative values clamp to 1; non-finite values fall back to
134
+ * the default. Stores must use this helper so InMemory and PostgreSQL agree.
135
+ */
136
+ export function normalizeChannelReplyJobListLimit(limit?: number): number {
137
+ if (limit === undefined || !Number.isFinite(limit)) {
138
+ return CHANNEL_REPLY_JOB_LIST_LIMIT_DEFAULT;
139
+ }
140
+ return Math.min(
141
+ CHANNEL_REPLY_JOB_LIST_LIMIT_MAX,
142
+ Math.max(CHANNEL_REPLY_JOB_LIST_LIMIT_MIN, Math.trunc(limit)),
143
+ );
144
+ }
145
+
146
+ /** Rejects missing/blank required fields and malformed create payloads at runtime. */
147
+ export function assertValidCreateChannelReplyJobInput(
148
+ input: CreateChannelReplyJobInput,
149
+ ): void {
150
+ if (input === null || typeof input !== "object") {
151
+ throw new Error("ChannelReplyJob create input is required");
152
+ }
153
+ const required: Array<[string, unknown]> = [
154
+ ["runId", input.runId],
155
+ ["tenantId", input.tenantId],
156
+ ["assistantId", input.assistantId],
157
+ ["threadId", input.threadId],
158
+ ["adapterChannel", input.adapterChannel],
159
+ ["channelInstallationId", input.channelInstallationId],
160
+ ["inputMessageId", input.inputMessageId],
161
+ ];
162
+ for (const [field, value] of required) {
163
+ if (typeof value !== "string" || value.trim() === "") {
164
+ throw new Error(`ChannelReplyJob requires a non-empty ${field}`);
165
+ }
166
+ }
167
+ if (
168
+ typeof input.replyTarget !== "object" ||
169
+ input.replyTarget === null ||
170
+ Array.isArray(input.replyTarget)
171
+ ) {
172
+ throw new Error("ChannelReplyJob requires a replyTarget object");
173
+ }
174
+ if (typeof input.availableAt !== "number" || !Number.isFinite(input.availableAt)) {
175
+ throw new Error("ChannelReplyJob requires a finite availableAt");
176
+ }
177
+ if (
178
+ input.streamCursor !== undefined &&
179
+ input.streamCursor !== null &&
180
+ typeof input.streamCursor !== "string"
181
+ ) {
182
+ throw new Error("ChannelReplyJob streamCursor must be a string");
183
+ }
184
+ }
@@ -0,0 +1,98 @@
1
+ /**
2
+ * Checkpoint storage usage for a single thread.
3
+ *
4
+ * This is a read-only accounting view over the checkpointer's persisted rows.
5
+ */
6
+ export interface ThreadCheckpointStorage {
7
+ /** Thread whose checkpoint rows were measured. */
8
+ threadId: string;
9
+ /** Sum of checkpoint, channel-value, and pending-write bytes. */
10
+ totalBytes: number;
11
+ /** Persisted checkpoint rows (`checkpoints` table). */
12
+ checkpoints: { count: number; bytes: number };
13
+ /** Persisted channel values (`checkpoint_blobs` table). */
14
+ channelValues: { count: number; bytes: number };
15
+ /** Persisted pending writes (`checkpoint_writes` table). */
16
+ pendingWrites: { count: number; bytes: number };
17
+ }
18
+
19
+ /**
20
+ * Read-only checkpoint storage accounting.
21
+ *
22
+ * Implementations must not create, copy, prune, or mutate checkpoint rows.
23
+ */
24
+ export interface CheckpointStorage {
25
+ /**
26
+ * Measure the checkpoint storage used by one thread.
27
+ *
28
+ * @param threadId - Thread id whose checkpoint rows should be measured.
29
+ * @returns Counts and byte sizes grouped by checkpoint storage category.
30
+ */
31
+ get(threadId: string): Promise<ThreadCheckpointStorage>;
32
+ }
33
+
34
+ /**
35
+ * Checkpoint maintenance operations over persisted thread checkpoints.
36
+ *
37
+ * Implementations copy, delete, and prune raw checkpoint rows for a thread.
38
+ * Callers must verify thread ownership and refuse to run while the thread is
39
+ * active.
40
+ */
41
+ export interface CheckpointMaintenance {
42
+ /**
43
+ * Copy every checkpoint row from a source thread to an empty target thread.
44
+ *
45
+ * @param sourceThreadId - Thread whose rows are copied.
46
+ * @param targetThreadId - Empty thread that receives the rows.
47
+ * @returns Resolves when the copy commits; an empty source is a no-op.
48
+ * @throws When the target already has checkpoint rows.
49
+ */
50
+ copyThread(sourceThreadId: string, targetThreadId: string): Promise<void>;
51
+
52
+ /**
53
+ * Delete every checkpoint row for a thread.
54
+ *
55
+ * @param threadId - Thread whose checkpoint rows are deleted.
56
+ * @returns Resolves when the deletion commits.
57
+ */
58
+ deleteThread(threadId: string): Promise<void>;
59
+
60
+ /**
61
+ * Delete the checkpoints that descend from a base checkpoint.
62
+ *
63
+ * Preserves the base checkpoint and every checkpoint that is not descended
64
+ * from it. Descendants are discovered by following both same-namespace
65
+ * `parent_checkpoint_id` edges and cross-namespace `metadata.parents` edges
66
+ * (the `{ "<parent checkpoint_ns>": "<parent checkpoint id>" }` map that
67
+ * LangGraph writes for subgraph checkpoints), recursively across all
68
+ * namespaces. Checkpoint-id ordering is not used. LangGraph keys the first
69
+ * superstep's writes of a resumed execution to the parent/base checkpoint, so
70
+ * writes keyed to the base itself belong to the rolled-back run and are
71
+ * deleted with descendant writes. Channel blobs are removed only when no
72
+ * retained checkpoint references them.
73
+ *
74
+ * @param threadId - Thread whose later checkpoint history is deleted.
75
+ * @param baseCheckpointId - Checkpoint to retain as the new tip, or `null` to
76
+ * delete every checkpoint for the thread. An empty string is invalid and must
77
+ * never be treated as the `null` wildcard.
78
+ * @returns Resolves when the deletion commits; no descendants is a no-op.
79
+ * @throws When the base checkpoint does not exist on the thread. Callers must
80
+ * fail closed rather than deleting an unknown range.
81
+ * @remarks The operation is transactional and idempotent so rollback recovery
82
+ * can retry it.
83
+ */
84
+ deleteAfter(threadId: string, baseCheckpointId: string | null): Promise<void>;
85
+
86
+ /**
87
+ * Retain only the latest N checkpoints per checkpoint namespace.
88
+ *
89
+ * @param threadId - Thread whose checkpoint history is pruned.
90
+ * @param options - Retention options.
91
+ * @param options.keepLatest - Number of latest checkpoints to retain per namespace.
92
+ * @returns Counts of deleted checkpoint, blob, and write rows.
93
+ */
94
+ pruneThread(
95
+ threadId: string,
96
+ options: { keepLatest: number },
97
+ ): Promise<{ checkpoints: number; blobs: number; writes: number }>;
98
+ }
@@ -104,6 +104,8 @@ export interface EvalRun {
104
104
  interruptedCases?: number;
105
105
  avgScore: number;
106
106
  error?: string;
107
+ /** A client requested cancellation; the executing worker polls this flag. */
108
+ cancelRequested?: boolean;
107
109
  /**
108
110
  * When true, this run executed ONLY the hold-out validation suite.
109
111
  * Per-case results of holdout runs are withheld from agents (aggregate
@@ -179,6 +181,8 @@ export interface EvalStore {
179
181
  getRunById(tenantId: string, id: string): Promise<EvalRun | null>;
180
182
  createRun(tenantId: string, projectId: string, id: string, data: CreateEvalRunRequest): Promise<EvalRun>;
181
183
  updateRunStatus(tenantId: string, id: string, updates: { status?: EvalRun['status']; passedCases?: number; failedCases?: number; interruptedCases?: number; avgScore?: number; error?: string; completedAt?: Date }): Promise<EvalRun | null>;
184
+ /** Request cooperative cancellation for a running evaluation. */
185
+ requestCancel?(tenantId: string, id: string): Promise<boolean>;
182
186
  deleteRun(tenantId: string, id: string): Promise<boolean>;
183
187
 
184
188
  getResultsByRun(tenantId: string, runId: string): Promise<EvalRunResult[]>;
@@ -32,7 +32,15 @@ export type McpUiRef =
32
32
  */
33
33
  structuredContent?: unknown;
34
34
  }
35
- | { kind: "plugin"; pluginType: string; resource: string; displayMode: McpUiDisplayMode; title?: string };
35
+ | {
36
+ kind: "plugin";
37
+ pluginType: string;
38
+ resource: string;
39
+ displayMode: McpUiDisplayMode;
40
+ title?: string;
41
+ /** Opaque HMAC-signed run/connection scope (1..4096 chars); never contains secrets. */
42
+ scope?: string;
43
+ };
36
44
 
37
45
  /** Resolved UI resource body returned by the gateway. */
38
46
  export interface McpUiResourcePayload {
@@ -45,6 +53,7 @@ export interface McpUiResourcePayload {
45
53
  }
46
54
 
47
55
  const MODES: McpUiDisplayMode[] = ["inline", "fullscreen", "pip"];
56
+ const MAX_MCP_UI_SCOPE_LENGTH = 4096;
48
57
  const isObj = (v: unknown): v is Record<string, unknown> => typeof v === "object" && v !== null;
49
58
 
50
59
  /** Type guard for a parsed `McpUiRef`. */
@@ -70,7 +79,18 @@ export function parseMcpUiRef(value: unknown): McpUiRef | null {
70
79
  };
71
80
  }
72
81
  if (value.kind === "plugin" && typeof value.pluginType === "string" && typeof value.resource === "string") {
73
- return { kind: "plugin", pluginType: value.pluginType, resource: value.resource, displayMode, ...(title ? { title } : {}) };
82
+ const scope =
83
+ typeof value.scope === "string" && value.scope.length > 0 && value.scope.length <= MAX_MCP_UI_SCOPE_LENGTH
84
+ ? value.scope
85
+ : undefined;
86
+ return {
87
+ kind: "plugin",
88
+ pluginType: value.pluginType,
89
+ resource: value.resource,
90
+ displayMode,
91
+ ...(title ? { title } : {}),
92
+ ...(scope ? { scope } : {}),
93
+ };
74
94
  }
75
95
  return null;
76
96
  }
@@ -54,6 +54,8 @@ export type McpAppMenuConfig =
54
54
  resource: string;
55
55
  displayMode?: McpUiDisplayMode;
56
56
  title?: string;
57
+ /** Signed scope copied from the ref so a pinned App can keep calling. */
58
+ scope?: string;
57
59
  toolInput?: Record<string, unknown>;
58
60
  toolResult?: unknown;
59
61
  ui?: ToolCallUi;
@@ -114,6 +114,14 @@ export type MessageChunkType = typeof MessageChunkTypes[keyof typeof MessageChun
114
114
 
115
115
  export interface MessageChunk {
116
116
  type: MessageChunkType;
117
+ /**
118
+ * Shared append-log cursor for this chunk.
119
+ *
120
+ * Set by streaming transports when the chunk is read from a cursor-addressable
121
+ * log (for example Redis Streams) so reconnects can resume via SSE
122
+ * `Last-Event-ID`. Omitted by in-memory buffers configured without a log.
123
+ */
124
+ cursor?: string;
117
125
  data: {
118
126
  id: string;
119
127
  content?: string;
@@ -0,0 +1,167 @@
1
+ /**
2
+ * Time range accepted by an observability dashboard.
3
+ */
4
+ export type ObservabilityDashboardRange = "1h" | "24h" | "7d" | "30d";
5
+
6
+ /**
7
+ * Input passed by the gateway to the selected observability provider.
8
+ */
9
+ export interface ObservabilityDashboardInput {
10
+ /** Time window to aggregate. */
11
+ range: ObservabilityDashboardRange;
12
+ /** Tenant resolved from the authenticated request, never from the client. */
13
+ tenantId: string;
14
+ }
15
+
16
+ /**
17
+ * Provider-owned dashboard result.
18
+ *
19
+ * The `payload` shape belongs to the provider; consumers must check `version`
20
+ * before rendering it.
21
+ */
22
+ export interface ObservabilityDashboardResult {
23
+ /** Provider payload version. */
24
+ version: number;
25
+ /** Section keys that could not be loaded. */
26
+ warnings?: string[];
27
+ /** Provider-specific dashboard data. */
28
+ payload: unknown;
29
+ }
30
+
31
+ /**
32
+ * Cross-provider response envelope returned by the gateway.
33
+ */
34
+ export interface ObservabilityDashboardEnvelope {
35
+ /** Provider type, also used as the UI element key suffix. */
36
+ provider: string;
37
+ /** Requested time window. */
38
+ range: ObservabilityDashboardRange;
39
+ /** Server generation timestamp. */
40
+ generatedAt: string;
41
+ /** Provider payload version. */
42
+ version: number;
43
+ /** Section keys that could not be loaded. */
44
+ warnings?: string[];
45
+ /** Provider-specific dashboard data. */
46
+ payload: unknown;
47
+ }
48
+
49
+ /** Prefix used to register a dashboard renderer in the GenUI element registry. */
50
+ export const OBSERVABILITY_DASHBOARD_ELEMENT_KEY_PREFIX =
51
+ "observability_dashboard_";
52
+
53
+ /**
54
+ * Returns the GenUI element key for a provider dashboard.
55
+ *
56
+ * @param provider - Provider type such as `"langfuse"`.
57
+ * @returns The registry key used by both the provider renderer and the panel.
58
+ */
59
+ export function observabilityDashboardElementKey(provider: string): string {
60
+ return `${OBSERVABILITY_DASHBOARD_ELEMENT_KEY_PREFIX}${provider}`;
61
+ }
62
+
63
+ /** Version of the built-in Langfuse dashboard payload. */
64
+ export const LANGFUSE_OBSERVABILITY_DASHBOARD_VERSION = 3;
65
+
66
+ /** One point in a Langfuse dashboard time series. */
67
+ export interface LangfuseObservabilitySeriesPoint {
68
+ /** ISO timestamp of the bucket start. */
69
+ bucket: string;
70
+ /** Aggregated value, or `null` when the bucket has no value. */
71
+ value: number | null;
72
+ }
73
+
74
+ /** Agent breakdown row, derived from the `assistant:<id>` trace tag. */
75
+ export interface LangfuseAgentBreakdownRow {
76
+ /** Assistant id from the trace tag. */
77
+ assistantId: string;
78
+ /** Display name from the trace name, when Langfuse captured one. */
79
+ name?: string;
80
+ /** Root-observation count, i.e. invocations started by this agent. */
81
+ calls: number;
82
+ /** All observations emitted by this agent. */
83
+ observations: number;
84
+ /** Total cost in USD. */
85
+ costUsd: number;
86
+ /** Input tokens consumed. */
87
+ inputTokens: number;
88
+ /** Output tokens produced. */
89
+ outputTokens: number;
90
+ /** Error-level observation count. */
91
+ errorCount: number;
92
+ /** Error ratio in `[0, 1]` over observations. */
93
+ errorRate: number;
94
+ }
95
+
96
+ /** One model's cost over time. */
97
+ export interface LangfuseModelCostSeries {
98
+ /** Model name. */
99
+ model: string;
100
+ /** Cost points for this model. */
101
+ points: LangfuseObservabilitySeriesPoint[];
102
+ }
103
+
104
+ /** Model breakdown row. */
105
+ export interface LangfuseModelBreakdownRow {
106
+ /** Model name reported by Langfuse. */
107
+ model: string;
108
+ /** Observation count. */
109
+ count: number;
110
+ /** Total cost in USD. */
111
+ costUsd: number;
112
+ /** Input tokens consumed. */
113
+ inputTokens: number;
114
+ /** Output tokens produced. */
115
+ outputTokens: number;
116
+ /** p95 generation latency in milliseconds, or `null` when unknown. */
117
+ p95LatencyMs: number | null;
118
+ }
119
+
120
+ /** Tool breakdown row. */
121
+ export interface LangfuseToolBreakdownRow {
122
+ /** Tool observation name. */
123
+ tool: string;
124
+ /** Observation count. */
125
+ count: number;
126
+ }
127
+
128
+ /**
129
+ * Built-in Langfuse dashboard payload.
130
+ *
131
+ * Aggregated for one tenant and time range; it never contains prompts,
132
+ * completions, user text, or credentials.
133
+ */
134
+ export interface LangfuseObservabilityDashboardPayload {
135
+ /** Provider sample rate; values below 1 mean the numbers are sampled. */
136
+ samplingRate: number;
137
+ /** Tracing environment used for the query, when configured. */
138
+ environment?: string;
139
+ kpis: {
140
+ traces: number;
141
+ costUsd: number;
142
+ inputTokens: number;
143
+ outputTokens: number;
144
+ /** Error observation ratio in `[0, 1]`. */
145
+ errorRate: number;
146
+ };
147
+ series: {
148
+ traces: LangfuseObservabilitySeriesPoint[];
149
+ cost: LangfuseObservabilitySeriesPoint[];
150
+ tokens: {
151
+ input: LangfuseObservabilitySeriesPoint[];
152
+ output: LangfuseObservabilitySeriesPoint[];
153
+ };
154
+ latency: {
155
+ p50: LangfuseObservabilitySeriesPoint[];
156
+ p95: LangfuseObservabilitySeriesPoint[];
157
+ p99: LangfuseObservabilitySeriesPoint[];
158
+ };
159
+ errorRate: LangfuseObservabilitySeriesPoint[];
160
+ toolCalls: LangfuseObservabilitySeriesPoint[];
161
+ /** Per-model cost trend. Optional so older payloads keep working. */
162
+ costByModel?: LangfuseModelCostSeries[];
163
+ };
164
+ agents: LangfuseAgentBreakdownRow[];
165
+ models: LangfuseModelBreakdownRow[];
166
+ tools: LangfuseToolBreakdownRow[];
167
+ }