@ai-matrx/chat 0.3.32 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (64) hide show
  1. package/CHANGELOG.md +29 -0
  2. package/dist/agents/components/ambient-assistant/intelligence-places.js +2 -2
  3. package/dist/agents/components/chat/ChatConversationRoom.d.ts +7 -1
  4. package/dist/agents/components/chat/ChatConversationRoom.js +8 -2
  5. package/dist/agents/components/chat/intelligence-places.js +7 -7
  6. package/dist/agents/components/inputs/smart-input/RunToolPicker.js +7 -13
  7. package/dist/agents/components/inputs/smart-input/composer/ComposerAgentPill.js +3 -2
  8. package/dist/agents/components/inputs/smart-input/composer/useComposerAgent.js +5 -5
  9. package/dist/agents/components/inputs/smart-input/composer/useComposerAgentFilter.js +3 -2
  10. package/dist/agents/components/run-controls/RunConfigOverrides.js +10 -16
  11. package/dist/agents/components/run-controls/panels/shared.d.ts +0 -1
  12. package/dist/agents/hooks/useAgentModelLabel.js +4 -3
  13. package/dist/agents/hooks/useDiffEnrichment.js +3 -5
  14. package/dist/agents/hooks/useModelControls.d.ts +5 -161
  15. package/dist/agents/identity/model-catalog.d.ts +37 -0
  16. package/dist/agents/identity/model-catalog.js +103 -0
  17. package/dist/agents/redux/agent-definition/selectors.d.ts +0 -237
  18. package/dist/agents/redux/agent-definition/thunks.d.ts +1 -1
  19. package/dist/agents/redux/agent-settings/agentSettingsSlice.d.ts +1 -1
  20. package/dist/agents/redux/agent-settings/agentSettingsSlice.js +4 -6
  21. package/dist/agents/redux/agent-settings/selectors.d.ts +1 -152
  22. package/dist/agents/redux/agent-settings/selectors.js +0 -39
  23. package/dist/agents/redux/agent-settings/useNormalizedControls.d.ts +8 -0
  24. package/dist/agents/redux/agent-settings/useNormalizedControls.js +24 -0
  25. package/dist/agents/redux/agent-shortcut-categories/selectors.d.ts +0 -50
  26. package/dist/agents/redux/agent-shortcuts/selectors.d.ts +0 -147
  27. package/dist/agents/redux/conversation-history/selectors.d.ts +0 -16
  28. package/dist/agents/redux/conversation-list/conversation-list.selectors.d.ts +0 -2
  29. package/dist/agents/redux/execution-system/active-requests/active-requests.selectors.d.ts +0 -54
  30. package/dist/agents/redux/execution-system/context-state/context-state.selectors.d.ts +0 -14
  31. package/dist/agents/redux/execution-system/conversations/conversations.selectors.d.ts +0 -5
  32. package/dist/agents/redux/execution-system/instance-context/instance-context.selectors.d.ts +0 -3
  33. package/dist/agents/redux/execution-system/instance-resources/instance-resources.selectors.d.ts +0 -8
  34. package/dist/agents/redux/execution-system/instance-ui-state/instance-ui-state.selectors.d.ts +0 -5
  35. package/dist/agents/redux/execution-system/instance-variable-values/instance-variable-values.selectors.d.ts +0 -7
  36. package/dist/agents/redux/execution-system/messages/messages.selectors.d.ts +0 -1
  37. package/dist/agents/redux/execution-system/observability/observability.selectors.d.ts +0 -7
  38. package/dist/agents/redux/execution-system/observational-memory/observational-memory.selectors.d.ts +0 -1
  39. package/dist/agents/redux/execution-system/selectors/aggregate.selectors.d.ts +0 -14
  40. package/dist/agents/redux/execution-system/thunks/execute-manual-instance.thunk.js +2 -2
  41. package/dist/agents/redux/execution-system/thunks/launch-agent-execution.thunk.js +10 -2
  42. package/dist/agents/redux/execution-system/utils/required-organization.d.ts +14 -0
  43. package/dist/agents/redux/execution-system/utils/required-organization.js +4 -0
  44. package/dist/agents/redux/model-registry.d.ts +6 -4
  45. package/dist/agents/redux/model-registry.js +23 -5
  46. package/dist/agents/redux/proposed-directives/proposedDirectivesSlice.d.ts +0 -1
  47. package/dist/agents/redux/tools/tools.selectors.d.ts +0 -1
  48. package/dist/agents/runtime/generation-job.js +4 -7
  49. package/dist/agents/runtime/get-model-capabilities.js +3 -3
  50. package/dist/agents/ui-first-tools/redux/agent-lists.selectors.d.ts +0 -2
  51. package/dist/host/db-types.d.ts +276 -9
  52. package/dist/store/create-chat-store.d.ts +0 -2
  53. package/dist/store/middlewares.d.ts +0 -5
  54. package/dist/store/slices.d.ts +0 -1
  55. package/dist/store/slices.js +0 -2
  56. package/dist/surfaces/redux/selectors.d.ts +0 -2
  57. package/dist/tool-call-visualization/intelligence-places.js +1 -1
  58. package/dist/tool-call-visualization/redux/selectors.d.ts +0 -1
  59. package/dist/voice-agent/intelligence-places.js +6 -6
  60. package/package.json +4 -4
  61. package/dist/agents/model-registry/modelRegistrySlice.d.ts +0 -691
  62. package/dist/agents/model-registry/modelRegistrySlice.js +0 -574
  63. package/dist/host/model-class.d.ts +0 -33
  64. package/dist/host/model-class.js +0 -21
