@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.
@@ -0,0 +1,76 @@
1
+ import { z } from "zod";
2
+
3
+ /**
4
+ * Upper bound for every contract string. These payloads travel between
5
+ * replicas and are persisted with A2A tasks, so unbounded input is rejected
6
+ * at the shared boundary instead of relying on transport limits.
7
+ */
8
+ const MAX_FIELD_LENGTH = 64 * 1024;
9
+
10
+ const boundedString = z.string().min(1).max(MAX_FIELD_LENGTH);
11
+
12
+ /** Terminal cancellation commands delivered to the replica that currently owns a run. */
13
+ const RUN_CANCEL_REASONS = ["abort", "a2a.cancel", "shutdown"] as const;
14
+
15
+ /**
16
+ * A cancellation signal routed to the current owner of a run.
17
+ *
18
+ * `runId` is mandatory: a signal without it cannot unambiguously identify the
19
+ * execution to stop, so it is rejected rather than applied to the thread.
20
+ */
21
+ export interface RunCancelSignal {
22
+ commandId: string;
23
+ tenantId: string;
24
+ assistantId: string;
25
+ threadId: string;
26
+ runId: string;
27
+ reason: "abort" | "a2a.cancel" | "shutdown";
28
+ createdAt: string;
29
+ }
30
+
31
+ /**
32
+ * Identity of a human-in-the-loop interrupt answer, used for distributed
33
+ * dedupe so multiple replicas settle the same answer at most once.
34
+ */
35
+ export interface InterruptAnswerKey {
36
+ tenantId: string;
37
+ assistantId: string;
38
+ threadId: string;
39
+ interruptId: string;
40
+ }
41
+
42
+ /**
43
+ * Durable A2A event delivery cursor stored with the shared A2A task.
44
+ * `lastEventId` is the last event already handed to the client.
45
+ */
46
+ export interface A2AEventCursor {
47
+ taskId: string;
48
+ lastEventId: string;
49
+ updatedAt: string;
50
+ }
51
+
52
+ /** Strict validator for {@link RunCancelSignal}. */
53
+ export const runCancelSignalSchema: z.ZodType<RunCancelSignal> = z.object({
54
+ commandId: boundedString,
55
+ tenantId: boundedString,
56
+ assistantId: boundedString,
57
+ threadId: boundedString,
58
+ runId: boundedString,
59
+ reason: z.enum(RUN_CANCEL_REASONS),
60
+ createdAt: boundedString,
61
+ }).strict();
62
+
63
+ /** Strict validator for {@link InterruptAnswerKey}. */
64
+ export const interruptAnswerKeySchema: z.ZodType<InterruptAnswerKey> = z.object({
65
+ tenantId: boundedString,
66
+ assistantId: boundedString,
67
+ threadId: boundedString,
68
+ interruptId: boundedString,
69
+ }).strict();
70
+
71
+ /** Strict validator for {@link A2AEventCursor}. */
72
+ export const a2aEventCursorSchema: z.ZodType<A2AEventCursor> = z.object({
73
+ taskId: boundedString,
74
+ lastEventId: boundedString,
75
+ updatedAt: boundedString,
76
+ }).strict();
@@ -361,6 +361,12 @@ export interface ScheduleClient {
361
361
  */
362
362
  restore(): Promise<number>;
363
363
 
364
+ /**
365
+ * Reconcile this replica's timers with the shared store. Implementations
366
+ * that support multi-replica scheduling should expose this.
367
+ */
368
+ reconcile?(): Promise<number>;
369
+
364
370
  // ===== Storage =====
365
371
 
366
372
  /**
@@ -414,4 +420,5 @@ export interface ScheduleLatticeProtocol
414
420
 
415
421
  // Recovery
416
422
  restore: () => Promise<number>;
423
+ reconcile?: () => Promise<number>;
417
424
  }
@@ -0,0 +1,181 @@
1
+ /**
2
+ * A tenant-scoped namespaced key-value item.
3
+ */
4
+ export interface StoreItem {
5
+ /** Hierarchical namespace path. */
6
+ namespace: string[];
7
+ /** Item key, unique within `(tenantId, namespace)`. */
8
+ key: string;
9
+ /** Arbitrary JSON object stored under the key. */
10
+ value: Record<string, unknown>;
11
+ /** Creation timestamp. */
12
+ createdAt: Date;
13
+ /** Last write timestamp. */
14
+ updatedAt: Date;
15
+ /** Optional expiry timestamp; expired items are invisible. */
16
+ expiresAt?: Date;
17
+ }
18
+
19
+ /** Input for {@link StoreProtocol.put}. */
20
+ export interface StorePutInput {
21
+ /** Non-empty namespace path. */
22
+ namespace: string[];
23
+ /** Non-empty item key. */
24
+ key: string;
25
+ /** JSON object value. */
26
+ value: Record<string, unknown>;
27
+ /**
28
+ * Optional time-to-live in minutes.
29
+ *
30
+ * @remarks Positive finite numbers are accepted; stores compute `expiresAt`.
31
+ */
32
+ ttlMinutes?: number;
33
+ /**
34
+ * Optional index hint accepted for future indexing.
35
+ *
36
+ * @remarks No vector index is created in this slice.
37
+ */
38
+ index?: boolean | string[];
39
+ }
40
+
41
+ /** Input for {@link StoreProtocol.search}. */
42
+ export interface StoreSearchInput {
43
+ /** Only return items whose namespace starts with this path. */
44
+ namespacePrefix?: string[];
45
+ /** Deep partial-equality filter over `value`. */
46
+ filter?: Record<string, unknown>;
47
+ /** Case-insensitive substring over the serialized value. */
48
+ query?: string;
49
+ /** Maximum items to return. */
50
+ limit?: number;
51
+ /** Pagination offset. */
52
+ offset?: number;
53
+ }
54
+
55
+ /**
56
+ * Tenant-scoped namespaced key-value store with TTL and simple search.
57
+ *
58
+ * @remarks
59
+ * - Unique identity is `(tenantId, namespace, key)`.
60
+ * - Expired items are invisible to {@link get} and {@link search}.
61
+ * - `filter` is deep partial equality over `value`; `query` is a
62
+ * case-insensitive substring over the serialized value. Semantic/embedding
63
+ * search is deferred.
64
+ */
65
+ export interface StoreProtocol {
66
+ /** Create or replace an item. */
67
+ put(tenantId: string, input: StorePutInput): Promise<StoreItem>;
68
+ /** Read an unexpired item. */
69
+ get(tenantId: string, namespace: string[], key: string): Promise<StoreItem | null>;
70
+ /** Delete an item regardless of expiry. */
71
+ delete(tenantId: string, namespace: string[], key: string): Promise<boolean>;
72
+ /** Search unexpired items. */
73
+ search(tenantId: string, input: StoreSearchInput): Promise<StoreItem[]>;
74
+ /** List distinct namespace paths under a prefix. */
75
+ listNamespaces(tenantId: string, prefix?: string[], limit?: number): Promise<string[][]>;
76
+ /** Hard-delete expired items across all tenants. */
77
+ deleteExpired(now: Date, limit: number): Promise<number>;
78
+ }
79
+
80
+
81
+ /** Separator reserved for encoded namespace paths. */
82
+ export const STORE_IDENTITY_SEPARATOR = "\u001f";
83
+
84
+ /** Raised when a store namespace/key identity is malformed. */
85
+ export class StoreValidationError extends Error {
86
+ readonly code = "STORE_IDENTITY_INVALID" as const;
87
+
88
+ constructor(message: string) {
89
+ super(message);
90
+ this.name = "StoreValidationError";
91
+ }
92
+ }
93
+
94
+ function validStoreSegment(value: unknown): value is string {
95
+ return typeof value === "string"
96
+ && value.length > 0
97
+ && !value.includes(STORE_IDENTITY_SEPARATOR);
98
+ }
99
+
100
+ /**
101
+ * Assert a non-empty namespace array of non-empty segments without the
102
+ * reserved separator.
103
+ *
104
+ * @param namespace - Candidate namespace path.
105
+ * @throws {StoreValidationError} When the namespace is malformed.
106
+ */
107
+ export function assertStoreIdentity(namespace: unknown, key: unknown): asserts namespace is string[] {
108
+ if (!Array.isArray(namespace) || namespace.length === 0) {
109
+ throw new StoreValidationError("namespace must be a non-empty string array");
110
+ }
111
+ for (const segment of namespace) {
112
+ if (!validStoreSegment(segment)) {
113
+ throw new StoreValidationError(
114
+ "namespace segments must be non-empty strings without the reserved separator",
115
+ );
116
+ }
117
+ }
118
+ if (!validStoreSegment(key)) {
119
+ throw new StoreValidationError(
120
+ "key must be a non-empty string without the reserved separator",
121
+ );
122
+ }
123
+ }
124
+
125
+ /**
126
+ * Assert a namespace prefix (which may be empty for "all namespaces").
127
+ *
128
+ * @param prefix - Candidate prefix path.
129
+ * @throws {StoreValidationError} When any segment is malformed.
130
+ */
131
+ export function assertStoreNamespacePrefix(prefix: unknown): asserts prefix is string[] {
132
+ if (!Array.isArray(prefix)) {
133
+ throw new StoreValidationError("namespace prefix must be a string array");
134
+ }
135
+ for (const segment of prefix) {
136
+ if (!validStoreSegment(segment)) {
137
+ throw new StoreValidationError(
138
+ "namespace segments must be non-empty strings without the reserved separator",
139
+ );
140
+ }
141
+ }
142
+ }
143
+
144
+ /**
145
+ * Validate and compute an expiry date from `ttlMinutes`.
146
+ *
147
+ * @param ttlMinutes - Optional positive finite TTL in minutes.
148
+ * @param now - Base time (default current time).
149
+ * @returns The expiry date, or `undefined` when no TTL was supplied.
150
+ * @throws {StoreValidationError} When the TTL is invalid or out of range.
151
+ */
152
+ export function storeExpiresAt(
153
+ ttlMinutes: number | undefined,
154
+ now: Date = new Date(),
155
+ ): Date | undefined {
156
+ if (ttlMinutes === undefined) return undefined;
157
+ if (typeof ttlMinutes !== "number" || !Number.isFinite(ttlMinutes) || ttlMinutes <= 0) {
158
+ throw new StoreValidationError("ttlMinutes must be a positive number");
159
+ }
160
+ const expiresAt = new Date(now.getTime() + ttlMinutes * 60_000);
161
+ if (!Number.isFinite(expiresAt.getTime()) || expiresAt.getUTCFullYear() > 9999) {
162
+ throw new StoreValidationError("ttlMinutes is out of range");
163
+ }
164
+ return expiresAt;
165
+ }
166
+
167
+ /**
168
+ * Validate a non-negative safe-integer pagination value.
169
+ *
170
+ * @param value - Candidate limit/offset.
171
+ * @param fallback - Value used when omitted.
172
+ * @returns The validated number.
173
+ * @throws {StoreValidationError} When the value is not a non-negative safe integer.
174
+ */
175
+ export function storePaginationValue(value: number | undefined, fallback: number): number {
176
+ if (value === undefined) return fallback;
177
+ if (!Number.isSafeInteger(value) || value < 0) {
178
+ throw new StoreValidationError("limit/offset must be a non-negative safe integer");
179
+ }
180
+ return value;
181
+ }
@@ -379,6 +379,14 @@ export interface TaskListFilter {
379
379
  */
380
380
  priority?: string;
381
381
 
382
+ /**
383
+ * Filter by the isolated run thread linked from `context.thread_id`.
384
+ *
385
+ * @remarks Used to authorize read-only views of task execution threads that
386
+ * do not have a durable Thread row or AgentRun row.
387
+ */
388
+ threadId?: string;
389
+
382
390
  /**
383
391
  * Filter by workspace ID
384
392
  */
@@ -38,6 +38,13 @@ export interface Thread {
38
38
  * Thread last update timestamp
39
39
  */
40
40
  updatedAt: Date;
41
+
42
+ /**
43
+ * Optional expiry timestamp used by the thread TTL sweeper.
44
+ *
45
+ * @remarks Absent means the thread never expires.
46
+ */
47
+ expiresAt?: Date;
41
48
  }
42
49
 
43
50
  /**
@@ -48,6 +55,14 @@ export interface CreateThreadRequest {
48
55
  * Thread metadata
49
56
  */
50
57
  metadata?: Record<string, any>;
58
+
59
+ /**
60
+ * Optional thread expiry timestamp.
61
+ *
62
+ * @remarks Routes compute this from `ttlSeconds`/`expiresAt`; the store only
63
+ * persists it.
64
+ */
65
+ expiresAt?: Date;
51
66
  }
52
67
 
53
68
  /**
@@ -123,4 +138,13 @@ export interface ThreadStore {
123
138
  * @returns true if thread exists, false otherwise
124
139
  */
125
140
  hasThread(tenantId: string, threadId: string): Promise<boolean>;
141
+
142
+ /**
143
+ * List expired threads across all tenants.
144
+ *
145
+ * @param now Current time; threads with `expiresAt <= now` are returned.
146
+ * @param limit Maximum number of threads to return.
147
+ * @returns Expired threads ordered by `expiresAt` ascending.
148
+ */
149
+ listExpiredThreads(now: Date, limit: number): Promise<Thread[]>;
126
150
  }
