@ai-matrx/chat 0.3.23 → 0.3.24

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 (41) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/dist/agents/components/chat/ChatConversationSurface.js +2 -2
  3. package/dist/agents/components/chat/ChatRunHeader.js +3 -5
  4. package/dist/agents/components/chat/PinnedAgentsSection.js +2 -2
  5. package/dist/agents/components/context-preview/ContextCompareView.js +5 -12
  6. package/dist/agents/components/inputs/smart-input/composer/useComposerAgent.js +4 -5
  7. package/dist/agents/components/run/AgentRunnerPage.js +3 -3
  8. package/dist/agents/hooks/useAgentModelLabel.js +6 -2
  9. package/dist/agents/hooks/useAgentRunSurfaceScope.js +3 -4
  10. package/dist/agents/identity/agent-catalog-lists.d.ts +45 -0
  11. package/dist/agents/identity/agent-catalog-lists.js +128 -0
  12. package/dist/agents/identity/agent-identity.d.ts +34 -0
  13. package/dist/agents/identity/agent-identity.js +127 -0
  14. package/dist/agents/identity/run-tier-store.d.ts +15 -0
  15. package/dist/agents/identity/run-tier-store.js +23 -0
  16. package/dist/agents/redux/agent-definition/list-mirror.d.ts +139 -0
  17. package/dist/agents/redux/agent-definition/list-mirror.js +157 -0
  18. package/dist/agents/redux/agent-definition/selectors.d.ts +8 -8
  19. package/dist/agents/redux/agent-definition/slice.d.ts +1 -0
  20. package/dist/agents/redux/agent-definition/slice.js +1 -0
  21. package/dist/agents/redux/agent-definition/thunks.d.ts +10 -205
  22. package/dist/agents/redux/agent-definition/thunks.js +43 -230
  23. package/dist/agents/redux/agent-filter/selectors.d.ts +9 -0
  24. package/dist/agents/redux/agent-filter/selectors.js +25 -23
  25. package/dist/agents/redux/agent-filter/useAgentIdsForFilter.d.ts +6 -0
  26. package/dist/agents/redux/agent-filter/useAgentIdsForFilter.js +14 -0
  27. package/dist/agents/redux/conversation-history/selectors.js +2 -3
  28. package/dist/agents/redux/execution-system/thunks/launch-agent-execution.thunk.js +2 -2
  29. package/dist/agents/redux/execution-system/thunks/load-conversation.thunk.js +2 -2
  30. package/dist/agents/redux/execution-system/thunks/run-headless-agent-json.js +2 -2
  31. package/dist/agents/redux/execution-system/utils/output-contract-guard.js +2 -1
  32. package/dist/agents/types/agent-definition.types.d.ts +8 -18
  33. package/dist/cx-chat/components/ChatMobileAgentName.js +4 -7
  34. package/dist/cx-chat/components/ChatSidebarClient.js +2 -2
  35. package/dist/cx-chat/components/agent/AgentPickerSheet.js +10 -11
  36. package/dist/cx-chat/hooks/useChatCatalogueInit.js +4 -7
  37. package/dist/cx-chat/hooks/useInstanceBootstrap.js +4 -5
  38. package/dist/host/db-types.d.ts +86 -0
  39. package/dist/window-panels/windows/agents/AgentRunWindow.js +4 -6
  40. package/dist/window-panels/windows/agents/RunControlsWindow.js +2 -4
  41. package/package.json +4 -4
@@ -8,34 +8,25 @@ import {
8
8
  hasField
9
9
  } from "@ai-matrx/agents/field-flags";
10
10
  import {
11
- supabase
12
- } from "../../../host/db.js";
13
- import { getAgentCatalog } from "../../../host/ui-slots.js";
14
- import {
15
- runWithSessionRetry
16
- } from "../../../host/session-retry.js";
11
+ RunTierNotReadableError,
12
+ RunTierReadError,
13
+ RunTierTimeoutError
14
+ } from "@ai-matrx/agents/runtime";
15
+ import { getRunTierStore } from "../../identity/run-tier-store.js";
17
16
  import {
18
17
  pgErrorToError
19
18
  } from "@ai-matrx/data";
20
19
  import {
21
20
  agentNameTakenError
22
21
  } from "./agentNameTaken.js";
23
- import {
24
- withRetry
25
- } from "@ai-matrx/data/net";
26
22
  import {
27
23
  ConnectTimeoutError
28
24
  } from "@ai-matrx/data/net";
