@ai-matrx/chat 0.4.12 → 0.4.15

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 (39) hide show
  1. package/CHANGELOG.md +44 -0
  2. package/dist/agents/components/shared/AgentModeController.d.ts +1 -1
  3. package/dist/agents/components/shared/AgentModeController.js +4 -0
  4. package/dist/agents/redux/execution-system/active-requests/active-requests.selectors.js +1 -1
  5. package/dist/agents/redux/execution-system/active-requests/active-requests.slice.d.ts +2 -0
  6. package/dist/agents/redux/execution-system/active-requests/active-requests.slice.js +7 -0
  7. package/dist/agents/redux/execution-system/conversations/conversation-persistence.js +2 -4
  8. package/dist/agents/redux/execution-system/instance-context/instance-context.slice.js +1 -1
  9. package/dist/agents/redux/execution-system/thunks/dispatch-matrx-extend-tool.thunk.js +3 -1
  10. package/dist/agents/redux/execution-system/thunks/process-stream.js +26 -23
  11. package/dist/agents/redux/execution-system/thunks/run-headless-agent-json.js +1 -1
  12. package/dist/agents/runtime-reconnect/api.d.ts +6 -25
  13. package/dist/agents/runtime-reconnect/api.js +7 -13
  14. package/dist/agents/runtime-reconnect/reconnect-server-operation.thunk.js +167 -159
  15. package/dist/agents/runtime-reconnect/types.d.ts +2 -0
  16. package/dist/agents/services/mcp-connections.service.d.ts +12 -0
  17. package/dist/agents/services/mcp-connections.service.js +4 -7
  18. package/dist/agents/types/request.types.d.ts +6 -0
  19. package/dist/agents/utils/json-extraction.js +2 -2
  20. package/dist/cx-chat/utils/settings-diff.js +1 -21
  21. package/dist/host/configure.js +15 -5
  22. package/dist/host/contract.d.ts +43 -2
  23. package/dist/host/db-types.d.ts +112 -0
  24. package/dist/host/db.js +6 -0
  25. package/dist/host/defaults/diagnostics.d.ts +5 -0
  26. package/dist/host/defaults/diagnostics.js +1 -2
  27. package/dist/host/defaults/identity.d.ts +26 -0
  28. package/dist/host/defaults/identity.js +133 -1
  29. package/dist/host/identity.d.ts +19 -2
  30. package/dist/host/identity.js +18 -10
  31. package/dist/host/session-retry.js +2 -2
  32. package/dist/host/ui-slots.js +27 -20
  33. package/dist/surfaces/services/bind-agent-to-surface.service.js +2 -4
  34. package/dist/tool-call-visualization/renderers/agent-call/CollabCallCard.js +1 -1
  35. package/dist/ui/mandates-storage/mandateStorage.d.ts +60 -0
  36. package/dist/ui/mandates-storage/shortcutStorage.d.ts +15 -0
  37. package/dist/utils/deep-equal.d.ts +5 -0
  38. package/dist/utils/deep-equal.js +24 -0
  39. package/package.json +7 -6