@@ -41,3 +41,51 @@ describe("parseMcpUiRef", () => {
41
41
  expect(isMcpUiRef("x")).toBe(false);
42
42
  });
43
43
  });
44
+
45
+ describe("plugin scope", () => {
46
+ const plugin = {
47
+ kind: "plugin" as const,
48
+ pluginType: "p",
49
+ resource: "ui/x.html",
50
+ };
51
+
52
+ it("preserves a scope between 1 and 4096 characters", () => {
53
+ const scope = "a".repeat(64);
54
+ expect(parseMcpUiRef({ ...plugin, scope })).toEqual({
55
+ ...plugin,
56
+ displayMode: "inline",
57
+ scope,
58
+ });
59
+ });
60
+
61
+ it("preserves boundary scopes of 1 and 4096 characters", () => {
62
+ for (const scope of ["a", "b".repeat(4096)]) {
63
+ expect(parseMcpUiRef({ ...plugin, scope })).toEqual({
64
+ ...plugin,
65
+ displayMode: "inline",
66
+ scope,
67
+ });
68
+ }
69
+ });
70
+
71
+ it("drops empty, non-string, and over-long scopes", () => {
72
+ for (const scope of ["", 42, "x".repeat(4097)]) {
73
+ const ref = parseMcpUiRef({ ...plugin, scope });
74
+ expect(ref && "scope" in ref).toBe(false);
75
+ }
76
+ });
77
+
78
+ it("does not carry scope on an mcp ref", () => {
79
+ const ref = parseMcpUiRef({
80
+ kind: "mcp",
81
+ serverKey: "weather",
82
+ resourceUri: "ui://weather/dashboard",
83
+ scope: "signed-scope-token",
84
+ });
85
+ expect(ref && "scope" in ref).toBe(false);
86
+ });
87
+
88
+ it("keeps a legacy plugin ref unchanged", () => {
89
+ expect(parseMcpUiRef(plugin)).toEqual({ ...plugin, displayMode: "inline" });
90
+ });
91
+ });
@@ -15,10 +15,14 @@ describe("MenuProtocol mcpApp content type", () => {
15
15
  pluginType: "my-plugin",
16
16
  resource: "ui/report.html",
17
17
  title: "Report",
18
+ scope: "signed-scope-token",
18
19
  };
19
20
  for (const cfg of [mcp, plugin] as MenuContentConfig[]) {
20
21
  expect(JSON.parse(JSON.stringify(cfg))).toEqual(cfg);
21
22
  }
23
+ const parsed = JSON.parse(JSON.stringify(plugin)) as McpAppMenuConfig;
24
+ expect(parsed.kind).toBe("plugin");
25
+ expect(parsed.kind === "plugin" ? parsed.scope : undefined).toBe("signed-scope-token");
22
26
  });