29
- import {
30
- readFavoriteIds
31
- } from "../../../context/sources/scopes.js";
32
25
  import {
33
26
  mergePartialAgent,
34
27
  setAgentFetchStatus,
35
28
  setAgentLoading,
36
- setAgentError,
37
- setAgentsStatus,
38
- setAgentsError
29
+ setAgentError
39
30
  } from "./slice.js";
40
31
  import {
41
32
  selectAgentReadyForExecution,
@@ -48,6 +39,15 @@ import {
48
39
  isSignedOutVisitor,
49
40
  NotAuthenticatedError
50
41
  } from "../../../host/identity.js";
42
+ function runTierFailure(agentId, e) {
43
+ if (e instanceof RunTierNotReadableError) return agentNotReadableError(agentId);
44
+ if (e instanceof RunTierTimeoutError) return new ConnectTimeoutError(e.timeoutMs);
45
+ if (e instanceof RunTierReadError && e.pgError) {
46
+ const pg = { message: e.pgError.message, code: e.pgError.code ?? "", details: e.pgError.details ?? "", hint: e.pgError.hint ?? "" };
47
+ return agentNameTakenError(pg) ?? pgErrorToError(pg);
48
+ }
49
+ return e instanceof Error ? e : new Error(String(e));
50
+ }
51
51
  class AgentToolAssignmentError extends Error {
52
52
  constructor(message) {
53
53
  super(message);
@@ -131,8 +131,6 @@ function sameStringArray(a, b) {
131
131
  function isToolAssignmentPhantom(current, base) {
132
132
  return sameStringArray(current.tools, base.tools) && current.model_id === base.model_id && current.created_by === base.created_by && current.organization_id === base.organization_id && current.is_archived === base.is_archived;
133
133
  }
134
- const AGENT_LIST_RPC_PAGE_SIZE = 100;
135
- const AGENT_SEARCH_LIMIT = 200;
136
134
  const REGISTRY_ACCESS_LEVELS = [
137
135
  "owner",
138
136
  "admin",
@@ -145,124 +143,6 @@ function toRegistryAccessLevel(value) {
145
143
  (level) => level === value
146
144
  ) ?? null;
147
145
  }
148
- function mergeAgentSummaries(dispatch, rows) {
149
- for (const row of rows) {
150
- dispatch(
151
- mergePartialAgent({
152
- id: row.id,
153
- name: row.name ?? void 0,
154
- description: row.description,
155
- category: row.category,
156
- tags: row.tags,
157
- agentType: row.agentType,
158
- modelId: row.modelId,
159
- isActive: row.isActive,
160
- isArchived: row.isArchived,
161
- // No `isFavorite`: the catalog row's column is retired — stars are
162
- // overlaid from platform.user_entity_state by `overlayAgentFavorites`.
163
- // The registry spells "absent" as `undefined` where the DB row spells
164
- // it `null`. Five fields differ; this is the vocabulary translation
165
- // at the seam, not a value change.
166
- createdBy: row.createdBy ?? void 0,
167
- organizationId: row.organizationId ?? void 0,
168
- taskId: row.taskId,
169
- sourceAgentId: row.sourceAgentId,
170
- createdAt: row.createdAt ?? void 0,
171
- updatedAt: row.updatedAt ?? void 0,
172
- isVersion: false,
173
- isOwner: row.isOwner,
174
- // `access_level` is a DB-owned vocabulary the package deliberately
175
- // leaves open (an unknown level renders as itself). This registry's
176
- // field is a closed union, so an unrecognised level is stored as
177
- // `null` — absent, never silently coerced into a wrong level.
178
- accessLevel: toRegistryAccessLevel(row.accessLevel),
179
- sharedByEmail: row.sharedByEmail ?? void 0,
180
- // The class pin; a catalog read that lacks the column leaves it absent.
181
- ...row.offeringId === void 0 ? {} : { listOfferingPin: row.offeringId }
182
- })
183
- );
184
- dispatch(setAgentFetchStatus({ id: row.id, status: "list" }));
185
- }
186
- }
187
- async function overlayAgentFavorites(dispatch, ids) {
188
- if (ids.length === 0) return;
189
- const favorites = await readFavoriteIds("agent", ids);
190
- if (!favorites) return;
191
- for (const id of ids) {
192
- dispatch(mergePartialAgent({ id, isFavorite: favorites.has(id) }));
193
- }
194
- }
195
- const overlayAgentFavoritesThunk = createAsyncThunk("agentDefinition/overlayFavorites", async (ids, { dispatch }) => {
196
- await overlayAgentFavorites(dispatch, ids);
197
- });
198
- const fetchAgentsList = createAsyncThunk(
199
- "agentDefinition/fetchList",
200
- async (_, { dispatch }) => {
201
- dispatch(setAgentsStatus("loading"));
202
- dispatch(setAgentsError(null));
203
- const catalog = getAgentCatalog();
204
- try {
205
- await catalog.ensureLoaded();
206
- } catch (e) {
207
- dispatch(setAgentsError(e instanceof Error ? e.message : String(e)));
208
- dispatch(setAgentsStatus("failed"));
209
- throw e;
210
- }
211
- const rows = catalog.getState().rows;
212
- mergeAgentSummaries(dispatch, rows);
213
- await overlayAgentFavorites(
214
- dispatch,
215
- rows.map((r) => r.id)
216
- );
217
- dispatch(setAgentsStatus("succeeded"));
218
- }
219
- );
220
- const searchAgentsServer = createAsyncThunk(
221
- "agentDefinition/searchServer",
222
- async ({ query, deep = false }, { dispatch }) => {
223
- const q = query.trim();
224
- if (!q) return { ids: [], deep, query: q };
225
- const catalog = getAgentCatalog();
226
- const ids = await catalog.searchServer(q, deep);
227
- const state = catalog.getState();
228
- const hits = ids.map((id) => state.byId[id]).filter((r) => !!r);
229
- mergeAgentSummaries(dispatch, hits);
230
- await overlayAgentFavorites(
231
- dispatch,
232
- hits.map((r) => r.id)
233
- );
234
- return { ids, deep, query: q };
235
- }
236
- );
237
- const fetchAgentsListFull = createAsyncThunk(
238
- "agentDefinition/fetchListFull",
239
- async (_, { dispatch }) => {
240
- const catalog = getAgentCatalog();
241
- await catalog.ensureLoaded();
242
- const rows = catalog.getState().rows;
243
- mergeAgentSummaries(dispatch, rows);
244
- await overlayAgentFavorites(
245
- dispatch,
246
- rows.map((r) => r.id)
247
- );
248
- }
249
- );
250
- const ensureAgentIdentity = createAsyncThunk(
251
- "agentDefinition/ensureIdentity",
252
- async (agentId, { dispatch, getState }) => {
253
- if (getState().agentDefinition.agents?.[agentId]?.name) return;
254
- const catalog = getAgentCatalog();
255
- let row = catalog.getState().byId[agentId];
256
- if (!row) {
257
- await catalog.ensureLoaded();
258
- row = catalog.getState().byId[agentId];
259
- }
260
- if (row) {
261
- mergeAgentSummaries(dispatch, [row]);
262
- await overlayAgentFavorites(dispatch, [row.id]);
263
- }
264
- }
265
- );
266
146
  const fetchAgentRunTier = createAsyncThunk(
267
147
  "agentDefinition/fetchRunTier",
268
148
  async (arg, { dispatch, getState }) => {
@@ -273,36 +153,16 @@ const fetchAgentRunTier = createAsyncThunk(
273
153
  return;
274
154
  if (await isSignedOutVisitor()) throw new NotAuthenticatedError();
275
155
  dispatch(setAgentLoading({ id: agentId, loading: true }));
276
- const { data, error } = await withRetry(
277
- () => new Promise((resolve, reject) => {
278
- const timer = setTimeout(() => {
279
- reject(new ConnectTimeoutError(15e3));
280
- }, 15e3);
281
- supabase.rpc("agx_get_run_tier", { p_agent_id: agentId }).then(
282
- (result) => {
283
- clearTimeout(timer);
284
- resolve(result);
285
- },
286
- (err) => {
287
- clearTimeout(timer);
288
- reject(err);
289
- }
290
- );
291
- }),
292
- { attempts: 2, initialDelayMs: 400 }
293
- );
294
- dispatch(setAgentLoading({ id: agentId, loading: false }));
295
- if (error) {
296
- dispatch(setAgentError({ id: agentId, error: error.message }));
297
- throw agentNameTakenError(error) ?? pgErrorToError(error);
298
- }
299
- const raw = Array.isArray(data) ? data[0] : data;
300
- if (!raw) {
301
- const refusal = agentNotReadableError(agentId);
302
- dispatch(setAgentError({ id: agentId, error: refusal.message }));
303
- throw refusal;
156
+ let row;
157
+ try {
158
+ row = await getRunTierStore().loadRunTier(agentId, { force: true });
159
+ } catch (e) {
160
+ dispatch(setAgentLoading({ id: agentId, loading: false }));
161
+ const failure = runTierFailure(agentId, e);
162
+ dispatch(setAgentError({ id: agentId, error: failure.message }));
163
+ throw failure;
304
164
  }
305
- const row = raw;
165
+ dispatch(setAgentLoading({ id: agentId, loading: false }));
306
166
  dispatch(
307
167
  mergePartialAgent({
308
168
  id: row.id,
@@ -333,18 +193,13 @@ const fetchAgentRunControls = createAsyncThunk(
333
193
  async (agentId, { dispatch, getState }) => {
334
194
  if (selectAgentRunControlsReady(getState(), agentId)) return;
335
195
  if (await isSignedOutVisitor()) throw new NotAuthenticatedError();
336
- const { data, error } = await supabase.rpc("agx_get_run_controls", {
337
- p_agent_id: agentId
338
- });
339
- if (error) {
340
- dispatch(setAgentError({ id: agentId, error: error.message }));
341
- throw pgErrorToError(error);
342
- }
343
- const row = Array.isArray(data) ? data[0] : data;
344
- if (!row) {
345
- const refusal = agentNotReadableError(agentId);
346
- dispatch(setAgentError({ id: agentId, error: refusal.message }));
347
- throw refusal;
196
+ let row;
197
+ try {
198
+ row = await getRunTierStore().loadRunControls(agentId, { force: true });
199
+ } catch (e) {
200
+ const failure = runTierFailure(agentId, e);
201
+ dispatch(setAgentError({ id: agentId, error: failure.message }));
202
+ throw failure;
348
203
  }
349
204
  const loaded = getState().agentDefinition.agents?.[agentId]?._loadedFields;
350
205
  const missing = (field) => !loaded || !hasField(loaded, field);
@@ -360,51 +215,11 @@ const fetchAgentRunControls = createAsyncThunk(
360
215
  );
361
216
  }
362
217
  );
363
- const fetchSharedAgents = createAsyncThunk("agentDefinition/fetchShared", async (_, { dispatch }) => {
364
- const { data, error } = await supabase.rpc("agx_get_shared_with_me");
365
- if (error) throw pgErrorToError(error);
366
- const rows = data ?? [];
367
- for (const row of rows) {
368
- dispatch(
369
- mergePartialAgent({
370
- id: row.id,
371
- name: row.name ?? void 0,
372
- description: row.description,
373
- category: row.category,
374
- tags: row.tags ?? [],
375
- agentType: row.agent_type,
376
- isVersion: false,
377
- createdAt: row.created_at,
378
- updatedAt: row.updated_at,
379
- isOwner: false,
380
- accessLevel: row.permission_level,
381
- sharedByEmail: row.owner_email
382
- })
383
- );
384
- dispatch(setAgentFetchStatus({ id: row.id, status: "list" }));
385
- }
386
- await overlayAgentFavorites(
387
- dispatch,
388
- rows.map((r) => r.id)
389
- );
390
- return rows;
391
- });
392
- const fetchSharedAgentsForChat = createAsyncThunk("agentDefinition/fetchSharedForChat", async () => {
393
- const { data, error } = await supabase.rpc("agx_get_shared_for_chat");
394
- if (error) throw pgErrorToError(error);
395
- return data ?? [];
396
- });
397
218
  true;
398
219
  const fetchAgentAccessLevel = createAsyncThunk("agentDefinition/fetchAccessLevel", async (agentId, { dispatch }) => {
399
- const { data, error } = await runWithSessionRetry(
400
- () => supabase.rpc("agx_get_access_level", {
401
- p_agent_id: agentId
402
- })
403
- );
404
- if (error) throw pgErrorToError(error);
405
- const rawRow = Array.isArray(data) ? data[0] : data;
406
- if (!rawRow) throw new Error(`No access level returned for agent ${agentId}`);
407
- const row = rawRow;
220
+ const row = await getRunTierStore().loadAccessLevel(agentId, { force: true }).catch((e) => {
221
+ throw runTierFailure(agentId, e);
222
+ });
408
223
  dispatch(
409
224
  mergePartialAgent({
410
225
  id: agentId,
@@ -416,15 +231,15 @@ const fetchAgentAccessLevel = createAsyncThunk("agentDefinition/fetchAccessLevel
416
231
  );
417
232
  return row;
418
233
  });
419
- function isChatListStale() {
420
- return getAgentCatalog().isStale();
421
- }
422
- const initializeChatAgents = createAsyncThunk("agentDefinition/initializeChatAgents", async (arg, { dispatch }) => {
423
- const force = arg?.force ?? false;
424
- const catalog = getAgentCatalog();
425
- await catalog.ensureLoaded(force ? { force: true } : void 0);
426
- mergeAgentSummaries(dispatch, catalog.getState().rows);
427
- });
234
+ import {
235
+ overlayAgentFavoritesThunk,
236
+ fetchAgentsList,
237
+ searchAgentsServer,
238
+ fetchAgentsListFull,
239
+ ensureAgentIdentity,
240
+ isChatListStale,
241
+ initializeChatAgents
242
+ } from "./list-mirror.js";
428
243
  export {
429
244
  AgentToolAssignmentConflictError,
430
245
  AgentToolAssignmentError,
@@ -439,8 +254,6 @@ export {
439
254
  fetchAgentRunTier,
440
255
  fetchAgentsList,
441
256
  fetchAgentsListFull,
442
- fetchSharedAgents,
443
- fetchSharedAgentsForChat,
444
257
  initializeChatAgents,
445
258
  isAvailableToolModel,
446
259
  isChatListStale,
@@ -338,6 +338,15 @@ export declare const makeSelectAgentsForFilter: () => ((state: {
338
338
  memoize: typeof import("reselect").weakMapMemoize;
339
339
  argsMemoize: typeof import("reselect").weakMapMemoize;
340
340
  };
341
+ /** The fields a filter reads — satisfied by a catalog `AgentSummary` and a registry record alike. */
342
+ export interface FilterableAgent {
343
+ id: string;
344
+ isFavorite: boolean;
345
+ tags?: string[] | null;
346
+ category?: string | null;
347
+ }
348
+ /** Pure: the agents matching a stored `CodeAgentFilter`. `null` / "all" returns the input. */
349
+ export declare function filterAgentsForFilter<T extends FilterableAgent>(agents: T[], filter: CodeAgentFilter | null | undefined): T[];
341
350
  /**
342
351
  * Convenience shorthand: returns just the ids that match the filter. Useful
343
352
  * for passing into `ConversationHistorySidebar`'s `agentIds` prop.
@@ -5,31 +5,32 @@ const makeSelectAgentsForFilter = () => createSelector(
5
5
  selectActiveAgents,
6
6
  (_state, filter) => filter
7
7
  ],
8
- (agents, filter) => {
9
- if (!filter || filter.mode === "all") return agents;
10
- switch (filter.mode) {
11
- case "favorites":
12
- return agents.filter((a) => a.isFavorite);
13
- case "tags": {
14
- if (filter.tags.length === 0) return agents;
15
- const wanted = new Set(filter.tags);
16
- return agents.filter((a) => a.tags?.some((t) => wanted.has(t)));
17
- }
18
- case "categories": {
19
- if (filter.categories.length === 0) return agents;
20
- const wanted = new Set(filter.categories);
21
- return agents.filter((a) => a.category && wanted.has(a.category));
22
- }
23
- case "explicit": {
24
- if (filter.agentIds.length === 0) return agents;
25
- const wanted = new Set(filter.agentIds);
26
- return agents.filter((a) => wanted.has(a.id));
27
- }
28
- default:
29
- return agents;
8
+ (agents, filter) => filterAgentsForFilter(agents, filter)
9
+ );
10
+ function filterAgentsForFilter(agents, filter) {
11
+ if (!filter || filter.mode === "all") return agents;
12
+ switch (filter.mode) {
13
+ case "favorites":
14
+ return agents.filter((a) => a.isFavorite);
15
+ case "tags": {
16
+ if (filter.tags.length === 0) return agents;
17
+ const wanted = new Set(filter.tags);
18
+ return agents.filter((a) => a.tags?.some((t) => wanted.has(t)));
19
+ }
20
+ case "categories": {
21
+ if (filter.categories.length === 0) return agents;
22
+ const wanted = new Set(filter.categories);
23
+ return agents.filter((a) => a.category && wanted.has(a.category));
30
24
  }
25
+ case "explicit": {
26
+ if (filter.agentIds.length === 0) return agents;
27
+ const wanted = new Set(filter.agentIds);
28
+ return agents.filter((a) => wanted.has(a.id));
29
+ }
30
+ default:
31
+ return agents;
31
32
  }
32
- );
33
+ }
33
34
  const makeSelectAgentIdsForFilter = () => {
34
35
  const selectAgents = makeSelectAgentsForFilter();
35
36
  return createSelector(
@@ -80,6 +81,7 @@ export {
80
81
  conversationHistoryAgentIds,
81
82
  describeEmptyFilter,
82
83
  describeFilter,
84
+ filterAgentsForFilter,
83
85
  makeSelectAgentIdsForFilter,
84
86
  makeSelectAgentsForFilter
85
87
  };
@@ -0,0 +1,6 @@
1
+ import type { CodeAgentFilter } from "../../../host/prefs.js";
2
+ /**
3
+ * The agent ids a stored `CodeAgentFilter` matches, over the ONE agent catalog (active, unarchived
4
+ * agents). "All" answers `[]` — the history sidebar's "every accessible conversation" sentinel.
5
+ */
6
+ export declare function useAgentIdsForFilter(filter: CodeAgentFilter | null | undefined): string[];
@@ -0,0 +1,14 @@
1
+ "use client";
2
+ import { useMemo } from "react";
3
+ import { isActiveAgent, useCatalogAgents } from "../../identity/agent-catalog-lists.js";
4
+ import { conversationHistoryAgentIds, filterAgentsForFilter } from "./selectors.js";
5
+ function useAgentIdsForFilter(filter) {
6
+ const rows = useCatalogAgents();
7
+ return useMemo(() => {
8
+ const matching = filterAgentsForFilter(rows.filter(isActiveAgent), filter);
9
+ return conversationHistoryAgentIds(filter, matching.map((a) => a.id));
10
+ }, [rows, filter]);
11
+ }
12
+ export {
13
+ useAgentIdsForFilter
14
+ };
@@ -1,6 +1,5 @@
1
1
  import { createSelector } from "@reduxjs/toolkit";
2
2
  import { idMatchesQuery } from "@ai-matrx/kit/search-scoring";
3
- import { selectAgentById } from "../agent-definition/selectors.js";
4
3
  import {
5
4
  defaultScopeState,
6
5
  rowMatchesArchiveView
@@ -10,6 +9,7 @@ import {
10
9
  normalizeLanes
11
10
  } from "./lanes.js";
12
11
  import { selectStoredConversationLanes } from "../../../host/prefs.js";
12
+ import { readAgentName } from "../../identity/agent-identity.js";
13
13
  const selectConversationHistory = (state) => state.conversationHistory;
14
14
  const selectSourceFacets = (state) => state.conversationHistory.sourceFacets;
15
15
  const selectLaneCounts = createSelector(
@@ -120,8 +120,7 @@ const makeSelectGroupedByAgent = (scopeId) => {
120
120
  for (const [agentId, agentItems] of buckets.entries()) {
121
121
  let label = "Unknown agent";
122
122
  if (agentId) {
123
- const agent = selectAgentById(state, agentId);
124
- label = agent?.name ?? agentId.slice(0, 8);
123
+ label = readAgentName(state, agentId) ?? agentId.slice(0, 8);
125
124
  }
126
125
  result.push({ agentId, label, items: agentItems });
127
126
  }
@@ -24,7 +24,6 @@ import {
24
24
  import { withSurfaceDocumentEvidence } from "../../../../surfaces/utils/document-evidence.js";
25
25
  import { alwaysOnSurfaceKeys } from "../../../../surfaces/utils/always-on-context.js";
26
26
  import {
27
- ensureAgentIdentity,
28
27
  fetchAgentRunTier
29
28
  } from "../../agent-definition/thunks.js";
30
29
  import {
@@ -81,6 +80,7 @@ import {
81
80
  resolveLaunchMappingLayers,
82
81
  resolvePerLaunchMappings
83
82
  } from "./surface-scope-mapping.js";
83
+ import { ensureAgentCatalog } from "../../../identity/agent-identity.js";
84
84
  function hasSignedInSession(state) {
85
85
  return Boolean(state.userAuth?.accessToken);
86
86
  }
@@ -217,7 +217,7 @@ const launchAgentExecution = createAsyncThunk("instances/launch", async (options
217
217
  let bindingAutoRunGap = [];
218
218
  if (agentId && !shortcutId) {
219
219
  if (hasSignedInSession(getState())) {
220
- void dispatch(ensureAgentIdentity(agentId));
220
+ void ensureAgentCatalog();
221
221
  }
222
222
  const preState = getState();
223
223
  const payload = selectAgentRunTier(preState, agentId);
@@ -1,4 +1,3 @@
1
- import { ensureAgentIdentity } from "../../agent-definition/thunks.js";
2
1
  import { createAsyncThunk } from "@reduxjs/toolkit";
3
2
  import { supabase } from "../../../../host/db.js";
4
3
  import {
@@ -70,6 +69,7 @@ import {
70
69
  } from "./conversation-bundle.js";
71
70
  import { getClaimsUser } from "../../../../host/db.js";
72
71
  import { canActOn } from "../../../../ui/canActOn.js";
72
+ import { ensureAgentCatalog } from "../../../identity/agent-identity.js";
73
73
  const NOT_READABLE_DETAIL = "it may have been removed, or your sign-in lost access to it";
74
74
  const loadConversation = createAsyncThunk(
75
75
  "conversations/load",
@@ -326,7 +326,7 @@ const loadConversation = createAsyncThunk(
326
326
  }
327
327
  let baseInputCapabilities = {};
328
328
  if (conv.initial_agent_id) {
329
- void dispatch(ensureAgentIdentity(conv.initial_agent_id));
329
+ void ensureAgentCatalog();
330
330
  try {
331
331
  baseInputCapabilities = await fetchInputCapabilitiesSnapshot({
332
332
  agentId: conv.initial_agent_id,
@@ -23,12 +23,12 @@ import {
23
23
  import { setUserInputMessageParts } from "../instance-user-input/instance-user-input.slice.js";
24
24
  import { launchAgentExecution } from "./launch-agent-execution.thunk.js";
25
25
  import { resolveMandate } from "../../../../mandates/service.js";
26
- import { selectAgentName } from "../../agent-definition/selectors.js";
27
26
  import {
28
27
  judgeDeclaredFlattening,
29
28
  judgeHarvestedFlattening
30
29
  } from "./structured-output-flattening.js";
31
30
  import { executeInstance } from "./execute-instance.thunk.js";
31
+ import { readAgentName } from "../../../identity/agent-identity.js";
32
32
  function livePosture(onConversationCreated) {
33
33
  return onConversationCreated ? { displayMode: "direct", keepInstance: true, onConversationCreated } : {};
34
34
  }
@@ -140,7 +140,7 @@ async function failWarnedOutputMissingKeys(getState, opts, result) {
140
140
  const holderId = warning.holderId ?? resolved.agentId;
141
141
  let holderName;
142
142
  try {
143
- holderName = holderId ? selectAgentName(getState(), holderId) ?? void 0 : void 0;
143
+ holderName = holderId ? readAgentName(getState(), holderId) ?? void 0 : void 0;
144
144
  } catch {
145
145
  holderName = void 0;
146
146
  }
@@ -4,6 +4,7 @@ import { peekMandateCatalogueEntry } from "../../../../host/ui-slots.js";
4
4
  import { fetchAgentOutputSchemas } from "../../../../mandates/output-contract.js";
5
5
  import { SURFACE_WRITE_TOOL_NAME } from "../../../../surfaces/runtime/surface-writeback.js";
6
6
  import { isJsonObject } from "../../../../utils/json.js";
7
+ import { readAgentName } from "../../../identity/agent-identity.js";
7
8
  function schemaName(schema) {
8
9
  if (!isJsonObject(schema)) return null;
9
10
  return typeof schema.name === "string" && schema.name ? schema.name : null;
@@ -14,7 +15,7 @@ async function resolveRunOutputContract(state, conversationId) {
14
15
  const agentId = conversation.agentId ?? null;
15
16
  const mandateKey = conversation.mandateKey ?? null;
16
17
  const record = agentId ? selectAgentById(state, agentId) : void 0;
17
- const agentName = record?.name ? record.name : null;
18
+ const agentName = readAgentName(state, agentId) ?? null;
18
19
  const outputSchemaLoaded = record ? hasField(record._loadedFields, "outputSchema") : false;
19
20
  if (record && outputSchemaLoaded && record.outputSchema) {
20
21
  const name2 = record.outputSchema.name;
@@ -6,6 +6,7 @@ import type { FieldFlags } from "@ai-matrx/agents/field-flags";
6
6
  import type { SkillConfig } from "../../ui/skills-types.js";
7
7
  import type { UiGates } from "../redux/agent-settings/ui-gates.js";
8
8
  import type { MatrxDirectivesConfig } from "./matrx-directives.types.js";
9
+ import type { AgentRunTierRow, AgentRunCounts } from "@ai-matrx/agents/runtime";
9
10
  export type AgentType = "user" | "builtin";
10
11
  /**
11
12
  * All available UI input types for agent variables.
@@ -493,29 +494,18 @@ export interface AgentExecutionMinimal {
493
494
  * `model_id` is the agent's default model (display + override diff base only —
494
495
  * the server owns the merge of `config_overrides`). `tool_ids` is display only.
495
496
  */
496
- export interface AgentRunTier {
497
- id: string;
498
- is_version: boolean;
499
- version_id: string | null;
500
- name: string;
501
- description: string | null;
497
+ /**
498
+ * The run tier row (`agx_get_run_tier`) — the core's `AgentRunTierRow`
499
+ * (`@ai-matrx/agents/runtime`, read by `createRunTierStore`) with its JSON
500
+ * columns narrowed to chat's variable, context-policy and gate shapes.
501
+ */
502
+ export interface AgentRunTier extends Omit<AgentRunTierRow, "variable_definitions" | "context_policies" | "ui_gates"> {
502
503
  variable_definitions: VariableDefinition[] | null;
503
504
  context_policies: ContextPolicy[] | null;
504
- auto_context_disabled: boolean;
505
- model_id: string | null;
506
505
  ui_gates: UiGates | null;
507
- tool_ids: string[] | null;
508
- access_level: string;
509
- custom_tool_count: number;
510
- skill_count: number;
511
- connection_count: number;
512
506
  }
513
507
  /** The run tier's plain counts, as the registry stores them (P24b). */
514
- export interface AgentRunCounts {
515
- customTools: number;
516
- skills: number;
517
- connections: number;
518
- }
508
+ export type { AgentRunCounts } from "@ai-matrx/agents/runtime";
519
509
  /** The run-control pickers' extra values (`agx_get_run_controls`, P25). */
520
510
  export interface AgentRunControls {
521
511
  customToolNames: string[];
@@ -4,8 +4,7 @@ import { useState, useCallback } from "react";
4
4
  import { ChevronDown } from "lucide-react";
5
5
  import { usePathname, useSearchParams, useRouter } from "../../host/navigation.js";
6
6
  import { AgentPickerSheet } from "../../next/lazy/AgentPickerSheet.js";
7
- import { useAppSelector } from "../../store/hooks.js";
8
- import { selectAgentById } from "../../agents/redux/agent-definition/selectors.js";
7
+ import { useAgentName } from "../../agents/identity/agent-identity.js";
9
8
  import { pushAppHref } from "../../host/ui-slots.js";
10
9
  import { Button } from "@ai-matrx/design-system/controls";
11
10
  function ChatMobileAgentName() {
@@ -17,10 +16,8 @@ function ChatMobileAgentName() {
17
16
  const pathMatch = pathname.match(/\/demos\/chat\/a\/([^/?]+)/);
18
17
  return pathMatch?.[1] ?? searchParams.get("agent") ?? void 0;
19
18
  })();
20
- const agentRecord = useAppSelector(
21
- (state) => agentIdFromUrl ? selectAgentById(state, agentIdFromUrl) : void 0
22
- );
23
- const displayName = agentRecord?.name ?? "Matrx Chat";
19
+ const agentName = useAgentName(agentIdFromUrl);
20
+ const displayName = agentName ?? "Matrx Chat";
24
21
  const handleAgentSelect = useCallback(
25
22
  (agent) => {
26
23
  setIsPickerOpen(false);
@@ -34,7 +31,7 @@ function ChatMobileAgentName() {
34
31
  {
35
32
  open: isPickerOpen,
36
33
  onOpenChange: setIsPickerOpen,
37
- selectedAgent: agentRecord ? { promptId: agentRecord.id, name: agentRecord.name } : null,
34
+ selectedAgent: agentIdFromUrl && agentName ? { promptId: agentIdFromUrl, name: agentName } : null,
38
35
  onSelect: handleAgentSelect
39
36
  }
40
37
  ),
@@ -15,9 +15,9 @@ import {
15
15
  } from "@ai-matrx/tap-target/buttons";
16
16
  import { useAppDispatch, useAppSelector } from "../../store/hooks.js";
17
17
  import { selectAgentById } from "../../agents/redux/agent-definition/selectors.js";
18
- import { initializeChatAgents } from "../../agents/redux/agent-definition/thunks.js";
19
18
  import { pushAppHref } from "../../host/ui-slots.js";
20
19
  import { Button } from "@ai-matrx/design-system/controls";
20
+ import { ensureAgentCatalog } from "../../agents/identity/agent-identity.js";
21
21
  function togglePanel() {
22
22
  const cb = document.getElementById(
23
23
  "shell-panel-toggle"
@@ -127,7 +127,7 @@ function ChatPanelContent() {
127
127
  const [searchQuery, setSearchQuery] = useState("");
128
128
  const dispatch = useAppDispatch();
129
129
  useEffect(() => {
130
- dispatch(initializeChatAgents());
130
+ void ensureAgentCatalog();
131
131
  }, [dispatch]);
132
132
  const agentIdFromUrl = (() => {
133
133
  const pathMatch = pathname.match(/\/demos\/chat\/a\/([^/?]+)/);