@relaycast/engine 5.0.0 → 5.0.2

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.
Files changed (202) hide show
  1. package/dist/adapters/node/index.d.ts.map +1 -1
  2. package/dist/adapters/node/index.js +0 -1
  3. package/dist/adapters/node/index.js.map +1 -1
  4. package/dist/adapters/node/realtime.d.ts +13 -41
  5. package/dist/adapters/node/realtime.d.ts.map +1 -1
  6. package/dist/adapters/node/realtime.js +68 -230
  7. package/dist/adapters/node/realtime.js.map +1 -1
  8. package/dist/auth/index.d.ts.map +1 -1
  9. package/dist/auth/index.js +31 -9
  10. package/dist/auth/index.js.map +1 -1
  11. package/dist/auth/tokenKind.d.ts +40 -0
  12. package/dist/auth/tokenKind.d.ts.map +1 -0
  13. package/dist/auth/tokenKind.js +89 -0
  14. package/dist/auth/tokenKind.js.map +1 -0
  15. package/dist/db/migrations/0025_node_kind_role_adapter.sql +45 -0
  16. package/dist/db/migrations/0026_observer_tokens.sql +32 -0
  17. package/dist/db/schema.d.ts +329 -0
  18. package/dist/db/schema.d.ts.map +1 -1
  19. package/dist/db/schema.js +32 -3
  20. package/dist/db/schema.js.map +1 -1
  21. package/dist/engine/action.d.ts +2 -2
  22. package/dist/engine/action.d.ts.map +1 -1
  23. package/dist/engine/action.js +182 -7
  24. package/dist/engine/action.js.map +1 -1
  25. package/dist/engine/activity.d.ts +6 -0
  26. package/dist/engine/activity.d.ts.map +1 -1
  27. package/dist/engine/activity.js +9 -2
  28. package/dist/engine/activity.js.map +1 -1
  29. package/dist/engine/agent.d.ts +0 -33
  30. package/dist/engine/agent.d.ts.map +1 -1
  31. package/dist/engine/agent.js +0 -155
  32. package/dist/engine/agent.js.map +1 -1
  33. package/dist/engine/attachments.d.ts +6 -0
  34. package/dist/engine/attachments.d.ts.map +1 -1
  35. package/dist/engine/attachments.js +37 -0
  36. package/dist/engine/attachments.js.map +1 -1
  37. package/dist/engine/console.d.ts +78 -1
  38. package/dist/engine/console.d.ts.map +1 -1
  39. package/dist/engine/console.js +175 -19
  40. package/dist/engine/console.js.map +1 -1
  41. package/dist/engine/delivery.d.ts +2 -0
  42. package/dist/engine/delivery.d.ts.map +1 -1
  43. package/dist/engine/delivery.js +4 -0
  44. package/dist/engine/delivery.js.map +1 -1
  45. package/dist/engine/deliveryWire.d.ts +4 -0
  46. package/dist/engine/deliveryWire.d.ts.map +1 -1
  47. package/dist/engine/deliveryWire.js +2 -0
  48. package/dist/engine/deliveryWire.js.map +1 -1
  49. package/dist/engine/deliveryWrites.d.ts +1 -0
  50. package/dist/engine/deliveryWrites.d.ts.map +1 -1
  51. package/dist/engine/deliveryWrites.js +7 -2
  52. package/dist/engine/deliveryWrites.js.map +1 -1
  53. package/dist/engine/dm.d.ts.map +1 -1
  54. package/dist/engine/dm.js +11 -33
  55. package/dist/engine/dm.js.map +1 -1
  56. package/dist/engine/dmAll.d.ts.map +1 -1
  57. package/dist/engine/dmAll.js +2 -0
  58. package/dist/engine/dmAll.js.map +1 -1
  59. package/dist/engine/groupDm.d.ts +2 -3
  60. package/dist/engine/groupDm.d.ts.map +1 -1
  61. package/dist/engine/groupDm.js +8 -26
  62. package/dist/engine/groupDm.js.map +1 -1
  63. package/dist/engine/invocationCompletion.d.ts +1 -1
  64. package/dist/engine/invocationCompletion.d.ts.map +1 -1
  65. package/dist/engine/invocationCompletion.js +9 -2
  66. package/dist/engine/invocationCompletion.js.map +1 -1
  67. package/dist/engine/message.d.ts.map +1 -1
  68. package/dist/engine/message.js +15 -44
  69. package/dist/engine/message.js.map +1 -1
  70. package/dist/engine/node.d.ts +13 -1
  71. package/dist/engine/node.d.ts.map +1 -1
  72. package/dist/engine/node.js +100 -31
  73. package/dist/engine/node.js.map +1 -1
  74. package/dist/engine/nodeContext.d.ts +6 -1
  75. package/dist/engine/nodeContext.d.ts.map +1 -1
  76. package/dist/engine/nodeContext.js +47 -32
  77. package/dist/engine/nodeContext.js.map +1 -1
  78. package/dist/engine/nodeDeliver.d.ts +23 -0
  79. package/dist/engine/nodeDeliver.d.ts.map +1 -0
  80. package/dist/engine/nodeDeliver.js +81 -0
  81. package/dist/engine/nodeDeliver.js.map +1 -0
  82. package/dist/engine/observerToken.d.ts +110 -0
  83. package/dist/engine/observerToken.d.ts.map +1 -0
  84. package/dist/engine/observerToken.js +528 -0
  85. package/dist/engine/observerToken.js.map +1 -0
  86. package/dist/engine/search.d.ts +4 -0
  87. package/dist/engine/search.d.ts.map +1 -1
  88. package/dist/engine/search.js +6 -1
  89. package/dist/engine/search.js.map +1 -1
  90. package/dist/engine/thread.d.ts +4 -3
  91. package/dist/engine/thread.d.ts.map +1 -1
  92. package/dist/engine/thread.js +8 -0
  93. package/dist/engine/thread.js.map +1 -1
  94. package/dist/engine/wsAuth.d.ts +2 -6
  95. package/dist/engine/wsAuth.d.ts.map +1 -1
  96. package/dist/engine/wsAuth.js +20 -12
  97. package/dist/engine/wsAuth.js.map +1 -1
  98. package/dist/engine/wsTransform.d.ts.map +1 -1
  99. package/dist/engine/wsTransform.js +52 -36
  100. package/dist/engine/wsTransform.js.map +1 -1
  101. package/dist/engine.d.ts.map +1 -1
  102. package/dist/engine.js +4 -26
  103. package/dist/engine.js.map +1 -1
  104. package/dist/entrypoints/node.d.ts.map +1 -1
  105. package/dist/entrypoints/node.js +1 -14
  106. package/dist/entrypoints/node.js.map +1 -1
  107. package/dist/env.d.ts +3 -1
  108. package/dist/env.d.ts.map +1 -1
  109. package/dist/index.d.ts +3 -1
  110. package/dist/index.d.ts.map +1 -1
  111. package/dist/index.js +2 -0
  112. package/dist/index.js.map +1 -1
  113. package/dist/middleware/auth.d.ts +8 -0
  114. package/dist/middleware/auth.d.ts.map +1 -1
  115. package/dist/middleware/auth.js +62 -0
  116. package/dist/middleware/auth.js.map +1 -1
  117. package/dist/middleware/logger.d.ts.map +1 -1
  118. package/dist/middleware/logger.js +4 -4
  119. package/dist/middleware/logger.js.map +1 -1
  120. package/dist/middleware/planLimits.d.ts.map +1 -1
  121. package/dist/middleware/planLimits.js +9 -2
  122. package/dist/middleware/planLimits.js.map +1 -1
  123. package/dist/middleware/rateLimit.d.ts.map +1 -1
  124. package/dist/middleware/rateLimit.js +13 -1
  125. package/dist/middleware/rateLimit.js.map +1 -1
  126. package/dist/middleware/usageTracker.d.ts.map +1 -1
  127. package/dist/middleware/usageTracker.js +2 -1
  128. package/dist/middleware/usageTracker.js.map +1 -1
  129. package/dist/node-reconnect.d.ts +13 -0
  130. package/dist/node-reconnect.d.ts.map +1 -0
  131. package/dist/node-reconnect.js +15 -0
  132. package/dist/node-reconnect.js.map +1 -0
  133. package/dist/ports/auth.d.ts +6 -2
  134. package/dist/ports/auth.d.ts.map +1 -1
  135. package/dist/ports/entitlements.d.ts +3 -1
  136. package/dist/ports/entitlements.d.ts.map +1 -1
  137. package/dist/ports/index.d.ts +1 -1
  138. package/dist/ports/index.d.ts.map +1 -1
  139. package/dist/ports/presence.d.ts +1 -3
  140. package/dist/ports/presence.d.ts.map +1 -1
  141. package/dist/ports/realtime.d.ts +16 -68
  142. package/dist/ports/realtime.d.ts.map +1 -1
  143. package/dist/ports/realtime.js +9 -9
  144. package/dist/providers/static-entitlements.js +4 -4
  145. package/dist/providers/static-entitlements.js.map +1 -1
  146. package/dist/routes/action.d.ts.map +1 -1
  147. package/dist/routes/action.js +13 -5
  148. package/dist/routes/action.js.map +1 -1
  149. package/dist/routes/agent.d.ts.map +1 -1
  150. package/dist/routes/agent.js +107 -51
  151. package/dist/routes/agent.js.map +1 -1
  152. package/dist/routes/channel.d.ts.map +1 -1
  153. package/dist/routes/channel.js +39 -46
  154. package/dist/routes/channel.js.map +1 -1
  155. package/dist/routes/console.d.ts.map +1 -1
  156. package/dist/routes/console.js +39 -9
  157. package/dist/routes/console.js.map +1 -1
  158. package/dist/routes/deliveryRouting.d.ts.map +1 -1
  159. package/dist/routes/deliveryRouting.js +39 -20
  160. package/dist/routes/deliveryRouting.js.map +1 -1
  161. package/dist/routes/fanout.d.ts +1 -4
  162. package/dist/routes/fanout.d.ts.map +1 -1
  163. package/dist/routes/fanout.js +44 -55
  164. package/dist/routes/fanout.js.map +1 -1
  165. package/dist/routes/file.d.ts.map +1 -1
  166. package/dist/routes/file.js +15 -5
  167. package/dist/routes/file.js.map +1 -1
  168. package/dist/routes/inboundWebhook.js +1 -1
  169. package/dist/routes/inboundWebhook.js.map +1 -1
  170. package/dist/routes/message.d.ts.map +1 -1
  171. package/dist/routes/message.js +22 -4
  172. package/dist/routes/message.js.map +1 -1
  173. package/dist/routes/node.d.ts.map +1 -1
  174. package/dist/routes/node.js +61 -10
  175. package/dist/routes/node.js.map +1 -1
  176. package/dist/routes/observerToken.d.ts +4 -0
  177. package/dist/routes/observerToken.d.ts.map +1 -0
  178. package/dist/routes/observerToken.js +105 -0
  179. package/dist/routes/observerToken.js.map +1 -0
  180. package/dist/routes/presence.d.ts.map +1 -1
  181. package/dist/routes/presence.js +8 -12
  182. package/dist/routes/presence.js.map +1 -1
  183. package/dist/routes/reaction.d.ts.map +1 -1
  184. package/dist/routes/reaction.js +59 -7
  185. package/dist/routes/reaction.js.map +1 -1
  186. package/dist/routes/receipt.d.ts.map +1 -1
  187. package/dist/routes/receipt.js +20 -3
  188. package/dist/routes/receipt.js.map +1 -1
  189. package/dist/routes/search.d.ts.map +1 -1
  190. package/dist/routes/search.js +7 -4
  191. package/dist/routes/search.js.map +1 -1
  192. package/dist/routes/thread.d.ts.map +1 -1
  193. package/dist/routes/thread.js +20 -3
  194. package/dist/routes/thread.js.map +1 -1
  195. package/dist/routes/workspace.d.ts.map +1 -1
  196. package/dist/routes/workspace.js +20 -7
  197. package/dist/routes/workspace.js.map +1 -1
  198. package/package.json +7 -3
  199. package/dist/engine/resyncQuery.d.ts +0 -15
  200. package/dist/engine/resyncQuery.d.ts.map +0 -1
  201. package/dist/engine/resyncQuery.js +0 -125
  202. package/dist/engine/resyncQuery.js.map +0 -1
