@axiom-lattice/protocols 4.11.0 → 5.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.
@@ -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
  }
@@ -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,7 @@ 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";