package/CHANGELOG.md CHANGED
@@ -1,5 +1,34 @@
1
1
  # @ai-matrx/chat
2
2
 
3
+ ## 0.4.0
4
+
5
+ Agent core B3 + the B2 tail — every piece of agent and model state has one holder:
6
+
7
+ - **The model registry slice is retired.** `modelRegistry` is gone from `chatReducers`; model records
8
+ (options, full records with controls, pinned-class configs, class labels, identities) live in the core
9
+ model catalog (`@ai-matrx/agents/models` 0.52+, `catalog.records`), bound once per page by
10
+ `agents/identity/model-catalog` (`getModelCatalog`, `getModelRecords`, `readModelRecords`,
11
+ `useModelRecords`, `useModelRecord`, `useModelLabel`, `useModelLabelWithClass`, `useModelClassLabels`,
12
+ `useModelClassControls`). `agents/redux/model-registry` re-exports the core selectors, which take the
13
+ records state. `agents/model-registry/modelRegistrySlice` and the `host/model-class` port are deleted;
14
+ `selectNormalizedControls` / `selectEffectiveModelName` became `useNormalizedControls(agentId)`
15
+ (`agents/redux/agent-settings/useNormalizedControls`).
16
+ - **The run tier is never copied onto the agent record**: selectors complete the record from the core run
17
+ tier store at read time (`agents/redux/agent-definition/run-tier-record`); a field the record read itself wins.
18
+ - G3 `check:one-holder`: the model part and the run-tier projection are hard bans (self-tested).
19
+ - Intelligence places name the package's real paths (`apps/shared/chat/src/...`).
20
+ - **Consumer action:** host — make `getModelCatalog()` return chat's instance, hydrate shell model rows with
21
+ `getModelRecords().hydrate(...)`, drop `registerChatModelClassHooks`, repoint `modelRegistrySlice` imports.
22
+
23
+ ## 0.3.33
24
+
25
+ - **Opening a chat never asks for an organization.** `launchAgentExecution` asks before opening only
26
+ when the run starts now or a window/gate opens (`launchAsksForOrganizationBeforeOpen`); an inline
27
+ composer (`/chat/new`) asks at the first send through the same hold-and-set gate.
28
+ - **Reading your own agent-less chat never waits on the active workspace.** `ChatConversationRoom`
29
+ takes `organizationId` (the row's own) and resolves its mandate there.
30
+ Consumer action: pass the conversation row's `organization_id` to `ChatConversationRoom`.
31
+
3
32
  ## 0.3.32
4
33
 
5
34
  Automatic changed-only republish (docs/metadata drift since the last tag — see
@@ -3,14 +3,14 @@ const K = MANDATE_KEYS;
3
3
  const AMBIENT_PLACES = {
4
4
  feature: "ambient",
5
5
  label: "Ambient assistant",
6
- roots: ["packages/chat/src/agents/components/ambient-assistant"],
6
+ roots: ["apps/shared/chat/src/agents/components/ambient-assistant"],
7
7
  places: [
8
8
  {
9
9
  id: "page-guidance",
10
10
  label: "Any page",
11
11
  trigger: "The page assistant (where a module has none of its own)",
12
12
  mandateKeys: [K.ambient__page_guidance],
13
- sources: ["packages/chat/src/agents/components/ambient-assistant/ambientAssistantMandates.ts"]
13
+ sources: ["apps/shared/chat/src/agents/components/ambient-assistant/ambientAssistantMandates.ts"]
14
14
  },
15
15
  {
16
16
  id: "listen",
@@ -19,8 +19,14 @@ import type { ComposerMode } from "../inputs/smart-input/composer/composer-types
19
19
  * `mandateKey` rides along so the next turn is answered by whoever the
20
20
  * org/user binding says, not by a frozen display identity.
21
21
  */
22
- export declare function ChatConversationRoom({ conversationId, agentId, ownedByMandate, sandboxBinding, composer, }: {
22
+ export declare function ChatConversationRoom({ conversationId, agentId, ownedByMandate, sandboxBinding, composer, organizationId, }: {
23
23
  conversationId: string;
24
+ /**
25
+ * The conversation ROW's own organization. An agent-less room resolves its
26
+ * mandate in THAT organization, so reading your own chat never waits on, or
27
+ * asks for, the active workspace (Arman, 2026-10-07).
28
+ */
29
+ organizationId?: string | null;
24
30
  /** The conversation's own agent, or the SSR-resolved mandate agent, or null. */
25
31
  agentId: string | null;
26
32
  /** True when `agentId` is the mandate's display identity, not the row's. */
@@ -10,7 +10,8 @@ function ChatConversationRoom({
10
10
  agentId,
11
11
  ownedByMandate,
12
12
  sandboxBinding = null,
13
- composer
13
+ composer,
14
+ organizationId = null
14
15
  }) {
15
16
  if (agentId && !ownedByMandate) {
16
17
  return /* @__PURE__ */ jsx(
@@ -39,6 +40,7 @@ function ChatConversationRoom({
39
40
  ChatConversationRoomResolved,
40
41
  {
41
42
  conversationId,
43
+ organizationId,
42
44
  sandboxBinding,
43
45
  composer
44
46
  }
@@ -46,10 +48,14 @@ function ChatConversationRoom({
46
48
  }
47
49
  function ChatConversationRoomResolved({
48
50
  conversationId,
51
+ organizationId,
49
52
  sandboxBinding,
50
53
  composer
51
54
  }) {
52
- const { mandate, loading, error, organizationPending } = useMandate(DEFAULT_NEW_CHAT_MANDATE_KEY);
55
+ const { mandate, loading, error, organizationPending } = useMandate(
56
+ DEFAULT_NEW_CHAT_MANDATE_KEY,
57
+ organizationId ? { organizationId } : {}
58
+ );
53
59
  if (loading) return /* @__PURE__ */ jsx(ChatNewLandingSkeleton, {});
54
60
  if (organizationPending) {
55
61
  return /* @__PURE__ */ jsx(WorkspaceGate, { blocked: true, sentence: "This chat needs a workspace to open.", children: /* @__PURE__ */ jsx(ChatNewLandingSkeleton, {}) });
@@ -15,7 +15,7 @@ const CHAT_PLACES = {
15
15
  feature: "chat",
16
16
  label: "Chat",
17
17
  aliases: { DEFAULT_NEW_CHAT_MANDATE_KEY },
18
- roots: ["packages/chat/src/agents/components/chat", "app/(core)/chat"],
18
+ roots: ["apps/shared/chat/src/agents/components/chat", "app/(core)/chat"],
19
19
  places: [
20
20
  {
21
21
  id: "new",
@@ -25,9 +25,9 @@ const CHAT_PLACES = {
25
25
  mandateKeys: [K.chat__default_new_chat],
26
26
  sources: [
27
27
  "app/(core)/chat/new/page.tsx",
28
- "packages/chat/src/agents/components/chat/ChatNewClient.tsx",
29
- "packages/chat/src/agents/components/chat/ChatNewHeader.tsx",
30
- "packages/chat/src/agents/components/chat/NewChatGreeting.tsx"
28
+ "apps/shared/chat/src/agents/components/chat/ChatNewClient.tsx",
29
+ "apps/shared/chat/src/agents/components/chat/ChatNewHeader.tsx",
30
+ "apps/shared/chat/src/agents/components/chat/NewChatGreeting.tsx"
31
31
  ]
32
32
  },
33
33
  {
@@ -46,8 +46,8 @@ const CHAT_PLACES = {
46
46
  mandateKeys: [K.chat__default_new_chat],
47
47
  sources: [
48
48
  "app/(core)/chat/[conversationId]/page.tsx",
49
- "packages/chat/src/agents/components/chat/ChatConversationRoom.tsx",
50
- "packages/chat/src/agents/components/chat/ChatRoomClient.tsx"
49
+ "apps/shared/chat/src/agents/components/chat/ChatConversationRoom.tsx",
50
+ "apps/shared/chat/src/agents/components/chat/ChatRoomClient.tsx"
51
51
  ]
52
52
  },
53
53
  {
@@ -69,7 +69,7 @@ const CHAT_PLACES = {
69
69
  K.chat__response_mode_data,
70
70
  K.chat__response_mode_brainstorm
71
71
  ],
72
- sources: ["packages/chat/src/cx-chat/components/agent/local-agents.ts"]
72
+ sources: ["apps/shared/chat/src/cx-chat/components/agent/local-agents.ts"]
73
73
  }
74
74
  ]
75
75
  };
@@ -30,18 +30,14 @@ import {
30
30
  } from "../../../redux/agent-definition/selectors.js";
31
31
  import { fetchAgentRunControls } from "../../../redux/agent-definition/thunks.js";
32
32
  import { selectAgentIdFromInstance } from "../../../redux/execution-system/conversations/conversations.selectors.js";
33
- import {
34
- selectAllModels,
35
- selectModelFullyLoaded,
36
- fetchModelById
37
- } from "../../../model-registry/modelRegistrySlice.js";
33
+ import { selectAllModels, selectModelFullyLoaded } from "@ai-matrx/agents/models";
38
34
  import {
39
35
  useModelControls,
40
36
  supportsTools
41
37
  } from "../../../hooks/useModelControls.js";
42
38
  import { selectInstanceOverrideState } from "../../../redux/execution-system/instance-model-overrides/instance-model-overrides.selectors.js";
43
39
  import { effectiveOfferingPin } from "../../../redux/execution-system/instance-model-overrides/offering-pin.js";
44
- import { useModelClassControls } from "../../../../host/model-class.js";
40
+ import { getModelRecords, useModelClassControls, useModelRecords } from "../../../identity/model-catalog.js";
45
41
  import { selectBuilderAdvancedSettings } from "../../../redux/execution-system/instance-ui-state/instance-ui-state.selectors.js";
46
42
  import { setBuilderAdvancedSettings } from "../../../redux/execution-system/instance-ui-state/instance-ui-state.slice.js";
47
43
  import { DEFAULT_BUILDER_ADVANCED_SETTINGS } from "../../../types/instance.types.js";
@@ -73,16 +69,14 @@ function RunToolPicker({ conversationId }) {
73
69
  selectInstanceOverrideState(conversationId)
74
70
  );
75
71
  const effectiveModelId = overrideState?.overrides?.model ?? overrideState?.baseSettings?.model ?? "";
76
- const models = useAppSelector(selectAllModels);
77
- const modelIsFull = useAppSelector(
78
- (s) => selectModelFullyLoaded(s, effectiveModelId)
79
- );
80
- const modelRegistryLoading = useAppSelector((s) => s.modelRegistry.isLoading);
72
+ const models = useModelRecords(selectAllModels);
73
+ const modelIsFull = useModelRecords((s) => selectModelFullyLoaded(s, effectiveModelId));
74
+ const modelRegistryLoading = useModelRecords((s) => s.isLoading);
81
75
  useEffect(() => {
82
76
  if (effectiveModelId && !modelIsFull && !modelRegistryLoading) {
83
- void dispatch(fetchModelById(effectiveModelId));
77
+ void getModelRecords().loadModel(effectiveModelId);
84
78
  }
85
- }, [effectiveModelId, modelIsFull, modelRegistryLoading, dispatch]);
79
+ }, [effectiveModelId, modelIsFull, modelRegistryLoading]);
86
80
  const classControls = useModelClassControls(
87
81
  effectiveModelId,
88
82
  effectiveOfferingPin(overrideState)
@@ -21,7 +21,8 @@ import {
21
21
  import { useSessionKnob } from "../../../../../host/prefs-react.js";
22
22
  import { knobRefusalSentence, setKnobOverride } from "../../../../../host/prefs.js";
23
23
  import { CHAT_DEFAULT_MODEL_KNOB } from "../../../../../ui/chat-default-model-knob.js";
24
- import { selectModelLabelWithClass } from "../../../../model-registry/modelRegistrySlice.js";
24
+ import { selectModelLabelWithClass } from "@ai-matrx/agents/models";
25
+ import { useModelRecords } from "../../../../identity/model-catalog.js";
25
26
  import {
26
27
  ComposerMenuDivider,
27
28
  ComposerMenuHelp,
@@ -173,7 +174,7 @@ function ChatPresetsPanel({
173
174
  const personalModelId = typeof personalModel === "string" && personalModel.trim() ? personalModel : null;
174
175
  const overrideState = useAppSelector(selectInstanceOverrideState(conversationId));
175
176
  const pinnedOfferingId = effectiveOfferingPin(overrideState);
176
- const personalModelLabel = useAppSelector((s) => selectModelLabelWithClass(s, personalModelId, pinnedOfferingId)) ?? null;
177
+ const personalModelLabel = useModelRecords((s) => selectModelLabelWithClass(s, personalModelId, pinnedOfferingId)) ?? null;
177
178
  const onSelectAgent = agentControl?.onSelectAgent;
178
179
  const agentFilter = useComposerAgentFilter(conversationId);
179
180
  if (!onSelectAgent) {
@@ -12,9 +12,9 @@ import { selectInstanceOverrideState } from "../../../../redux/execution-system/
12
12
  import {
13
13
  selectModelLabelById,
14
14
  selectModelLabelWithClass
15
- } from "../../../../model-registry/modelRegistrySlice.js";
15
+ } from "@ai-matrx/agents/models";
16
16
  import { useModelOptions } from "../../../../../host/ui-slots.js";
17
- import { useModelClassLabels } from "../../../../../host/model-class.js";
17
+ import { useModelClassLabels, useModelRecords } from "../../../../identity/model-catalog.js";
18
18
  import { useEffectiveClassPin } from "../../../../redux/execution-system/instance-model-overrides/useEffectiveClassPin.js";
19
19
  import { ensureAgentCatalog, useAgentName } from "../../../../identity/agent-identity.js";
20
20
  const CHAT_AGENT_TAG = "chat-agent";
@@ -49,14 +49,14 @@ function useComposerAgent(conversationId) {
49
49
  const classPins = useEffectiveClassPin(conversationId);
50
50
  const effectivePin = classPins.effectivePin ?? null;
51
51
  const agentOwnPin = classPins.basePin ?? null;
52
- const effectiveModelLabel = useAppSelector((state) => selectModelLabelWithClass(state, effectiveModelId, effectivePin)) ?? null;
52
+ const effectiveModelLabel = useModelRecords((state) => selectModelLabelWithClass(state, effectiveModelId, effectivePin)) ?? null;
53
53
  const agentOwnModelId = useAgentOwnModelId(conversationId);
54
- const agentModelLabel = useAppSelector((state) => selectModelLabelWithClass(state, agentOwnModelId, agentOwnPin)) ?? null;
54
+ const agentModelLabel = useModelRecords((state) => selectModelLabelWithClass(state, agentOwnModelId, agentOwnPin)) ?? null;
55
55
  const { mandate: customMandate } = useMandate(MANDATE_KEYS.chat__default_new_chat, { optional: true });
56
56
  const customAgentId = customMandate?.agentId ?? null;
57
57
  const rows = useAgentCatalogRows();
58
58
  const presetRows = rows.filter(isPresetRow);
59
- const modelLabels = useAppSelector(
59
+ const modelLabels = useModelRecords(
60
60
  (state) => presetRows.map((row) => selectModelLabelById(state, row.modelId) ?? null).join("\0")
61
61
  ).split("\0");
62
62
  const presets = presetRows.map((row, index) => ({
@@ -1,12 +1,13 @@
1
1
  "use client";
2
2
  import { useAppSelector } from "../../../../../store/hooks.js";
3
3
  import { selectBuilderAdvancedSettings } from "../../../../redux/execution-system/instance-ui-state/instance-ui-state.selectors.js";
4
- import { selectAllModels } from "../../../../model-registry/modelRegistrySlice.js";
4
+ import { selectAllModels } from "@ai-matrx/agents/models";
5
+ import { useModelRecords } from "../../../../identity/model-catalog.js";
5
6
  import { readOutputTypes } from "./output-selection.js";
6
7
  import { agentOutputFilterSpec, modelMakesAny, modelOutputModalities } from "./agent-output-filter.js";
7
8
  function useComposerAgentFilter(conversationId) {
8
9
  const settings = useAppSelector(selectBuilderAdvancedSettings(conversationId));
9
- const models = useAppSelector(selectAllModels);
10
+ const models = useModelRecords(selectAllModels);
10
11
  const spec = agentOutputFilterSpec(readOutputTypes(settings?.outputTypes));
11
12
  if (!spec) return void 0;
12
13
  const outputsById = new Map(models.map((model) => [model.id, modelOutputModalities(model.capabilities)]));
@@ -8,12 +8,10 @@ import { useAppDispatch, useAppSelector, useAppStore } from "../../../store/hook
8
8
  import {
9
9
  selectAllModels,
10
10
  selectModelFullyLoaded,
11
- fetchModelById,
12
- retryModelDetail,
13
11
  selectModelDetailError
14
- } from "../../model-registry/modelRegistrySlice.js";
12
+ } from "@ai-matrx/agents/models";
15
13
  import { useModelControls } from "../../hooks/useModelControls.js";
16
- import { useModelClassControls } from "../../../host/model-class.js";
14
+ import { getModelRecords, useModelClassControls, useModelRecords } from "../../identity/model-catalog.js";
17
15
  import {
18
16
  selectInstanceOverrideState,
19
17
  selectSettingsOverridesForApi
@@ -88,7 +86,7 @@ function RunConfigOverrides({
88
86
  const overrideState = useAppSelector(
89
87
  selectInstanceOverrideState(conversationId)
90
88
  );
91
- const models = useAppSelector(selectAllModels);
89
+ const models = useModelRecords(selectAllModels);
92
90
  const overrideJson = useAppSelector(
93
91
  (state) => JSON.stringify(
94
92
  selectSettingsOverridesForApi(conversationId)(state) ?? {},
@@ -133,18 +131,14 @@ function RunConfigOverrides({
133
131
  const effectiveOffering = removals.includes("offering_id") ? nullDefaults?.offering_id : overrides.offering_id ?? base.offering_id;
134
132
  const pinnedOfferingId = typeof effectiveOffering === "string" && effectiveOffering ? effectiveOffering : void 0;
135
133
  const modelChoiceOverridden = "model" in overrides || "offering_id" in overrides || removals.includes("offering_id");
136
- const isFull = useAppSelector(
137
- (s) => selectModelFullyLoaded(s, effectiveModelId)
138
- );
139
- const modelDetailError = useAppSelector(
140
- (s) => selectModelDetailError(s, effectiveModelId)
141
- );
142
- const registryLoading = useAppSelector((s) => s.modelRegistry.isLoading);
134
+ const isFull = useModelRecords((s) => selectModelFullyLoaded(s, effectiveModelId));
135
+ const modelDetailError = useModelRecords((s) => selectModelDetailError(s, effectiveModelId));
136
+ const registryLoading = useModelRecords((s) => s.isLoading);
143
137
  useEffect(() => {
144
138
  if (effectiveModelId && !isFull && !registryLoading) {
145
- dispatch(fetchModelById(effectiveModelId));
139
+ void getModelRecords().loadModel(effectiveModelId);
146
140
  }
147
- }, [dispatch, effectiveModelId, isFull, registryLoading]);
141
+ }, [effectiveModelId, isFull, registryLoading]);
148
142
  const classControls = useModelClassControls(effectiveModelId, pinnedOfferingId);
149
143
  const { normalizedControls } = useModelControls(
150
144
  models,
@@ -194,8 +188,8 @@ function RunConfigOverrides({
194
188
  {
195
189
  variant: "outline",
196
190
  onClick: () => {
197
- dispatch(retryModelDetail(effectiveModelId));
198
- void dispatch(fetchModelById(effectiveModelId));
191
+ getModelRecords().retryModel(effectiveModelId);
192
+ void getModelRecords().loadModel(effectiveModelId);
199
193
  },
200
194
  children: "Retry"
201
195
  }
@@ -64,7 +64,6 @@ export declare function makeSelectConversationRequests(conversationId: string):
64
64
  proposedDirectives: import("../../../redux/proposed-directives/proposedDirectivesSlice.js").ProposedDirectivesState;
65
65
  agentSettings: import("../../../redux/agent-settings/types.js").AgentSettingsState;
66
66
  conversationAttachments: import("../../../connectors/attachments.slice.js").AttachmentsSliceState;
67
- modelRegistry: import("../../../model-registry/modelRegistrySlice.js").ModelRegistryState;
68
67
  chatHost: import("../../../../store/chat-host.slice.js").ChatHostState;
69
68
  } & import("../../../../store/root-state.js").ChatHostReadSlices) => ActiveRequest[]) & {
70
69
  clearCache: () => void;
@@ -7,8 +7,8 @@ import {
7
7
  import {
8
8
  selectModelLabelById,
9
9
  selectModelLabelWithClass
10
- } from "../model-registry/modelRegistrySlice.js";
11
- import { useModelClassLabels } from "../../host/model-class.js";
10
+ } from "@ai-matrx/agents/models";
11
+ import { useModelClassLabels, useModelLabel, useModelRecords } from "../identity/model-catalog.js";
12
12
  import { useAgentSummaryRow } from "../identity/agent-identity.js";
13
13
  function useAgentModelLabel(agentId) {
14
14
  useModelClassLabels();
@@ -21,7 +21,8 @@ function useAgentModelLabel(agentId) {
21
21
  );
22
22
  const modelId = ownModelId ?? summary?.modelId ?? null;
23
23
  const offeringId = ownOfferingId !== void 0 ? ownOfferingId : summary?.offeringId;
24
- const label = useAppSelector(
24
+ useModelLabel(modelId);
25
+ const label = useModelRecords(
25
26
  (state) => offeringId === void 0 ? selectModelLabelById(state, modelId) : selectModelLabelWithClass(state, modelId, offeringId)
26
27
  );
27
28
  return { modelId, offeringId, label };
@@ -6,15 +6,13 @@ import {
6
6
  selectToolIdentityMap
7
7
  } from "../redux/tools/tools.selectors.js";
8
8
  import { selectMcpCatalog } from "../redux/mcp/mcp.slice.js";
9
- import { selectModelIdentityMap } from "../model-registry/modelRegistrySlice.js";
9
+ import { useModelRecords } from "../identity/model-catalog.js";
10
10
  function useDiffEnrichment() {
11
11
  const allTools = useAppSelector(selectAllTools);
12
12
  const toolIdentities = useAppSelector(selectToolIdentityMap);
13
13
  const mcpCatalog = useAppSelector(selectMcpCatalog);
14
- const modelEntities = useAppSelector(
15
- (state) => state.modelRegistry.entities
16
- );
17
- const modelIdentities = useAppSelector(selectModelIdentityMap);
14
+ const modelEntities = useModelRecords((s) => s.entities);
15
+ const modelIdentities = useModelRecords((s) => s.identityById);
18
16
  return useMemo(
19
17
  () => ({
20
18
  resolveModelId: (id) => {
@@ -2,7 +2,7 @@
2
2
  * Hook to parse and normalize model controls from dynamic API data
3
3
  * Keeps snake_case naming for compatibility with Python backend
4
4
  */
5
- import type { ModelClassControls } from "../../host/model-class.js";
5
+ import type { ModelClassControls } from "../identity/model-catalog.js";
6
6
  import type { AIModelRecord } from "../redux/model-registry.js";
7
7
  export interface ControlDefinition {
8
8
  type: "number" | "integer" | "boolean" | "string" | "enum" | "array" | "string_array" | "object_array";
@@ -160,89 +160,11 @@ export declare function useModelControls(models: AIModelRecord[], selectedModelI
160
160
  error: string;
161
161
  } | {
162
162
  normalizedControls: NormalizedControls;
163
- selectedModel: {
164
- capabilities: import("../../host/db-types.js").Json | null;
165
- common_name: string | null;
166
- context_window: number | null;
167
- cost_rating: number | null;
168
- created_at: string;
169
- created_by: string | null;
170
- deleted_at: string | null;
171
- description: string | null;
172
- guest_fallback_id: string | null;
173
- id: string;
174
- is_deprecated: boolean | null;
175
- is_premium: boolean | null;
176
- is_primary: boolean | null;
177
- is_system: boolean;
178
- max_tokens: number | null;
179
- metadata: import("../../host/db-types.js").Json;
180
- mid_fallback_id: string | null;
181
- name: string;
182
- organization_id: string;
183
- provider_id: string | null;
184
- published_to_web: boolean;
185
- published_to_web_at: string | null;
186
- published_to_web_by: string | null;
187
- release_date: string | null;
188
- retired_at: string | null;
189
- retry_fallback_id: string | null;
190
- retry_max_attempts: number;
191
- shown_to: import("../../host/db-types.js").Database["platform"]["Enums"]["shown_to"] | null;
192
- speed_rating: number | null;
193
- successor_id: string | null;
194
- updated_at: string;
195
- updated_by: string | null;
196
- version: number;
197
- visibility: import("../../host/db-types.js").Database["platform"]["Enums"]["visibility"];
198
- _fetchType: import("../model-registry/modelRegistrySlice.js").ModelRecordFetchType;
199
- maker: string | null;
200
- controls: unknown;
201
- constraints: unknown;
202
- };
163
+ selectedModel: AIModelRecord;
203
164
  error: null;
204
165
  } | {
205
166
  normalizedControls: NormalizedControls;
206
- selectedModel: {
207
- capabilities: import("../../host/db-types.js").Json | null;
208
- common_name: string | null;
209
- context_window: number | null;
210
- cost_rating: number | null;
211
- created_at: string;
212
- created_by: string | null;
213
- deleted_at: string | null;
214
- description: string | null;
215
- guest_fallback_id: string | null;
216
- id: string;
217
- is_deprecated: boolean | null;
218
- is_premium: boolean | null;
219
- is_primary: boolean | null;
220
- is_system: boolean;
221
- max_tokens: number | null;
222
- metadata: import("../../host/db-types.js").Json;
223
- mid_fallback_id: string | null;
224
- name: string;
225
- organization_id: string;
226
- provider_id: string | null;
227
- published_to_web: boolean;
228
- published_to_web_at: string | null;
229
- published_to_web_by: string | null;
230
- release_date: string | null;
231
- retired_at: string | null;
232
- retry_fallback_id: string | null;
233
- retry_max_attempts: number;
234
- shown_to: import("../../host/db-types.js").Database["platform"]["Enums"]["shown_to"] | null;
235
- speed_rating: number | null;
236
- successor_id: string | null;
237
- updated_at: string;
238
- updated_by: string | null;
239
- version: number;
240
- visibility: import("../../host/db-types.js").Database["platform"]["Enums"]["visibility"];
241
- _fetchType: import("../model-registry/modelRegistrySlice.js").ModelRecordFetchType;
242
- maker: string | null;
243
- controls: unknown;
244
- constraints: unknown;
245
- };
167
+ selectedModel: AIModelRecord;
246
168
  error: string;
247
169
  };
248
170
  /**
@@ -265,89 +187,11 @@ export declare function resolveModelControls(models: AIModelRecord[], selectedMo
265
187
  error: string;
266
188
  } | {
267
189
  normalizedControls: NormalizedControls;
268
- selectedModel: {
269
- capabilities: import("../../host/db-types.js").Json | null;
270
- common_name: string | null;
271
- context_window: number | null;
272
- cost_rating: number | null;
273
- created_at: string;
274
- created_by: string | null;
275
- deleted_at: string | null;
276
- description: string | null;
277
- guest_fallback_id: string | null;
278
- id: string;
279
- is_deprecated: boolean | null;
280
- is_premium: boolean | null;
281
- is_primary: boolean | null;
282
- is_system: boolean;
283
- max_tokens: number | null;
284
- metadata: import("../../host/db-types.js").Json;
285
- mid_fallback_id: string | null;
286
- name: string;
287
- organization_id: string;
288
- provider_id: string | null;
289
- published_to_web: boolean;
290
- published_to_web_at: string | null;
291
- published_to_web_by: string | null;
292
- release_date: string | null;
293
- retired_at: string | null;
294
- retry_fallback_id: string | null;
295
- retry_max_attempts: number;
296
- shown_to: import("../../host/db-types.js").Database["platform"]["Enums"]["shown_to"] | null;
297
- speed_rating: number | null;
298
- successor_id: string | null;
299
- updated_at: string;
300
- updated_by: string | null;
301
- version: number;
302
- visibility: import("../../host/db-types.js").Database["platform"]["Enums"]["visibility"];
303
- _fetchType: import("../model-registry/modelRegistrySlice.js").ModelRecordFetchType;
304
- maker: string | null;
305
- controls: unknown;
306
- constraints: unknown;
307
- };
190
+ selectedModel: AIModelRecord;
308
191
  error: null;
309
192
  } | {
310
193
  normalizedControls: NormalizedControls;
311
- selectedModel: {
312
- capabilities: import("../../host/db-types.js").Json | null;
313
- common_name: string | null;
314
- context_window: number | null;
315
- cost_rating: number | null;
316
- created_at: string;
317
- created_by: string | null;
318
- deleted_at: string | null;
319
- description: string | null;
320
- guest_fallback_id: string | null;
321
- id: string;
322
- is_deprecated: boolean | null;
323
- is_premium: boolean | null;
324
- is_primary: boolean | null;
325
- is_system: boolean;
326
- max_tokens: number | null;
327
- metadata: import("../../host/db-types.js").Json;
328
- mid_fallback_id: string | null;
329
- name: string;
330
- organization_id: string;
331
- provider_id: string | null;
332
- published_to_web: boolean;
333
- published_to_web_at: string | null;
334
- published_to_web_by: string | null;
335
- release_date: string | null;
336
- retired_at: string | null;
337
- retry_fallback_id: string | null;
338
- retry_max_attempts: number;
339
- shown_to: import("../../host/db-types.js").Database["platform"]["Enums"]["shown_to"] | null;
340
- speed_rating: number | null;
341
- successor_id: string | null;
342
- updated_at: string;
343
- updated_by: string | null;
344
- version: number;
345
- visibility: import("../../host/db-types.js").Database["platform"]["Enums"]["visibility"];
346
- _fetchType: import("../model-registry/modelRegistrySlice.js").ModelRecordFetchType;
347
- maker: string | null;
348
- controls: unknown;
349
- constraints: unknown;
350
- };
194
+ selectedModel: AIModelRecord;
351
195
  error: string;
352
196
  };
353
197
  /**
@@ -0,0 +1,37 @@
1
+ import { type ModelCatalog, type ModelClassConfig, type ModelRecord, type ModelRecords, type ModelRecordsState } from "@ai-matrx/agents/models";
2
+ export declare function getModelCatalog(): ModelCatalog;
3
+ export declare const getModelRecords: () => ModelRecords;
4
+ /** The records state now. A host with no database bound yet (a unit test) reads empty. */
5
+ export declare function readModelRecords(): ModelRecordsState;
6
+ /** Subscribe to a slice of the records. `select` must return a stable value for unchanged state. */
7
+ export declare function useModelRecords<T>(select: (state: ModelRecordsState) => T): T;
8
+ /** One model's record (options or full); reads the full record when `full` and it is missing. */
9
+ export declare function useModelRecord(modelId: string | null | undefined, options?: {
10
+ full?: boolean;
11
+ }): ModelRecord | undefined;
12
+ /** A model's display label; loads the options list (then the identity) when it is unknown. */
13
+ export declare function useModelLabel(modelId: string | null | undefined): string | undefined;
14
+ /** "Qwen3.8 27B · Matrx Lightning" when the model has several classes; loads the class index once. */
15
+ export declare function useModelLabelWithClass(modelId: string | null | undefined, offeringId: string | null | undefined): string | undefined;
16
+ /** Loads every model's classes once (no-op after the first success). */
17
+ export declare function useModelClassLabels(): void;
18
+ /**
19
+ * The resolved controls for the CLASS a model runs on. With a pin it loads that class's controls;
20
+ * without one it answers `undefined` and callers keep the model's own (preferred-class) controls.
21
+ * `{ pending: true }` while it loads, `{ failed: true }` when the pin is refused — never another
22
+ * class's controls.
23
+ */
24
+ export type ModelClassControls = undefined | {
25
+ pending: true;
26
+ failed?: false;
27
+ config?: undefined;
28
+ } | {
29
+ pending?: false;
30
+ failed: true;
31
+ config?: undefined;
32
+ } | {
33
+ pending?: false;
34
+ failed?: false;
35
+ config: ModelClassConfig;
36
+ };
37
+ export declare function useModelClassControls(modelId: string | null | undefined, offeringId: string | null | undefined): ModelClassControls;