@@ -0,0 +1,110 @@
1
+ import { OBSERVER_SCOPES } from '@relaycast/types';
2
+ import type { ObserverScope as SharedObserverScope } from '@relaycast/types';
3
+ import type { getDb } from '../db/index.js';
4
+ import { observerTokens } from '../db/schema.js';
5
+ import type { ObserverTokenFilters } from '../db/schema.js';
6
+ type Db = ReturnType<typeof getDb>;
7
+ export type ObserverToken = typeof observerTokens.$inferSelect;
8
+ export { OBSERVER_SCOPES };
9
+ export type ObserverScope = SharedObserverScope;
10
+ export declare const OBSERVER_SCOPE_SET: Set<string>;
11
+ export interface PublicObserverToken {
12
+ id: string;
13
+ name: string;
14
+ description: string | null;
15
+ scopes: ObserverScope[];
16
+ filters: ObserverTokenFilters;
17
+ status: string;
18
+ expires_at: string | null;
19
+ created_at: string;
20
+ updated_at: string | null;
21
+ revoked_at: string | null;
22
+ last_used_at: string | null;
23
+ token?: string;
24
+ }
25
+ export interface CreateObserverTokenInput {
26
+ name: string;
27
+ description?: string | null;
28
+ scopes: ObserverScope[];
29
+ filters?: ObserverTokenFilters;
30
+ expires_at?: string | null;
31
+ }
32
+ export interface UpdateObserverTokenInput {
33
+ name?: string;
34
+ description?: string | null;
35
+ scopes?: ObserverScope[];
36
+ filters?: ObserverTokenFilters;
37
+ expires_at?: string | null;
38
+ }
39
+ export declare function normalizeObserverFilters(filters?: ObserverTokenFilters | null): ObserverTokenFilters;
40
+ export declare function createObserverToken(db: Db, workspaceId: string, input: CreateObserverTokenInput): Promise<PublicObserverToken>;
41
+ export declare function listObserverTokens(db: Db, workspaceId: string): Promise<PublicObserverToken[]>;
42
+ export declare function getObserverToken(db: Db, workspaceId: string, id: string): Promise<PublicObserverToken | null>;
43
+ export declare function updateObserverToken(db: Db, workspaceId: string, id: string, input: UpdateObserverTokenInput): Promise<PublicObserverToken | null>;
44
+ export declare function rotateObserverToken(db: Db, workspaceId: string, id: string): Promise<PublicObserverToken | null>;
45
+ export declare function revokeObserverToken(db: Db, workspaceId: string, id: string): Promise<boolean>;
46
+ export declare function getActiveObserverTokenByHash(db: Db, tokenHash: string): Promise<ObserverToken | null>;
47
+ export declare function hasObserverScope(observer: Pick<ObserverToken, 'scopes'>, scope: ObserverScope): boolean;
48
+ export declare function hasAnyObserverScope(observer: Pick<ObserverToken, 'scopes'>, scopes: ObserverScope[]): boolean;
49
+ export declare function observerAllowsChannel(observer: Pick<ObserverToken, 'filters'> | undefined, channel: {
50
+ id?: string | null;
51
+ name?: string | null;
52
+ channel_type?: number | null;
53
+ }): boolean;
54
+ export declare function observerAllowsConversation(observer: Pick<ObserverToken, 'filters' | 'scopes'> | undefined, conversationId: string | null | undefined): boolean;
55
+ export declare function observerAllowsAgent(observer: Pick<ObserverToken, 'filters'> | undefined, agentId: string | null | undefined): boolean;
56
+ export declare function observerAllowsCreatedAt(observer: Pick<ObserverToken, 'filters'> | undefined, createdAt: string | Date | number | null | undefined): boolean;
57
+ export declare function observerAllowsMessage(observer: Pick<ObserverToken, 'filters'> | undefined, message: {
58
+ agent_id?: string | null;
59
+ created_at?: string | Date | number | null;
60
+ }): boolean;
61
+ export type MessageObserverResource = {
62
+ channel_id: string;
63
+ channel_name: string;
64
+ channel_type: number;
65
+ conversation_id: string | null;
66
+ agent_id: string;
67
+ created_at: string;
68
+ };
69
+ export declare function observerAllowsMessageResource(observer: Pick<ObserverToken, 'filters' | 'scopes'> | undefined, resource: MessageObserverResource, options?: {
70
+ normalScope?: ObserverScope;
71
+ }): boolean;
72
+ export type FileObserverResource = {
73
+ id: string;
74
+ uploaded_by_id: string;
75
+ created_at: string;
76
+ attachments: Array<{
77
+ channel_id: string;
78
+ channel_name: string;
79
+ channel_type: number;
80
+ conversation_id: string | null;
81
+ }>;
82
+ };
83
+ export declare function observerAllowsFile(observer: Pick<ObserverToken, 'filters' | 'scopes'> | undefined, file: FileObserverResource): boolean;
84
+ export declare function observerAllowsAnyEventType(observer: Pick<ObserverToken, 'filters'> | undefined, eventTypes: string[]): boolean;
85
+ export declare function observerAllowsEvent(observer: Pick<ObserverToken, 'filters' | 'scopes'> | undefined, event: Record<string, unknown>): boolean;
86
+ export declare function getMessageObserverResource(db: Db, workspaceId: string, messageId: string): Promise<MessageObserverResource | null>;
87
+ export declare function getChannelObserverResource(db: Db, workspaceId: string, name: string): Promise<{
88
+ id: string;
89
+ name: string;
90
+ channel_type: number;
91
+ } | null>;
92
+ export declare function getFileObserverResource(db: Db, workspaceId: string, fileId: string): Promise<FileObserverResource | null>;
93
+ export declare function getFileObserverResources(db: Db, workspaceId: string, fileIds: string[]): Promise<Map<string, FileObserverResource>>;
94
+ export declare function filterObserverFileResults<T extends {
95
+ id: string;
96
+ }>(db: Db, workspaceId: string, observer: Pick<ObserverToken, 'filters' | 'scopes'> | undefined, results: T[]): Promise<T[]>;
97
+ export declare function filterObserverSearchResults<T extends {
98
+ channel_id?: string;
99
+ channel_name?: string | null;
100
+ channel_type?: number | null;
101
+ conversation_id?: string | null;
102
+ agent_id?: string | null;
103
+ created_at?: string | null;
104
+ }>(db: Db, workspaceId: string, observer: Pick<ObserverToken, 'filters' | 'scopes'> | undefined, results: T[], opts?: {
105
+ eventTypes?: (result: T) => string[];
106
+ }): Promise<T[]>;
107
+ export declare function getObserverTokenFromContext(c: {
108
+ get(key: 'observerToken'): ObserverToken | undefined;
109
+ }): ObserverToken | undefined;
110
+ //# sourceMappingURL=observerToken.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"observerToken.d.ts","sourceRoot":"","sources":["../../src/engine/observerToken.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,528 @@
1
+ import { and, eq, or, gt, isNull, inArray, ne } from 'drizzle-orm';
2
+ import { OBSERVER_SCOPES } from '@relaycast/types';
3
+ import { channels, dmConversations, files as fileRows, messageAttachments, messages, observerTokens } from '../db/schema.js';
4
+ import { randomHex, sha256Hex } from '../lib/crypto.js';
5
+ import { codedError } from '../lib/httpError.js';
6
+ import { generateId } from './snowflake.js';
7
+ const LAST_USED_DEBOUNCE_MS = 30_000;
8
+ export { OBSERVER_SCOPES };
9
+ export const OBSERVER_SCOPE_SET = new Set(OBSERVER_SCOPES);
10
+ function parseExpiresAt(value) {
11
+ if (value == null)
12
+ return null;
13
+ const date = new Date(value);
14
+ if (Number.isNaN(date.getTime())) {
15
+ throw codedError('expires_at must be an ISO-8601 timestamp', 'invalid_request', 400);
16
+ }
17
+ if (date.getTime() <= Date.now()) {
18
+ throw codedError('expires_at must be in the future', 'invalid_request', 400);
19
+ }
20
+ return date;
21
+ }
22
+ export function normalizeObserverFilters(filters) {
23
+ return {
24
+ ...(filters?.channel_ids?.length ? { channel_ids: [...new Set(filters.channel_ids)] } : {}),
25
+ ...(filters?.channel_names?.length ? { channel_names: [...new Set(filters.channel_names)] } : {}),
26
+ include_dms: filters?.include_dms === true,
27
+ ...(filters?.dm_conversation_ids?.length ? { dm_conversation_ids: [...new Set(filters.dm_conversation_ids)] } : {}),
28
+ ...(filters?.agent_ids?.length ? { agent_ids: [...new Set(filters.agent_ids)] } : {}),
29
+ ...(filters?.event_types?.length ? { event_types: [...new Set(filters.event_types)] } : {}),
30
+ ...(filters?.created_after ? { created_after: filters.created_after } : {}),
31
+ };
32
+ }
33
+ function normalizeScopes(scopes) {
34
+ const unique = [...new Set(scopes)];
35
+ const invalid = unique.filter((scope) => !OBSERVER_SCOPE_SET.has(scope));
36
+ if (invalid.length > 0) {
37
+ throw codedError(`Invalid observer scopes: ${invalid.join(', ')}`, 'invalid_request', 400);
38
+ }
39
+ if (unique.length === 0) {
40
+ throw codedError('At least one observer scope is required', 'invalid_request', 400);
41
+ }
42
+ return unique;
43
+ }
44
+ function publicScopes(scopes) {
45
+ const unique = [...new Set(scopes ?? [])];
46
+ return unique.filter((scope) => OBSERVER_SCOPE_SET.has(scope));
47
+ }
48
+ function publicObserverToken(row, token) {
49
+ return {
50
+ id: row.id,
51
+ name: row.name,
52
+ description: row.description,
53
+ scopes: publicScopes(row.scopes),
54
+ filters: normalizeObserverFilters(row.filters),
55
+ status: row.status,
56
+ expires_at: row.expiresAt?.toISOString() ?? null,
57
+ created_at: row.createdAt.toISOString(),
58
+ updated_at: row.updatedAt?.toISOString() ?? null,
59
+ revoked_at: row.revokedAt?.toISOString() ?? null,
60
+ last_used_at: row.lastUsedAt?.toISOString() ?? null,
61
+ ...(token ? { token } : {}),
62
+ };
63
+ }
64
+ function isObserverTokenNameConflict(err) {
65
+ const candidate = err;
66
+ const message = candidate.message ?? '';
67
+ return (candidate.code === 'SQLITE_CONSTRAINT'
68
+ || candidate.code === 'SQLITE_CONSTRAINT_UNIQUE'
69
+ || message.includes('observer_tokens_workspace_name_unique')
70
+ || message.includes('observer_tokens.workspace_id, observer_tokens.name'));
71
+ }
72
+ function observerTokenNameConflict() {
73
+ throw codedError('Observer token name already exists in this workspace', 'observer_token_name_conflict', 409);
74
+ }
75
+ export async function createObserverToken(db, workspaceId, input) {
76
+ const token = `ot_live_${randomHex(24)}`;
77
+ const tokenHash = await sha256Hex(token);
78
+ const expiresAt = parseExpiresAt(input.expires_at);
79
+ const scopes = normalizeScopes(input.scopes);
80
+ const filters = normalizeObserverFilters(input.filters);
81
+ let created;
82
+ try {
83
+ [created] = await db
84
+ .insert(observerTokens)
85
+ .values({
86
+ id: `ot_${generateId()}`,
87
+ workspaceId,
88
+ name: input.name,
89
+ description: input.description ?? null,
90
+ tokenHash,
91
+ scopes,
92
+ filters,
93
+ expiresAt,
94
+ createdBy: workspaceId,
95
+ createdByType: 'workspace',
96
+ })
97
+ .returning();
98
+ }
99
+ catch (err) {
100
+ if (isObserverTokenNameConflict(err))
101
+ observerTokenNameConflict();
102
+ throw err;
103
+ }
104
+ if (!created) {
105
+ throw codedError('Failed to create observer token', 'internal_error', 500);
106
+ }
107
+ return publicObserverToken(created, token);
108
+ }
109
+ export async function listObserverTokens(db, workspaceId) {
110
+ const rows = await db
111
+ .select()
112
+ .from(observerTokens)
113
+ .where(eq(observerTokens.workspaceId, workspaceId));
114
+ return rows.map((row) => publicObserverToken(row));
115
+ }
116
+ export async function getObserverToken(db, workspaceId, id) {
117
+ const [row] = await db
118
+ .select()
119
+ .from(observerTokens)
120
+ .where(and(eq(observerTokens.workspaceId, workspaceId), eq(observerTokens.id, id)));
121
+ return row ? publicObserverToken(row) : null;
122
+ }
123
+ export async function updateObserverToken(db, workspaceId, id, input) {
124
+ const update = {
125
+ updatedAt: new Date(),
126
+ };
127
+ if (input.name !== undefined)
128
+ update.name = input.name;
129
+ if (input.description !== undefined)
130
+ update.description = input.description;
131
+ if (input.scopes !== undefined)
132
+ update.scopes = normalizeScopes(input.scopes);
133
+ if (input.filters !== undefined)
134
+ update.filters = normalizeObserverFilters(input.filters);
135
+ if (input.expires_at !== undefined)
136
+ update.expiresAt = parseExpiresAt(input.expires_at);
137
+ let row;
138
+ try {
139
+ [row] = await db
140
+ .update(observerTokens)
141
+ .set(update)
142
+ .where(and(eq(observerTokens.workspaceId, workspaceId), eq(observerTokens.id, id), eq(observerTokens.status, 'active')))
143
+ .returning();
144
+ }
145
+ catch (err) {
146
+ if (isObserverTokenNameConflict(err))
147
+ observerTokenNameConflict();
148
+ throw err;
149
+ }
150
+ return row ? publicObserverToken(row) : null;
151
+ }
152
+ export async function rotateObserverToken(db, workspaceId, id) {
153
+ const token = `ot_live_${randomHex(24)}`;
154
+ const tokenHash = await sha256Hex(token);
155
+ const [row] = await db
156
+ .update(observerTokens)
157
+ .set({ tokenHash, updatedAt: new Date(), lastUsedAt: null })
158
+ .where(and(eq(observerTokens.workspaceId, workspaceId), eq(observerTokens.id, id), eq(observerTokens.status, 'active')))
159
+ .returning();
160
+ return row ? publicObserverToken(row, token) : null;
161
+ }
162
+ export async function revokeObserverToken(db, workspaceId, id) {
163
+ const now = new Date();
164
+ const [row] = await db
165
+ .update(observerTokens)
166
+ .set({ status: 'revoked', revokedAt: now, updatedAt: now })
167
+ .where(and(eq(observerTokens.workspaceId, workspaceId), eq(observerTokens.id, id), eq(observerTokens.status, 'active')))
168
+ .returning({ id: observerTokens.id });
169
+ return Boolean(row);
170
+ }
171
+ export async function getActiveObserverTokenByHash(db, tokenHash) {
172
+ const now = new Date();
173
+ const [row] = await db
174
+ .select()
175
+ .from(observerTokens)
176
+ .where(and(eq(observerTokens.tokenHash, tokenHash), eq(observerTokens.status, 'active'), or(isNull(observerTokens.expiresAt), gt(observerTokens.expiresAt, now))));
177
+ if (!row)
178
+ return null;
179
+ if (!row.lastUsedAt || now.getTime() - row.lastUsedAt.getTime() > LAST_USED_DEBOUNCE_MS) {
180
+ void (async () => {
181
+ try {
182
+ await db
183
+ .update(observerTokens)
184
+ .set({ lastUsedAt: now })
185
+ .where(eq(observerTokens.id, row.id));
186
+ }
187
+ catch {
188
+ // last_used_at is best-effort audit metadata; authentication should not fail
189
+ // solely because a read replica or adapter rejects this update.
190
+ }
191
+ })();
192
+ }
193
+ return row;
194
+ }
195
+ export function hasObserverScope(observer, scope) {
196
+ return publicScopes(observer.scopes).includes(scope);
197
+ }
198
+ export function hasAnyObserverScope(observer, scopes) {
199
+ return scopes.some((scope) => hasObserverScope(observer, scope));
200
+ }
201
+ function filterList(values) {
202
+ return values && values.length > 0 ? new Set(values) : null;
203
+ }
204
+ function hasChannelOrDmFilter(filters) {
205
+ return Boolean(filters.channel_ids?.length
206
+ || filters.channel_names?.length
207
+ || filters.dm_conversation_ids?.length
208
+ || filters.include_dms);
209
+ }
210
+ export function observerAllowsChannel(observer, channel) {
211
+ if (!observer)
212
+ return true;
213
+ if (channel.channel_type != null && channel.channel_type !== 0)
214
+ return false;
215
+ const filters = normalizeObserverFilters(observer.filters);
216
+ const ids = filterList(filters.channel_ids);
217
+ const names = filterList(filters.channel_names);
218
+ if (!ids && !names)
219
+ return true;
220
+ return Boolean((channel.id && ids?.has(channel.id)) || (channel.name && names?.has(channel.name)));
221
+ }
222
+ export function observerAllowsConversation(observer, conversationId) {
223
+ if (!observer)
224
+ return true;
225
+ const filters = normalizeObserverFilters(observer.filters);
226
+ if (!hasObserverScope(observer, 'dms:read'))
227
+ return false;
228
+ if (!filters.include_dms)
229
+ return false;
230
+ const ids = filterList(filters.dm_conversation_ids);
231
+ if (!ids)
232
+ return true;
233
+ return Boolean(conversationId && ids.has(conversationId));
234
+ }
235
+ export function observerAllowsAgent(observer, agentId) {
236
+ if (!observer)
237
+ return true;
238
+ const ids = filterList(normalizeObserverFilters(observer.filters).agent_ids);
239
+ if (!ids)
240
+ return true;
241
+ return Boolean(agentId && ids.has(agentId));
242
+ }
243
+ function timestampMillis(value) {
244
+ if (value == null)
245
+ return null;
246
+ if (value instanceof Date)
247
+ return value.getTime();
248
+ if (typeof value === 'number')
249
+ return Number.isFinite(value) ? value : null;
250
+ const parsed = new Date(value).getTime();
251
+ return Number.isNaN(parsed) ? null : parsed;
252
+ }
253
+ export function observerAllowsCreatedAt(observer, createdAt) {
254
+ if (!observer)
255
+ return true;
256
+ const cutoff = normalizeObserverFilters(observer.filters).created_after;
257
+ if (!cutoff)
258
+ return true;
259
+ const cutoffMs = timestampMillis(cutoff);
260
+ const createdMs = timestampMillis(createdAt);
261
+ if (cutoffMs == null || createdMs == null)
262
+ return false;
263
+ return createdMs >= cutoffMs;
264
+ }
265
+ export function observerAllowsMessage(observer, message) {
266
+ return observerAllowsAgent(observer, message.agent_id)
267
+ && observerAllowsCreatedAt(observer, message.created_at);
268
+ }
269
+ export function observerAllowsMessageResource(observer, resource, options = {}) {
270
+ if (!observer)
271
+ return true;
272
+ if (!observerAllowsMessage(observer, resource))
273
+ return false;
274
+ if (resource.channel_type !== 0) {
275
+ return observerAllowsConversation(observer, resource.conversation_id);
276
+ }
277
+ if (options.normalScope && !hasObserverScope(observer, options.normalScope))
278
+ return false;
279
+ return observerAllowsChannel(observer, {
280
+ id: resource.channel_id,
281
+ name: resource.channel_name,
282
+ channel_type: resource.channel_type,
283
+ });
284
+ }
285
+ export function observerAllowsFile(observer, file) {
286
+ if (!observer)
287
+ return true;
288
+ if (!observerAllowsAgent(observer, file.uploaded_by_id))
289
+ return false;
290
+ if (!observerAllowsCreatedAt(observer, file.created_at))
291
+ return false;
292
+ if (file.attachments.length === 0) {
293
+ return !hasChannelOrDmFilter(normalizeObserverFilters(observer.filters));
294
+ }
295
+ return file.attachments.some((attachment) => (attachment.conversation_id
296
+ ? observerAllowsConversation(observer, attachment.conversation_id)
297
+ : observerAllowsChannel(observer, {
298
+ id: attachment.channel_id,
299
+ name: attachment.channel_name,
300
+ channel_type: attachment.channel_type,
301
+ })));
302
+ }
303
+ function nestedRecord(value) {
304
+ return value && typeof value === 'object' && !Array.isArray(value) ? value : {};
305
+ }
306
+ function eventChannelName(event) {
307
+ const channel = event.channel;
308
+ if (typeof channel === 'string')
309
+ return channel;
310
+ const channelObject = nestedRecord(channel);
311
+ return typeof channelObject.name === 'string' ? channelObject.name : null;
312
+ }
313
+ function eventAgentId(event) {
314
+ const message = nestedRecord(event.message);
315
+ const agent = nestedRecord(event.agent);
316
+ return (typeof event.agent_id === 'string' ? event.agent_id : null)
317
+ ?? (typeof event.subject_agent_id === 'string' ? event.subject_agent_id : null)
318
+ ?? (typeof message.agent_id === 'string' ? message.agent_id : null)
319
+ ?? (typeof agent.id === 'string' ? agent.id : null);
320
+ }
321
+ function eventCreatedAt(event) {
322
+ const message = nestedRecord(event.message);
323
+ return (typeof event.created_at === 'string' ? event.created_at : null)
324
+ ?? (typeof event.timestamp === 'string' ? event.timestamp : null)
325
+ ?? (typeof message.created_at === 'string' ? message.created_at : null)
326
+ ?? null;
327
+ }
328
+ /**
329
+ * Event types that are intrinsically scoped to a single channel. These must
330
+ * carry channel metadata to be visible to a channel-filtered observer; if the
331
+ * metadata is missing they are rejected (fail closed). Workspace-wide events
332
+ * (e.g. `agent.status.*`, `delivery.*`, `node.*`) are not channel-bound, so the
333
+ * channel filter only applies to them when they actually reference a channel.
334
+ */
335
+ function isChannelScopedEventType(type) {
336
+ return (type === 'message.created'
337
+ || type === 'message.updated'
338
+ || type === 'message.read'
339
+ || type === 'message.reacted'
340
+ || type === 'thread.reply'
341
+ || type === 'webhook.received'
342
+ || type.startsWith('member.')
343
+ || type.startsWith('channel.'));
344
+ }
345
+ function eventRequiredScopes(type) {
346
+ if (type === 'message.created' || type === 'message.updated' || type === 'message.read' || type === 'webhook.received')
347
+ return ['messages:read'];
348
+ if (type === 'thread.reply')
349
+ return ['threads:read'];
350
+ if (type === 'message.reacted')
351
+ return ['reactions:read'];
352
+ if (type === 'dm.received' || type === 'group_dm.received')
353
+ return ['dms:read'];
354
+ if (type === 'file.uploaded')
355
+ return ['files:read'];
356
+ if (type.startsWith('agent.status.'))
357
+ return ['agents:read'];
358
+ if (type.startsWith('member.') || type.startsWith('channel.'))
359
+ return ['channels:read'];
360
+ if (type.startsWith('delivery.'))
361
+ return ['deliveries:read'];
362
+ if (type.startsWith('node.'))
363
+ return ['nodes:read'];
364
+ return ['activity:read'];
365
+ }
366
+ export function observerAllowsAnyEventType(observer, eventTypes) {
367
+ if (!observer)
368
+ return true;
369
+ const filters = normalizeObserverFilters(observer.filters);
370
+ if (!filters.event_types?.length)
371
+ return true;
372
+ return eventTypes.some((type) => filters.event_types?.includes(type));
373
+ }
374
+ export function observerAllowsEvent(observer, event) {
375
+ if (!observer)
376
+ return true;
377
+ const filters = normalizeObserverFilters(observer.filters);
378
+ const type = typeof event.type === 'string' ? event.type : '';
379
+ if (filters.event_types?.length && !filters.event_types.includes(type)) {
380
+ return false;
381
+ }
382
+ if (!hasAnyObserverScope(observer, eventRequiredScopes(type))) {
383
+ return false;
384
+ }
385
+ if (!observerAllowsCreatedAt(observer, eventCreatedAt(event))) {
386
+ return false;
387
+ }
388
+ const conversationId = typeof event.conversation_id === 'string' ? event.conversation_id : null;
389
+ const isDm = conversationId !== null || type === 'dm.received' || type === 'group_dm.received';
390
+ if (isDm) {
391
+ if (!observerAllowsConversation(observer, conversationId)) {
392
+ return false;
393
+ }
394
+ }
395
+ else {
396
+ const channelId = typeof event.channel_id === 'string' ? event.channel_id : null;
397
+ const channelName = eventChannelName(event);
398
+ const hasChannelContext = Boolean(channelId || channelName);
399
+ // Only apply the channel filter when the event carries channel context, or
400
+ // when its type is intrinsically channel-scoped (so missing metadata fails
401
+ // closed). Workspace-wide events with no channel context skip this check so
402
+ // agent_id matching can still apply.
403
+ if ((hasChannelContext || isChannelScopedEventType(type))
404
+ && !observerAllowsChannel(observer, { id: channelId, name: channelName })) {
405
+ return false;
406
+ }
407
+ }
408
+ return observerAllowsAgent(observer, eventAgentId(event));
409
+ }
410
+ export async function getMessageObserverResource(db, workspaceId, messageId) {
411
+ const [row] = await db
412
+ .select({
413
+ channel_id: channels.id,
414
+ channel_name: channels.name,
415
+ channel_type: channels.channelType,
416
+ conversation_id: dmConversations.id,
417
+ agent_id: messages.agentId,
418
+ created_at: messages.createdAt,
419
+ })
420
+ .from(messages)
421
+ .innerJoin(channels, eq(messages.channelId, channels.id))
422
+ .leftJoin(dmConversations, eq(dmConversations.channelId, channels.id))
423
+ .where(and(eq(messages.id, messageId), eq(messages.workspaceId, workspaceId)));
424
+ return row ? { ...row, created_at: row.created_at.toISOString() } : null;
425
+ }
426
+ export async function getChannelObserverResource(db, workspaceId, name) {
427
+ const [row] = await db
428
+ .select({ id: channels.id, name: channels.name, channel_type: channels.channelType })
429
+ .from(channels)
430
+ .where(and(eq(channels.workspaceId, workspaceId), eq(channels.name, name)));
431
+ return row ?? null;
432
+ }
433
+ export async function getFileObserverResource(db, workspaceId, fileId) {
434
+ return (await getFileObserverResources(db, workspaceId, [fileId])).get(fileId) ?? null;
435
+ }
436
+ export async function getFileObserverResources(db, workspaceId, fileIds) {
437
+ const ids = [...new Set(fileIds)].filter((id) => id.length > 0);
438
+ const resources = new Map();
439
+ if (ids.length === 0)
440
+ return resources;
441
+ const files = await db
442
+ .select({
443
+ id: fileRows.id,
444
+ uploaded_by_id: fileRows.uploadedBy,
445
+ created_at: fileRows.createdAt,
446
+ })
447
+ .from(fileRows)
448
+ .where(and(eq(fileRows.workspaceId, workspaceId), ne(fileRows.status, 'deleted'), inArray(fileRows.id, ids)));
449
+ for (const file of files) {
450
+ resources.set(file.id, {
451
+ id: file.id,
452
+ uploaded_by_id: file.uploaded_by_id,
453
+ created_at: file.created_at.toISOString(),
454
+ attachments: [],
455
+ });
456
+ }
457
+ if (resources.size === 0)
458
+ return resources;
459
+ const attachments = await db
460
+ .select({
461
+ file_id: messageAttachments.fileId,
462
+ channel_id: channels.id,
463
+ channel_name: channels.name,
464
+ channel_type: channels.channelType,
465
+ conversation_id: dmConversations.id,
466
+ })
467
+ .from(messageAttachments)
468
+ .innerJoin(messages, eq(messageAttachments.messageId, messages.id))
469
+ .innerJoin(channels, eq(messages.channelId, channels.id))
470
+ .leftJoin(dmConversations, eq(dmConversations.channelId, channels.id))
471
+ .where(and(eq(messages.workspaceId, workspaceId), inArray(messageAttachments.fileId, [...resources.keys()])));
472
+ for (const attachment of attachments) {
473
+ const resource = resources.get(attachment.file_id);
474
+ if (!resource)
475
+ continue;
476
+ resource.attachments.push({
477
+ channel_id: attachment.channel_id,
478
+ channel_name: attachment.channel_name,
479
+ channel_type: attachment.channel_type,
480
+ conversation_id: attachment.conversation_id,
481
+ });
482
+ }
483
+ return resources;
484
+ }
485
+ export async function filterObserverFileResults(db, workspaceId, observer, results) {
486
+ if (!observer || results.length === 0)
487
+ return results;
488
+ const resources = await getFileObserverResources(db, workspaceId, results.map((result) => result.id));
489
+ return results.filter((result) => {
490
+ const resource = resources.get(result.id);
491
+ return Boolean(resource && observerAllowsFile(observer, resource));
492
+ });
493
+ }
494
+ export async function filterObserverSearchResults(db, workspaceId, observer, results, opts = {}) {
495
+ if (!observer || results.length === 0)
496
+ return results;
497
+ const missingChannelIds = [...new Set(results
498
+ .filter((result) => !result.channel_name && result.channel_id)
499
+ .map((result) => result.channel_id))];
500
+ const channelsById = new Map();
501
+ if (missingChannelIds.length > 0) {
502
+ const rows = await db
503
+ .select({ id: channels.id, name: channels.name, channel_type: channels.channelType })
504
+ .from(channels)
505
+ .where(and(eq(channels.workspaceId, workspaceId), inArray(channels.id, missingChannelIds)));
506
+ for (const row of rows)
507
+ channelsById.set(row.id, { name: row.name, channel_type: row.channel_type });
508
+ }
509
+ return results.filter((result) => {
510
+ if (opts.eventTypes && !observerAllowsAnyEventType(observer, opts.eventTypes(result)))
511
+ return false;
512
+ if (!observerAllowsMessage(observer, result))
513
+ return false;
514
+ if (result.conversation_id) {
515
+ return observerAllowsConversation(observer, result.conversation_id);
516
+ }
517
+ const channel = result.channel_id ? channelsById.get(result.channel_id) : undefined;
518
+ return observerAllowsChannel(observer, {
519
+ id: result.channel_id,
520
+ name: result.channel_name ?? channel?.name,
521
+ channel_type: result.channel_type ?? channel?.channel_type,
522
+ });
523
+ });
524
+ }
525
+ export function getObserverTokenFromContext(c) {
526
+ return c.get('observerToken');
527
+ }
528
+ //# sourceMappingURL=observerToken.js.map