@axiom-lattice/protocols 4.1.4 → 4.2.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/.turbo/turbo-build.log +10 -10
- package/CHANGELOG.md +16 -0
- package/dist/index.d.mts +61 -6
- package/dist/index.d.ts +61 -6
- package/dist/index.js +9 -0
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +7 -0
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
- package/src/McpLatticeProtocol.ts +7 -1
- package/src/McpServerConfigStoreProtocol.ts +2 -1
- package/src/ProjectRoomMessageStoreProtocol.ts +8 -0
- package/src/ProjectRoomReadStateProtocol.ts +20 -0
- package/src/ProjectRoomRealtimeProtocol.ts +28 -3
- package/src/__tests__/ProjectRoomReadStateProtocol.test.ts +16 -0
- package/src/__tests__/ProjectRoomStores.test.ts +4 -0
- package/src/index.ts +1 -0
package/package.json
CHANGED
|
@@ -29,8 +29,14 @@ export interface McpServerConfig {
|
|
|
29
29
|
args?: string[];
|
|
30
30
|
/** URL for HTTP/SSE transport */
|
|
31
31
|
url?: string;
|
|
32
|
-
/** Environment variables */
|
|
32
|
+
/** Environment variables (stdio transport; credentials passed to the server process) */
|
|
33
33
|
env?: Record<string, string>;
|
|
34
|
+
/**
|
|
35
|
+
* Custom HTTP headers sent with every request (streamable_http / sse only).
|
|
36
|
+
* Commonly used for authentication, e.g. `{ Authorization: "Bearer <token>" }`.
|
|
37
|
+
* Ignored for stdio transport. Values are encrypted at rest by config stores.
|
|
38
|
+
*/
|
|
39
|
+
headers?: Record<string, string>;
|
|
34
40
|
/** Connection timeout in milliseconds */
|
|
35
41
|
timeout?: number;
|
|
36
42
|
/** Retry attempts on connection failure */
|
|
@@ -38,7 +38,8 @@ export interface McpServerConfigEntry {
|
|
|
38
38
|
selectedTools: string[];
|
|
39
39
|
|
|
40
40
|
/**
|
|
41
|
-
* Whether
|
|
41
|
+
* Whether secret values (env vars and headers) are encrypted at rest.
|
|
42
|
+
* Field name kept for backward compatibility; it covers config.headers too.
|
|
42
43
|
*/
|
|
43
44
|
isEnvEncrypted: boolean;
|
|
44
45
|
|
|
@@ -23,4 +23,12 @@ export interface ProjectRoomMessageStore {
|
|
|
23
23
|
|
|
24
24
|
/** Finds a room message by identifier within a tenant. */
|
|
25
25
|
findById(tenantId: string, id: string): Promise<ProjectRoomMessage | null>;
|
|
26
|
+
|
|
27
|
+
/** Counts messages created strictly after a horizon, optionally excluding one human author. */
|
|
28
|
+
countAfter(input: {
|
|
29
|
+
tenantId: string;
|
|
30
|
+
roomId: string;
|
|
31
|
+
after: Date;
|
|
32
|
+
excludeAuthorUserId?: string;
|
|
33
|
+
}): Promise<number>;
|
|
26
34
|
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/** A user's per-room read marker used to compute unread counts. */
|
|
2
|
+
export interface ProjectRoomReadState {
|
|
3
|
+
tenantId: string;
|
|
4
|
+
roomId: string;
|
|
5
|
+
userId: string;
|
|
6
|
+
/** Read horizon: messages created strictly after this instant are unread. */
|
|
7
|
+
lastReadAt: Date;
|
|
8
|
+
updatedAt: Date;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/** Persistence operations for per-user, per-room read markers. */
|
|
12
|
+
export interface ProjectRoomReadStateStore {
|
|
13
|
+
/** Returns the user's read marker for a room, or null when never reported. */
|
|
14
|
+
get(tenantId: string, roomId: string, userId: string): Promise<ProjectRoomReadState | null>;
|
|
15
|
+
/**
|
|
16
|
+
* Upserts the read marker monotonically: an earlier lastReadAt never moves
|
|
17
|
+
* the marker backwards. Returns the stored state after the write.
|
|
18
|
+
*/
|
|
19
|
+
markRead(input: { tenantId: string; roomId: string; userId: string; lastReadAt: Date }): Promise<ProjectRoomReadState>;
|
|
20
|
+
}
|
|
@@ -83,25 +83,41 @@ export type ProjectRoomTaskChangedEvent = ProjectRoomEventOf<
|
|
|
83
83
|
}
|
|
84
84
|
>;
|
|
85
85
|
|
|
86
|
+
/** A read-marker update broadcast on the acting user's channel. */
|
|
87
|
+
export type ProjectRoomReadChangedEvent = ProjectRoomEventOf<
|
|
88
|
+
"read.changed",
|
|
89
|
+
{ projectId: string; roomId: string; lastReadAt: string }
|
|
90
|
+
>;
|
|
91
|
+
|
|
92
|
+
/** A membership mutation that may change a user's room subscription set. */
|
|
93
|
+
export type ProjectRoomMembershipAffectedEvent = ProjectRoomEventOf<
|
|
94
|
+
"membership.affected",
|
|
95
|
+
{ change: "added" | "removed" | "role_changed"; projectId: string; roomId: string }
|
|
96
|
+
>;
|
|
97
|
+
|
|
86
98
|
/** All identified business events retained by the realtime broker. */
|
|
87
99
|
export type ProjectRoomBusinessEvent =
|
|
88
100
|
| ProjectRoomMessageCreatedEvent
|
|
89
101
|
| ProjectRoomRosterChangedEvent
|
|
90
102
|
| ProjectRoomMembershipChangedEvent
|
|
91
|
-
| ProjectRoomTaskChangedEvent
|
|
103
|
+
| ProjectRoomTaskChangedEvent
|
|
104
|
+
| ProjectRoomReadChangedEvent
|
|
105
|
+
| ProjectRoomMembershipAffectedEvent;
|
|
92
106
|
|
|
93
107
|
/** A business event before the broker assigns its process-local ID. */
|
|
94
108
|
export type ProjectRoomBusinessEventDraft =
|
|
95
109
|
| Omit<ProjectRoomMessageCreatedEvent, "id">
|
|
96
110
|
| Omit<ProjectRoomRosterChangedEvent, "id">
|
|
97
111
|
| Omit<ProjectRoomMembershipChangedEvent, "id">
|
|
98
|
-
| Omit<ProjectRoomTaskChangedEvent, "id"
|
|
112
|
+
| Omit<ProjectRoomTaskChangedEvent, "id">
|
|
113
|
+
| Omit<ProjectRoomReadChangedEvent, "id">
|
|
114
|
+
| Omit<ProjectRoomMembershipAffectedEvent, "id">;
|
|
99
115
|
|
|
100
116
|
/** A connection control event; control events are never replayed. */
|
|
101
117
|
export type ProjectRoomControlEvent =
|
|
102
118
|
| { type: "ready"; data: { epoch: string; headEventId: string | null } }
|
|
103
119
|
| { type: "resync"; data: { reason: "SERVER_RESTART" | "CURSOR_EXPIRED" | "SLOW_CONSUMER" } }
|
|
104
|
-
| { type: "access.revoked"; data: { reason: "PROJECT_ACCESS_REVOKED" | "TOKEN_EXPIRED" } };
|
|
120
|
+
| { type: "access.revoked"; data: { reason: "PROJECT_ACCESS_REVOKED" | "TOKEN_EXPIRED" | "CONNECTION_SUPERSEDED" } };
|
|
105
121
|
|
|
106
122
|
/** The authenticated identity used by Project Room realtime access checks. */
|
|
107
123
|
export interface ProjectRoomRealtimeActor {
|
|
@@ -194,6 +210,15 @@ export function isProjectRoomEventId(value: unknown): value is string {
|
|
|
194
210
|
return parseProjectRoomEventId(value) !== undefined;
|
|
195
211
|
}
|
|
196
212
|
|
|
213
|
+
/** projectId sentinel marking a per-user event channel inside the room broker. */
|
|
214
|
+
export const PROJECT_ROOM_USER_CHANNEL_PROJECT = "__user_channel__";
|
|
215
|
+
|
|
216
|
+
/** Builds the per-user broker scope used for membership and read broadcasts. */
|
|
217
|
+
export function projectRoomUserEventScope(tenantId: string, userId: string): ProjectRoomEventScope {
|
|
218
|
+
if (!tenantId || !userId) throw new TypeError("Project Room user scope requires non-empty identifiers");
|
|
219
|
+
return { tenantId, roomId: `__user__:${userId}`, projectId: PROJECT_ROOM_USER_CHANNEL_PROJECT };
|
|
220
|
+
}
|
|
221
|
+
|
|
197
222
|
function isoDate(value: unknown): string | undefined {
|
|
198
223
|
if (typeof value !== "object" || value === null) return undefined;
|
|
199
224
|
try {
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { projectRoomUserEventScope, parseProjectRoomEventId } from "../ProjectRoomRealtimeProtocol";
|
|
2
|
+
|
|
3
|
+
describe("projectRoomUserEventScope", () => {
|
|
4
|
+
it("builds a stable per-user scope that cannot collide with room scopes", () => {
|
|
5
|
+
expect(projectRoomUserEventScope("t1", "u1")).toEqual({
|
|
6
|
+
tenantId: "t1", roomId: "__user__:u1", projectId: "__user_channel__",
|
|
7
|
+
});
|
|
8
|
+
expect(projectRoomUserEventScope("t1", "u1")).toEqual(projectRoomUserEventScope("t1", "u1"));
|
|
9
|
+
expect(projectRoomUserEventScope("t1", "u2")).not.toEqual(projectRoomUserEventScope("t1", "u1"));
|
|
10
|
+
});
|
|
11
|
+
it("user scope ids are valid broker event id candidates", () => {
|
|
12
|
+
const scope = projectRoomUserEventScope("t1", "u1");
|
|
13
|
+
expect(typeof scope.roomId).toBe("string");
|
|
14
|
+
expect(parseProjectRoomEventId(`${"0a1b2c3d-4e5f-4a6b-8c9d-0e1f2a3b4c5d"}:1`)?.sequence).toBe(1);
|
|
15
|
+
});
|
|
16
|
+
});
|
|
@@ -264,6 +264,10 @@ class FakeProjectRoomMessageStore implements ProjectRoomMessageStore {
|
|
|
264
264
|
const [tenantId, id] = args;
|
|
265
265
|
return tenantId === "tenant-1" && id === "message-1" ? message : null;
|
|
266
266
|
}
|
|
267
|
+
|
|
268
|
+
async countAfter(input: Parameters<ProjectRoomMessageStore["countAfter"]>[0]): Promise<number> {
|
|
269
|
+
return input.tenantId === "tenant-1" && input.roomId === "room-1" ? 1 : 0;
|
|
270
|
+
}
|
|
267
271
|
}
|
|
268
272
|
|
|
269
273
|
describe("ProjectRoomStore", () => {
|
package/src/index.ts
CHANGED
|
@@ -56,6 +56,7 @@ export * from "./ProjectRoomStoreProtocol";
|
|
|
56
56
|
export * from "./ProjectMembershipStoreProtocol";
|
|
57
57
|
export * from "./ProjectBotMembershipStoreProtocol";
|
|
58
58
|
export * from "./ProjectRoomMessageStoreProtocol";
|
|
59
|
+
export * from "./ProjectRoomReadStateProtocol";
|
|
59
60
|
export * from "./ExactDataSnapshot";
|
|
60
61
|
export * from "./ProjectRoomRealtimeProtocol";
|
|
61
62
|
|