23
27
 
24
28
  it("includes file and mcpApp in MenuContentType", () => {
@@ -0,0 +1,17 @@
1
+ import {
2
+ LANGFUSE_OBSERVABILITY_DASHBOARD_VERSION,
3
+ OBSERVABILITY_DASHBOARD_ELEMENT_KEY_PREFIX,
4
+ observabilityDashboardElementKey,
5
+ } from "../ObservabilityDashboardProtocol";
6
+
7
+ describe("observabilityDashboardElementKey", () => {
8
+ it("namespaces the provider key", () => {
9
+ expect(observabilityDashboardElementKey("langfuse")).toBe(
10
+ "observability_dashboard_langfuse",
11
+ );
12
+ expect(OBSERVABILITY_DASHBOARD_ELEMENT_KEY_PREFIX).toBe(
13
+ "observability_dashboard_",
14
+ );
15
+ expect(LANGFUSE_OBSERVABILITY_DASHBOARD_VERSION).toBe(3);
16
+ });
17
+ });
@@ -0,0 +1,148 @@
1
+ import {
2
+ a2aEventCursorSchema,
3
+ interruptAnswerKeySchema,
4
+ runCancelSignalSchema,
5
+ } from "../RunCancelProtocol";
6
+ import type {
7
+ A2AEventCursor,
8
+ InterruptAnswerKey,
9
+ RunCancelSignal,
10
+ } from "../RunCancelProtocol";
11
+ import {
12
+ a2aEventCursorSchema as a2aEventCursorSchemaFromIndex,
13
+ interruptAnswerKeySchema as interruptAnswerKeySchemaFromIndex,
14
+ runCancelSignalSchema as runCancelSignalSchemaFromIndex,
15
+ } from "../index";
16
+
17
+ const MAX_FIELD_LENGTH = 64 * 1024;
18
+
19
+ const validCancelSignal: RunCancelSignal = {
20
+ commandId: "command-1",
21
+ tenantId: "tenant-1",
22
+ assistantId: "assistant-1",
23
+ threadId: "thread-1",
24
+ runId: "run-1",
25
+ reason: "abort",
26
+ createdAt: "2026-10-20T00:00:00.000Z",
27
+ };
28
+
29
+ const validInterruptAnswerKey: InterruptAnswerKey = {
30
+ tenantId: "tenant-1",
31
+ assistantId: "assistant-1",
32
+ threadId: "thread-1",
33
+ interruptId: "interrupt-1",
34
+ };
35
+
36
+ const validA2AEventCursor: A2AEventCursor = {
37
+ taskId: "task-1",
38
+ lastEventId: "event-1",
39
+ updatedAt: "2026-10-20T00:00:00.000Z",
40
+ };
41
+
42
+ describe("runCancelSignalSchema", () => {
43
+ it("round-trips a valid cancellation signal", () => {
44
+ expect(runCancelSignalSchema.parse(validCancelSignal)).toEqual(validCancelSignal);
45
+ });
46
+
47
+ it("accepts every supported reason", () => {
48
+ for (const reason of ["abort", "a2a.cancel", "shutdown"] as const) {
49
+ expect(runCancelSignalSchema.parse({ ...validCancelSignal, reason }).reason).toBe(reason);
50
+ }
51
+ });
52
+
53
+ it.each(["commandId", "tenantId", "assistantId", "threadId", "runId", "createdAt"] as const)(
54
+ "rejects a signal missing %s",
55
+ (field) => {
56
+ const invalid: Record<string, unknown> = { ...validCancelSignal };
57
+ delete invalid[field];
58
+
59
+ expect(runCancelSignalSchema.safeParse(invalid).success).toBe(false);
60
+ },
61
+ );
62
+
63
+ it.each(["commandId", "tenantId", "threadId", "runId"] as const)(
64
+ "rejects an empty %s",
65
+ (field) => {
66
+ expect(runCancelSignalSchema.safeParse({ ...validCancelSignal, [field]: "" }).success).toBe(false);
67
+ },
68
+ );
69
+
70
+ it("rejects an unknown reason", () => {
71
+ expect(runCancelSignalSchema.safeParse({ ...validCancelSignal, reason: "restart" }).success).toBe(false);
72
+ });
73
+
74
+ it("rejects an oversized field", () => {
75
+ const oversized = "x".repeat(MAX_FIELD_LENGTH + 1);
76
+
77
+ expect(runCancelSignalSchema.safeParse({ ...validCancelSignal, runId: oversized }).success).toBe(false);
78
+ expect(runCancelSignalSchema.safeParse({ ...validCancelSignal, createdAt: oversized }).success).toBe(false);
79
+ });
80
+
81
+ it("rejects unknown keys", () => {
82
+ expect(runCancelSignalSchema.safeParse({ ...validCancelSignal, extra: true }).success).toBe(false);
83
+ });
84
+ });
85
+
86
+ describe("interruptAnswerKeySchema", () => {
87
+ it("round-trips a valid interrupt answer key", () => {
88
+ expect(interruptAnswerKeySchema.parse(validInterruptAnswerKey)).toEqual(validInterruptAnswerKey);
89
+ });
90
+
91
+ it.each(["tenantId", "assistantId", "threadId", "interruptId"] as const)(
92
+ "rejects a key missing %s",
93
+ (field) => {
94
+ const invalid: Record<string, unknown> = { ...validInterruptAnswerKey };
95
+ delete invalid[field];
96
+
97
+ expect(interruptAnswerKeySchema.safeParse(invalid).success).toBe(false);
98
+ },
99
+ );
100
+
101
+ it("rejects an oversized field", () => {
102
+ const oversized = "x".repeat(MAX_FIELD_LENGTH + 1);
103
+
104
+ expect(interruptAnswerKeySchema.safeParse({ ...validInterruptAnswerKey, interruptId: oversized }).success)
105
+ .toBe(false);
106
+ });
107
+
108
+ it("rejects unknown keys", () => {
109
+ expect(interruptAnswerKeySchema.safeParse({ ...validInterruptAnswerKey, runId: "run-1" }).success)
110
+ .toBe(false);
111
+ });
112
+ });
113
+
114
+ describe("a2aEventCursorSchema", () => {
115
+ it("round-trips a valid A2A event cursor", () => {
116
+ expect(a2aEventCursorSchema.parse(validA2AEventCursor)).toEqual(validA2AEventCursor);
117
+ });
118
+
119
+ it.each(["taskId", "lastEventId", "updatedAt"] as const)(
120
+ "rejects a cursor missing %s",
121
+ (field) => {
122
+ const invalid: Record<string, unknown> = { ...validA2AEventCursor };
123
+ delete invalid[field];
124
+
125
+ expect(a2aEventCursorSchema.safeParse(invalid).success).toBe(false);
126
+ },
127
+ );
128
+
129
+ it("rejects an oversized field", () => {
130
+ const oversized = "x".repeat(MAX_FIELD_LENGTH + 1);
131
+
132
+ expect(a2aEventCursorSchema.safeParse({ ...validA2AEventCursor, lastEventId: oversized }).success)
133
+ .toBe(false);
134
+ });
135
+
136
+ it("rejects unknown keys", () => {
137
+ expect(a2aEventCursorSchema.safeParse({ ...validA2AEventCursor, tenantId: "tenant-1" }).success)
138
+ .toBe(false);
139
+ });
140
+ });
141
+
142
+ describe("package exports", () => {
143
+ it("exposes the contracts from the package entry point", () => {
144
+ expect(runCancelSignalSchemaFromIndex).toBe(runCancelSignalSchema);
145
+ expect(interruptAnswerKeySchemaFromIndex).toBe(interruptAnswerKeySchema);
146
+ expect(a2aEventCursorSchemaFromIndex).toBe(a2aEventCursorSchema);
147
+ });
148
+ });
@@ -140,6 +140,8 @@ const errorCodes = [
140
140
  "THREAD_NOT_FOUND",
141
141
  "STREAM_CONFLICT",
142
142
  "STREAM_FAILED",
143
+ "RUN_CANCEL_UNAVAILABLE",
144
+ "RUN_CANCEL_STORE_UNAVAILABLE",
143
145
  "INVALID_REQUEST",
144
146
  "INTERNAL_ERROR",
145
147
  ] as const satisfies readonly AgentWebAppErrorCode[];
@@ -309,6 +311,8 @@ describe("Agent Web App protocol types", () => {
309
311
  "THREAD_NOT_FOUND",
310
312
  "STREAM_CONFLICT",
311
313
  "STREAM_FAILED",
314
+ "RUN_CANCEL_UNAVAILABLE",
315
+ "RUN_CANCEL_STORE_UNAVAILABLE",
312
316
  "INVALID_REQUEST",
313
317
  "INTERNAL_ERROR",
314
318
  ]);
package/src/index.ts CHANGED
@@ -17,6 +17,7 @@ export * from "./STTModelLatticeProtocol";
17
17
  export * from "./VectorStoreLatticeProtocol";
18
18
  export * from "./LoggerLatticeProtocol";
19
19
  export * from "./MessageProtocol";
20
+ export * from "./AgentInterruptProtocol";
20
21
  export * from "./ThreadStoreProtocol";
21
22
  export * from "./AssistantStoreProtocol";
22
23
  export * from "./SkillLatticeProtocol";
@@ -62,6 +63,9 @@ export * from "./UserPushSubscriptionStoreProtocol";
62
63
  export * from "./PluginDeploymentStoreProtocol";
63
64
  export * from "./LeaseStoreProtocol";
64
65
  export * from "./OutboxStoreProtocol";
66
+ export * from "./CheckpointStorageProtocol";
67
+ export * from "./StoreProtocol";
68
+ export * from "./ChannelReplyJobProtocol";
65
69
  export * from "./ExactDataSnapshot";
66
70
  export * from "./ProjectRoomRealtimeProtocol";
67
71
 
@@ -98,6 +102,8 @@ export * from "./SandboxPluginProtocol";
98
102
  export * from "./types";
99
103
  export * from "./TrustedRunContextProtocol";
100
104
  export * from "./RunScope";
105
+ export * from "./RunCancelProtocol";
101
106
  export * from "./AgentAffinity";
102
107
  export * from "./OpenProtocol";
103
108
  export * from "./McpUiProtocol";
109
+ export * from "./ObservabilityDashboardProtocol";