@@ -738,6 +738,34 @@ export interface ChatAppIdentity {
738
738
  * `"local-only"` (confidential) conversation refuses every target but the Matrx Local engine on this
739
739
  * computer, loudly, before a byte leaves the machine. Default: `"any"` for every conversation.
740
740
  */
741
+ /** What a device-tool run answered (the same shape the browser-extension bridge answers). */
742
+ export type ChatDeviceToolInvocation = {
743
+ handled: false;
744
+ reason: string;
745
+ } | {
746
+ handled: true;
747
+ ok: true;
748
+ output: Record<string, unknown>;
749
+ } | {
750
+ handled: true;
751
+ ok: false;
752
+ error: string;
753
+ };
754
+ /**
755
+ * Tools that run ON THIS DEVICE for a cloud agent (lanes/agents-chat-proposal.md W0(c)): the
756
+ * browser tools when the chat runs inside Matrx Extend, local machine tools in a desktop host. The
757
+ * package owns the whole loop around them — the delegated call's lifecycle, the durable result
758
+ * submit with retry, and the resume (`surfaceDelegatedToolCall` → `submitToolResult`); the host
759
+ * only executes. `handled: false` = "not a tool this device owns" (the model is told, never a
760
+ * wedge). Absent: delegated device tools go to the installed browser extension through the server
761
+ * port (`invokeMatrxExtendTool`), as on the web.
762
+ */
763
+ export interface ChatDeviceToolsPort {
764
+ invoke(toolName: string, args: Record<string, unknown>, context: {
765
+ conversationId: string;
766
+ callId: string;
767
+ }): Promise<ChatDeviceToolInvocation>;
768
+ }
741
769
  export interface ChatRoutingPort {
742
770
  policy(conversationId: string): ChatRoutingPolicy;
743
771
  }
@@ -767,7 +795,16 @@ export interface ChatHost {
767
795
  * that is not matrx-frontend never labels its rows as the main app's.
768
796
  */
769
797
  app?: ChatAppIdentity;
770
- /** Default: from db.auth (getSession + onAuthStateChange); admin level from admin.admins. */
798
+ /**
799
+ * The token function a token-only client was built with (supabase-js `accessToken` option, the
800
+ * matrx-local custodian shape): pass the SAME function here and the default identity reads who is
801
+ * signed in from it. A token-only `db` without it is signed out, announced once with this remedy.
802
+ */
803
+ accessToken?: () => Promise<string | null>;
804
+ /**
805
+ * Default: from db.auth (getSession + onAuthStateChange); for a token-only `db`, from
806
+ * `accessToken`. Admin level from admin.admins either way.
807
+ */
771
808
  identity?: ChatIdentityPort;
772
809
  /** Default: no active org; `require` refuses loudly (never auto-picks). */
773
810
  org?: ChatOrgPort;
@@ -803,8 +840,10 @@ export interface ChatHost {
803
840
  canvas?: ChatCanvasPort;
804
841
  /** Default: every conversation may go to any target (`"any"`). */
805
842
  routing?: ChatRoutingPort;
843
+ /** Default: none — delegated device tools go to the browser extension via the server port. */
844
+ deviceTools?: ChatDeviceToolsPort;
806
845
  }
807
- export type ChatPortName = "identity" | "org" | "server" | "notify" | "diagnostics" | "prefs" | "navigation" | "windows" | "catalog" | "registry" | "chrome" | "feedback" | "routes" | "canvas" | "routing";
846
+ export type ChatPortName = "identity" | "org" | "server" | "notify" | "diagnostics" | "prefs" | "navigation" | "windows" | "catalog" | "registry" | "chrome" | "feedback" | "routes" | "canvas" | "routing" | "deviceTools";
808
847
  /** Every port present. `overridden` names the ports the host supplied itself. */
809
848
  export interface ResolvedChatHost {
810
849
  /** The host's client, or the no-database stand-in when it gave only a `source`. */
@@ -831,5 +870,7 @@ export interface ResolvedChatHost {
831
870
  routes: ChatRoutes;
832
871
  canvas: ChatCanvasPort;
833
872
  routing: ChatRoutingPort;
873
+ /** The host's on-device tool executor, or null. */
874
+ deviceTools: ChatDeviceToolsPort | null;
834
875
  overridden: ReadonlySet<ChatPortName>;
835
876
  }
@@ -16251,6 +16251,10 @@ export type ChatDatabase = {
16251
16251
  token: string;
16252
16252
  }[];
16253
16253
  };
16254
+ access_shadow_status: {
16255
+ Args: never;
16256
+ Returns: Json;
16257
+ };
16254
16258
  accessible_child_parents: {
16255
16259
  Args: {
16256
16260
  p_child_type: string;
@@ -17034,12 +17038,25 @@ export type ChatDatabase = {
17034
17038
  Args: never;
17035
17039
  Returns: boolean;
17036
17040
  };
17041
+ kernel_set_form_on: {
17042
+ Args: {
17043
+ p_person: string;
17044
+ };
17045
+ Returns: boolean;
17046
+ };
17037
17047
  kernel_shadow_on: {
17038
17048
  Args: {
17039
17049
  p_person: string;
17040
17050
  };
17041
17051
  Returns: boolean;
17042
17052
  };
17053
+ kernel_shadow_sweep: {
17054
+ Args: {
17055
+ p_people?: number;
17056
+ p_tables?: number;
17057
+ };
17058
+ Returns: Json;
17059
+ };
17043
17060
  lane_of: {
17044
17061
  Args: {
17045
17062
  p_resource_id: string;
@@ -19866,6 +19883,21 @@ export type ChatDatabase = {
19866
19883
  version_live: boolean;
19867
19884
  }[];
19868
19885
  };
19886
+ run_history: {
19887
+ Args: {
19888
+ p_agent_id?: string;
19889
+ p_desc?: boolean;
19890
+ p_limit?: number;
19891
+ p_mandate_key?: string;
19892
+ p_offset?: number;
19893
+ p_org_id?: string;
19894
+ p_sort?: string;
19895
+ p_status?: string;
19896
+ p_user_id?: string;
19897
+ p_view?: string;
19898
+ };
19899
+ Returns: Json;
19900
+ };
19869
19901
  sanitize_app_segment: {
19870
19902
  Args: {
19871
19903
  p_seg: string;
@@ -28302,6 +28334,26 @@ export type ChatDatabase = {
28302
28334
  };
28303
28335
  Returns: Json;
28304
28336
  };
28337
+ _drill_fine_agg_sql: {
28338
+ Args: {
28339
+ p_cnt: string;
28340
+ p_col: string;
28341
+ p_filter?: string;
28342
+ p_measure: Json;
28343
+ p_once: string;
28344
+ };
28345
+ Returns: string;
28346
+ };
28347
+ _drill_fine_ratio_sql: {
28348
+ Args: {
28349
+ p_cnt: string;
28350
+ p_filter?: string;
28351
+ p_measure: Json;
28352
+ p_once: string;
28353
+ p_prefix: string;
28354
+ };
28355
+ Returns: string;
28356
+ };
28305
28357
  _drill_fk: {
28306
28358
  Args: {
28307
28359
  p_column: string;
@@ -30411,6 +30463,10 @@ export type ChatDatabase = {
30411
30463
  Args: never;
30412
30464
  Returns: Json;
30413
30465
  };
30466
+ drill_def__account_roster: {
30467
+ Args: never;
30468
+ Returns: Json;
30469
+ };
30414
30470
  drill_def__agents_by_model: {
30415
30471
  Args: never;
30416
30472
  Returns: Json;
@@ -30427,10 +30483,46 @@ export type ChatDatabase = {
30427
30483
  Args: never;
30428
30484
  Returns: Json;
30429
30485
  };
30486
+ drill_def__app_log: {
30487
+ Args: never;
30488
+ Returns: Json;
30489
+ };
30490
+ drill_def__crm_deals: {
30491
+ Args: never;
30492
+ Returns: Json;
30493
+ };
30494
+ drill_def__cx_requests: {
30495
+ Args: never;
30496
+ Returns: Json;
30497
+ };
30498
+ drill_def__hr_timesheets: {
30499
+ Args: never;
30500
+ Returns: Json;
30501
+ };
30430
30502
  drill_def__kg_cost: {
30431
30503
  Args: never;
30432
30504
  Returns: Json;
30433
30505
  };
30506
+ drill_def__ops_issue_events: {
30507
+ Args: never;
30508
+ Returns: Json;
30509
+ };
30510
+ drill_def__rs_analyses: {
30511
+ Args: never;
30512
+ Returns: Json;
30513
+ };
30514
+ drill_def__rs_syntheses: {
30515
+ Args: never;
30516
+ Returns: Json;
30517
+ };
30518
+ drill_def__system_errors: {
30519
+ Args: never;
30520
+ Returns: Json;
30521
+ };
30522
+ drill_def__tool_calls: {
30523
+ Args: never;
30524
+ Returns: Json;
30525
+ };
30434
30526
  drill_def__tool_refetch: {
30435
30527
  Args: never;
30436
30528
  Returns: Json;
@@ -30443,6 +30535,10 @@ export type ChatDatabase = {
30443
30535
  Args: never;
30444
30536
  Returns: Json;
30445
30537
  };
30538
+ drill_def__write_failures: {
30539
+ Args: never;
30540
+ Returns: Json;
30541
+ };
30446
30542
  drill_definition_problems: {
30447
30543
  Args: {
30448
30544
  p_def: Json;
@@ -51605,6 +51701,22 @@ export type ChatDatabase = {
51605
51701
  };
51606
51702
  };
51607
51703
  Views: {
51704
+ _account_facts: {
51705
+ Row: {
51706
+ blocked: boolean | null;
51707
+ campaign: string | null;
51708
+ cost: number | null;
51709
+ created_at: string | null;
51710
+ identity_state: string | null;
51711
+ person_id: string | null;
51712
+ plan_key: string | null;
51713
+ plan_name: string | null;
51714
+ referrer_host: string | null;
51715
+ requests: number | null;
51716
+ traffic_kind: string | null;
51717
+ };
51718
+ Relationships: [];
51719
+ };
51608
51720
  _acquisition_facts: {
51609
51721
  Row: {
51610
51722
  blocked: boolean | null;
package/dist/host/db.js CHANGED
@@ -3,6 +3,7 @@ import {
3
3
  AuthSessionMissingError
4
4
  } from "@supabase/supabase-js";
5
5
  import { getChatHost } from "./configure.js";
6
+ import { decodeTokenClaims, isTokenOnlyClient } from "./defaults/identity.js";
6
7
  function createClient() {
7
8
  return getChatHost().db;
8
9
  }
@@ -35,6 +36,11 @@ function userFromClaims(claims) {
35
36
  };
36
37
  }
37
38
  async function getClaimsUser(client, jwt) {
39
+ if (isTokenOnlyClient(client)) {
40
+ const token = jwt ?? await getChatHost().identity.getAccessToken().catch(() => null);
41
+ const claims = token ? decodeTokenClaims(token) : null;
42
+ return { data: { user: userFromClaims(claims ?? void 0) }, error: null };
43
+ }
38
44
  const { data, error } = await client.auth.getClaims(jwt);
39
45
  if (error) {
40
46
  if (isSignedOutError(error)) return { data: { user: null }, error: null };
@@ -27,6 +27,11 @@ export interface LogClientErrorDiagnosticsOptions {
27
27
  /** Default: `process.env.NODE_ENV === "production"`. */
28
28
  persist?: boolean;
29
29
  flushDelayMs?: number;
30
+ /**
31
+ * Is someone signed in? The resolver passes the identity port's answer, so a token-only client
32
+ * works. Default (a db with an auth-js session): `db.auth.getSession()`.
33
+ */
34
+ signedIn?: () => Promise<boolean>;
30
35
  }
31
36
  export declare function isChatSourceApp(value: unknown): value is ChatSourceApp;
32
37
  export declare function createLogClientErrorDiagnostics(db: ChatDb, options: LogClientErrorDiagnosticsOptions): ChatDiagnosticsPort;
@@ -59,8 +59,7 @@ function createLogClientErrorDiagnostics(db, options) {
59
59
  if (queue.length === 0) return;
60
60
  let signedIn = false;
61
61
  try {
62
- const { data } = await db.auth.getSession();
63
- signedIn = Boolean(data.session?.user);
62
+ signedIn = options.signedIn ? await options.signedIn() : Boolean((await db.auth.getSession()).data.session?.user);
64
63
  } catch {
65
64
  signedIn = false;
66
65
  }
@@ -3,6 +3,32 @@
3
3
  * admin level from `admin.admins` (RLS: a person reads their own row). Starts
4
4
  * on first use, never at import, so SSR and tests that never ask pay nothing.
5
5
  */
6
+ import type { User } from "@supabase/supabase-js";
6
7
  import type { ChatDb, ChatDiagnosticsPort, ChatIdentity, ChatIdentityPort } from "../contract.js";
7
8
  export declare const SIGNED_OUT_IDENTITY: ChatIdentity;
8
9
  export declare function createDbIdentity(db: ChatDb, diagnostics: () => ChatDiagnosticsPort): ChatIdentityPort;
10
+ /**
11
+ * Is this a supabase-js client built with the `accessToken` option? Such a client has no auth-js
12
+ * session: touching `db.auth` throws by construction (matrx-local's custodian client is one). The
13
+ * probe reads the property inside a try, so it never throws itself.
14
+ */
15
+ export declare function isTokenOnlyClient(db: unknown): boolean;
16
+ /** A JWT's payload as an object (no verification — routing metadata only). */
17
+ export declare function decodeTokenClaims(token: string): Record<string, unknown> | null;
18
+ /**
19
+ * The person a token names, read from its claims — routing metadata, not verification (the
20
+ * server and RLS verify every call that token makes). `null` when the token names nobody.
21
+ */
22
+ export declare function userFromAccessToken(token: string | null): User | null;
23
+ /**
24
+ * Identity for a token-only client: the host hands the same `accessToken` function it gave
25
+ * supabase-js, and the package reads who is signed in from it. Every `getAccessToken()` re-reads
26
+ * the token, so a refresh or an account switch reaches the snapshot (and its subscribers) the next
27
+ * time anything asks; admin level is read through `db` exactly like the session default.
28
+ */
29
+ export declare function createTokenIdentity(db: ChatDb, getToken: () => Promise<string | null>, diagnostics: () => ChatDiagnosticsPort): ChatIdentityPort;
30
+ /**
31
+ * A token-only client with no `accessToken` function: nobody can be read. Signed out and settled
32
+ * (never a forever-loading screen), announced once with the remedy.
33
+ */
34
+ export declare function createUnreadableTokenIdentity(announce: (message: string) => void): ChatIdentityPort;
@@ -113,7 +113,139 @@ function createDbIdentity(db, diagnostics) {
113
113
  }
114
114
  };
115
115
  }
116
+ function isTokenOnlyClient(db) {
117
+ if (db === null || typeof db !== "object") return false;
118
+ try {
119
+ const auth = db.auth;
120
+ if (!auth) return true;
121
+ void auth.getSession;
122
+ return false;
123
+ } catch {
124
+ return true;
125
+ }
126
+ }
127
+ function decodeTokenClaims(token) {
128
+ const payload = token.split(".")[1];
129
+ if (!payload) return null;
130
+ try {
131
+ const normalized = payload.replace(/-/g, "+").replace(/_/g, "/");
132
+ const padded = normalized + "=".repeat((4 - normalized.length % 4) % 4);
133
+ const parsed = JSON.parse(
134
+ typeof atob === "function" ? atob(padded) : Buffer.from(padded, "base64").toString("utf8")
135
+ );
136
+ return parsed !== null && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : null;
137
+ } catch {
138
+ return null;
139
+ }
140
+ }
141
+ function userFromAccessToken(token) {
142
+ if (!token) return null;
143
+ const claims = decodeTokenClaims(token);
144
+ if (!claims || typeof claims.sub !== "string" || !claims.sub) return null;
145
+ const meta = claims.user_metadata;
146
+ const appMeta = claims.app_metadata;
147
+ return {
148
+ id: claims.sub,
149
+ email: typeof claims.email === "string" ? claims.email : void 0,
150
+ user_metadata: meta !== null && typeof meta === "object" ? meta : {},
151
+ app_metadata: appMeta !== null && typeof appMeta === "object" ? appMeta : {},
152
+ aud: typeof claims.aud === "string" ? claims.aud : "authenticated",
153
+ created_at: ""
154
+ };
155
+ }
156
+ function createTokenIdentity(db, getToken, diagnostics) {
157
+ let snapshot = SIGNED_OUT_IDENTITY;
158
+ let started = false;
159
+ let adminFor = null;
160
+ const listeners = /* @__PURE__ */ new Set();
161
+ function publish(next) {
162
+ if (snapshot.userId === next.userId && snapshot.accessToken === next.accessToken && snapshot.adminLevel === next.adminLevel && snapshot.authReady === next.authReady) {
163
+ return;
164
+ }
165
+ snapshot = next;
166
+ for (const listener of listeners) listener();
167
+ }
168
+ async function readAdminLevel(userId) {
169
+ try {
170
+ const { data, error } = await db.schema("admin").from("admins").select("level").eq("user_id", userId).maybeSingle();
171
+ if (error) throw error;
172
+ const level = data?.level;
173
+ return ADMIN_LEVELS.includes(level) ? level : null;
174
+ } catch (error) {
175
+ diagnostics().capture(error, { area: "identity", code: "admin-level-unreadable" });
176
+ return null;
177
+ }
178
+ }
179
+ async function refresh() {
180
+ let token;
181
+ try {
182
+ token = await getToken();
183
+ } catch (error) {
184
+ diagnostics().capture(error, { area: "identity", code: "access-token-unreadable" });
185
+ token = null;
186
+ }
187
+ const user = userFromAccessToken(token);
188
+ const level = user && user.id === snapshot.userId ? snapshot.adminLevel : null;
189
+ publish(identityFromUser(user, level, user ? token : null));
190
+ if (user && adminFor !== user.id) {
191
+ adminFor = user.id;
192
+ const read = await readAdminLevel(user.id);
193
+ if (snapshot.userId === user.id) publish(identityFromUser(user, read, snapshot.accessToken));
194
+ }
195
+ return user ? token : null;
196
+ }
197
+ function start() {
198
+ if (started) return;
199
+ started = true;
200
+ void refresh();
201
+ }
202
+ return {
203
+ current() {
204
+ start();
205
+ return snapshot;
206
+ },
207
+ subscribe(listener) {
208
+ start();
209
+ listeners.add(listener);
210
+ return () => {
211
+ listeners.delete(listener);
212
+ };
213
+ },
214
+ getAccessToken: refresh
215
+ };
216
+ }
217
+ function createUnreadableTokenIdentity(announce) {
218
+ const value = Object.freeze({ ...SIGNED_OUT_IDENTITY, authReady: true });
219
+ let said = false;
220
+ const say = () => {
221
+ if (said) return;
222
+ said = true;
223
+ announce(
224
+ "This chat's database client was built with an access-token function, so the chat cannot read who is signed in. Pass the same function to the chat host as `accessToken`."
225
+ );
226
+ };
227
+ return {
228
+ current() {
229
+ say();
230
+ return value;
231
+ },
232
+ subscribe() {
233
+ say();
234
+ return () => {
235
+ };
236
+ },
237
+ async getAccessToken() {
238
+ say();
239
+ return null;
240
+ }
241
+ };
242
+ }
116
243
  export {
117
244
  SIGNED_OUT_IDENTITY,
118
- createDbIdentity
245
+ createDbIdentity,
246
+ createTokenIdentity,
247
+ createUnreadableTokenIdentity,
248
+ decodeTokenClaims,
249
+ isTokenOnlyClient,
250
+ userFromAccessToken
119
251
  };
@@ -50,8 +50,25 @@ export declare function isNotAuthenticatedError(error: unknown): error is NotAut
50
50
  /** The signed-in person's id; throws `NotAuthenticatedError` when nobody is. */
51
51
  export declare function requireUserId(): string;
52
52
  /**
53
- * Does this client carry a signed-in session? A local read of the host's
54
- * `db.auth` (no network). For skipping reads only a signed-in person can make;
53
+ * The signed-in person's access token, or null — THE one session read in the package. It goes
54
+ * through the host's identity port, so a session client (auth-js) and a token-only client
55
+ * (`ChatHost.accessToken`) answer the same way; nothing else in the package touches `db.auth`
56
+ * (guard: `host/__tests__/session-reads-go-through-the-identity-port.test.ts`).
57
+ */
58
+ export declare function readSessionToken(): Promise<string | null>;
59
+ /** `readSessionToken` in the structural shape `@ai-matrx/data`'s session retry reads. */
60
+ export declare const hostSessionSource: {
61
+ getSession(): Promise<{
62
+ data: {
63
+ session: {
64
+ access_token: string;
65
+ } | null;
66
+ };
67
+ error: null;
68
+ }>;
69
+ };
70
+ /**
71
+ * Does this client carry a signed-in session? For skipping reads only a signed-in person can make;
55
72
  * "no session" means an empty result, never a way around a reader a guest needs.
56
73
  */
57
74
  export declare function hasBrowserSession(): Promise<boolean>;
@@ -40,11 +40,11 @@ function requireUserId() {
40
40
  if (!id) throw new NotAuthenticatedError();
41
41
  return id;
42
42
  }
43
- async function hasBrowserSession() {
44
- if (!chatHostHasDatabase()) return false;
45
- let db;
43
+ async function readSessionToken() {
44
+ if (!chatHostHasDatabase()) return null;
45
+ let host;
46
46
  try {
47
- db = getChatHost().db;
47
+ host = getChatHost();
48
48
  } catch (error) {
49
49
  announceOnce(
50
50
  "identity:no-host-session",
@@ -52,16 +52,22 @@ async function hasBrowserSession() {
52
52
  error?.message ?? error
53
53
  )}).`
54
54
  );
55
- return false;
55
+ return null;
56
56
  }
57
57
  try {
58
- const {
59
- data: { session }
60
- } = await db.auth.getSession();
61
- return Boolean(session);
58
+ return await host.identity.getAccessToken();
62
59
  } catch {
63
- return false;
60
+ return null;
61
+ }
62
+ }
63
+ const hostSessionSource = {
64
+ async getSession() {
65
+ const token = await readSessionToken();
66
+ return { data: { session: token ? { access_token: token } : null }, error: null };
64
67
  }
68
+ };
69
+ async function hasBrowserSession() {
70
+ return await readSessionToken() !== null;
65
71
  }
66
72
  async function isSignedOutVisitor() {
67
73
  return !getUserId() && !await hasBrowserSession();
@@ -70,8 +76,10 @@ export {
70
76
  NotAuthenticatedError,
71
77
  getUserId,
72
78
  hasBrowserSession,
79
+ hostSessionSource,
73
80
  isNotAuthenticatedError,
74
81
  isSignedOutVisitor,
82
+ readSessionToken,
75
83
  requireUserId,
76
84
  selectAccessToken,
77
85
  selectActiveUserName,
@@ -1,11 +1,11 @@
1
1
  import { createSessionRetry } from "@ai-matrx/data/db";
2
- import { supabase } from "./db.js";
2
+ import { hostSessionSource } from "./identity.js";
3
3
  import {
4
4
  isMissingSessionError,
5
5
  SessionUnavailableError
6
6
  } from "@ai-matrx/data/db";
7
7
  const runWithSessionRetry = createSessionRetry({
8
- auth: { getSession: () => supabase.auth.getSession() }
8
+ auth: hostSessionSource
9
9
  });
10
10
  export {
11
11
  SessionUnavailableError,
@@ -18,7 +18,8 @@ function slotComponent(name, Fallback) {
18
18
  const Wrapper = (props) => {
19
19
  const Impl = slots[name];
20
20
  if (!Impl) {
21
- reportUnregistered(name, Fallback ? "a plain stand-in is drawn" : "it renders nothing here");
21
+ const absent = !Fallback || Fallback.optionalAbsent;
22
+ reportUnregistered(name, absent ? "it renders nothing here" : "a plain stand-in is drawn");
22
23
  return Fallback ? createElement(Fallback, props) : null;
23
24
  }
24
25
  return createElement(Impl, props);
@@ -35,6 +36,12 @@ function unregisteredWidget(name) {
35
36
  UnregisteredWidget.displayName = `UnregisteredWidget(${name})`;
36
37
  return UnregisteredWidget;
37
38
  }
39
+ function optionalWidget(name) {
40
+ const AbsentOptionalWidget = () => null;
41
+ AbsentOptionalWidget.displayName = `AbsentOptionalWidget(${name})`;
42
+ AbsentOptionalWidget.optionalAbsent = true;
43
+ return AbsentOptionalWidget;
44
+ }
38
45
  function hostSlot(name, Fallback) {
39
46
  return slotComponent(name, Fallback);
40
47
  }
@@ -160,7 +167,7 @@ function slotFn(name, declared) {
160
167
  const CopyButtons = slotComponent("CopyButtons");
161
168
  const InfoHint = slotComponent("InfoHint");
162
169
  const AnswerValueView = slotComponent("AnswerValueView");
163
- const ErrorAlchemyMenu = slotComponent("ErrorAlchemyMenu", unregisteredWidget("ErrorAlchemyMenu"));
170
+ const ErrorAlchemyMenu = slotComponent("ErrorAlchemyMenu", optionalWidget("ErrorAlchemyMenu"));
164
171
  const ErrorNotice = slotComponent(
165
172
  "ErrorNotice",
166
173
  ({ title, message, error, actions, children, className }) => createElement(
@@ -187,16 +194,16 @@ const NonEditableContextMenu = slotComponent("NonEditableContextMenu", contentWi
187
194
  const TableChooser = slotComponent("TableChooser");
188
195
  const FileResourceChip = slotComponent("FileResourceChip");
189
196
  const ConnectorMark = slotComponent("ConnectorMark");
190
- const MessageFilesStrip = slotComponent("MessageFilesStrip", unregisteredWidget("MessageFilesStrip"));
191
- const RulebookNudge = slotComponent("RulebookNudge", unregisteredWidget("RulebookNudge"));
192
- const NegativeVerdictFollowUp = slotComponent("NegativeVerdictFollowUp", unregisteredWidget("NegativeVerdictFollowUp"));
193
- const SpeakerButton = slotComponent("SpeakerButton", unregisteredWidget("SpeakerButton"));
194
- const GmailReviewCard = slotComponent("GmailReviewCard", unregisteredWidget("GmailReviewCard"));
195
- const ShareButton = slotComponent("ShareButton", unregisteredWidget("ShareButton"));
196
- const ShareModal = slotComponent("ShareModal", unregisteredWidget("ShareModal"));
197
- const ReviewAnswersLink = slotComponent("ReviewAnswersLink", unregisteredWidget("ReviewAnswersLink"));
198
- const RecordChangeApprovalCard = slotComponent("RecordChangeApprovalCard", unregisteredWidget("RecordChangeApprovalCard"));
199
- const AttachedResourcesSection = slotComponent("AttachedResourcesSection", unregisteredWidget("AttachedResourcesSection"));
197
+ const MessageFilesStrip = slotComponent("MessageFilesStrip", optionalWidget("MessageFilesStrip"));
198
+ const RulebookNudge = slotComponent("RulebookNudge", optionalWidget("RulebookNudge"));
199
+ const NegativeVerdictFollowUp = slotComponent("NegativeVerdictFollowUp", optionalWidget("NegativeVerdictFollowUp"));
200
+ const SpeakerButton = slotComponent("SpeakerButton", optionalWidget("SpeakerButton"));
201
+ const GmailReviewCard = slotComponent("GmailReviewCard", optionalWidget("GmailReviewCard"));
202
+ const ShareButton = slotComponent("ShareButton", optionalWidget("ShareButton"));
203
+ const ShareModal = slotComponent("ShareModal", optionalWidget("ShareModal"));
204
+ const ReviewAnswersLink = slotComponent("ReviewAnswersLink", optionalWidget("ReviewAnswersLink"));
205
+ const RecordChangeApprovalCard = slotComponent("RecordChangeApprovalCard", optionalWidget("RecordChangeApprovalCard"));
206
+ const AttachedResourcesSection = slotComponent("AttachedResourcesSection", optionalWidget("AttachedResourcesSection"));
200
207
  const ConnectorPromptHost = slotComponent("ConnectorPromptHost");
201
208
  const WebpageSnapshotView = slotComponent("WebpageSnapshotView", DefaultWebpageSnapshotView);
202
209
  function hostFn(name, fallback) {
@@ -230,16 +237,16 @@ const traceWarRoomRenderPath = slotFn("traceWarRoomRenderPath", () => void 0);
230
237
  const isWarRoomThreadAgentSurface = slotFn("isWarRoomThreadAgentSurface", () => false);
231
238
  const useOpenCloudBrowserCanvas = slotFn("useOpenCloudBrowserCanvas", () => () => void 0);
232
239
  const cloudBrowserCanvasSourceId = slotFn("cloudBrowserCanvasSourceId", (conversationId) => `cloud-browser:${conversationId}`);
233
- const SystemInstructionModal = slotComponent("SystemInstructionModal", unregisteredWidget("SystemInstructionModal"));
240
+ const SystemInstructionModal = slotComponent("SystemInstructionModal", optionalWidget("SystemInstructionModal"));
234
241
  const WindowPanel = slotComponent("WindowPanel", DefaultWindowPanel);
235
242
  const FullScreenOverlay = slotComponent("FullScreenOverlay", DefaultFullScreenOverlay);
236
- const ResourcePickerWindow = slotComponent("ResourcePickerWindow", unregisteredWidget("ResourcePickerWindow"));
237
- const ResourcePickerMenu = slotComponent("ResourcePickerMenu", unregisteredWidget("ResourcePickerMenu"));
238
- const FilesResourcePicker = slotComponent("FilesResourcePicker", unregisteredWidget("FilesResourcePicker"));
239
- const NotePickerPopover = slotComponent("NotePickerPopover", unregisteredWidget("NotePickerPopover"));
243
+ const ResourcePickerWindow = slotComponent("ResourcePickerWindow", optionalWidget("ResourcePickerWindow"));
244
+ const ResourcePickerMenu = slotComponent("ResourcePickerMenu", optionalWidget("ResourcePickerMenu"));
245
+ const FilesResourcePicker = slotComponent("FilesResourcePicker", optionalWidget("FilesResourcePicker"));
246
+ const NotePickerPopover = slotComponent("NotePickerPopover", optionalWidget("NotePickerPopover"));
240
247
  const SmartInputMessageTemplatePicker = slotComponent(
241
248
  "SmartInputMessageTemplatePicker",
242
- unregisteredWidget("SmartInputMessageTemplatePicker")
249
+ optionalWidget("SmartInputMessageTemplatePicker")
243
250
  );
244
251
  const flattenResourcePickerItems = slotFn("flattenResourcePickerItems", defaultResourcePickerItems);
245
252
  const useRunControlCounts = slotFn("useRunControlCounts", () => ({}));
@@ -253,8 +260,8 @@ const emitFullScreenEditorSave = slotFn(
253
260
  () => Promise.reject(new Error("This host has no full-screen editor save target"))
254
261
  );
255
262
  const FileRagBadge = slotComponent("FileRagBadge");
256
- const MediaAttachmentThumbnail = slotComponent("MediaAttachmentThumbnail", unregisteredWidget("MediaAttachmentThumbnail"));
257
- const UnifiedImageBlockRenderer = slotComponent("UnifiedImageBlockRenderer", unregisteredWidget("UnifiedImageBlockRenderer"));
263
+ const MediaAttachmentThumbnail = slotComponent("MediaAttachmentThumbnail", optionalWidget("MediaAttachmentThumbnail"));
264
+ const UnifiedImageBlockRenderer = slotComponent("UnifiedImageBlockRenderer", optionalWidget("UnifiedImageBlockRenderer"));
258
265
  const MicrophoneIconButton = slotComponent("MicrophoneIconButton");
259
266
  const TranscriptionLoader = slotComponent("TranscriptionLoader");
260
267
  const MicDeviceMenu = slotComponent("MicDeviceMenu");