You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@maka.apache.org by as...@apache.org on 2026/09/01 04:25:29 UTC

(maka) branch refactor/model-catalog-host-authority created (now 3073ce0f9b)

This is an automated email from the ASF dual-hosted git repository.

Astro-Han pushed a change to branch refactor/model-catalog-host-authority
in repository https://gitbox.apache.org/repos/asf/maka.git


      at 3073ce0f9b refactor(core): ship only the catalog-entry fields something reads

This branch includes the following new commits:

     new 3073ce0f9b refactor(core): ship only the catalog-entry fields something reads

The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.



---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@maka.apache.org
For additional commands, e-mail: commits-help@maka.apache.org


(maka) 01/01: refactor(core): ship only the catalog-entry fields something reads

Posted by as...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

Astro-Han pushed a commit to branch refactor/model-catalog-host-authority
in repository https://gitbox.apache.org/repos/asf/maka.git

commit 3073ce0f9b5df1b0c08d714cfe661ab8a2506683
Author: AstroHan <le...@outlook.com>
AuthorDate: Tue Sep 1 12:25:20 2026 +0800

    refactor(core): ship only the catalog-entry fields something reads
    
    The resolved entry crosses the wire and then the desktop IPC boundary, so
    every field is paid for on each catalog read by each attached client.
    Thirteen of its twenty-two had no reader anywhere: `providerType` and
    `connectionSlug` (the connection that owns the entry already holds both),
    `source`, `unavailableReason`, `lifecycle`, `docsUrl`, `inputLimit`,
    `maxOutputTokens`, `structuredOutput`, `lastUpdated`, `modalities`,
    `provenance`, and every capability but vision. They are not needed today;
    when a surface asks for one, it comes back with the reader that wants it.
    
    `makeEntry` still consults all of those facts — they just stop being
    shipped. `capabilities` becomes the one boolean the only consumer
    projected out of it anyway.
    
    Dropping `unavailableReason` collapses what fed it. `canUseAsChatDefault`
    was true for `none`, `stale` and `not_in_live_list` alike, so staleness
    and live-list absence never changed an answer: `isStale`, its seven-day
    window, and the `now`/`staleAfterMs` inputs that only tests supplied are
    gone with them. What is left is the two real vetoes — provider retirement
    and an explicit "cannot chat" — and `providerRetired` names the one
    producer `providerAvailable` ever had. `authOk` had no producer at all,
    so its `'auth'` reason was unreachable.
    
    The connection-level input loses the six options nothing passed:
    `savedModelIds`, `fallbackModels`, `now`, `staleAfterMs`,
    `providerAvailable`, `authOk`. `pricing` and `pricingSource` stay: the
    entry's `pricing` seam is documented as the one field kept without a
    producer, and cost accounting does not depend on it — a call is priced
    from `pricingModelKey` when it is recorded.
    
    Behavior change: an enabled id no inventory describes now runs the same
    chat guard as a listed row, so one the bundled metadata knows to be
    image-only stops being default-capable. It previously skipped that check
    by construction and was offered. #1584 is unaffected — absence from a
    live list is still not a veto, and a bare id with no metadata is still
    selectable.
    
    The draft comparison narrows to the stored fields an entry is actually
    built from, and gains `description`, `knowledgeCutoff` and `modalities`
    which it should have compared all along; `inputLimit`, `maxOutputTokens`
    and `parallelToolCalls` no longer reach an entry, so a change to them no
    longer throws the Host's entries away.
    
    Generated-by: Claude Code
---
 packages/core/src/__tests__/model-catalog.test.ts  | 152 +++--------
 .../__tests__/provider-catalog-contract.test.ts    |   6 -
 packages/core/src/chat-model-choice.ts             |   2 +-
 packages/core/src/model-catalog.ts                 | 299 +++++----------------
 .../runtime-policy/model-catalog-entry-codec.ts    | 133 +--------
 .../__tests__/runtime-policy-coordinator.test.ts   |   3 -
 .../src/server/runtime-policy-coordinator.ts       |   3 -
 7 files changed, 109 insertions(+), 489 deletions(-)

diff --git a/packages/core/src/__tests__/model-catalog.test.ts b/packages/core/src/__tests__/model-catalog.test.ts
index 7a1238165e..afb0fff804 100644
--- a/packages/core/src/__tests__/model-catalog.test.ts
+++ b/packages/core/src/__tests__/model-catalog.test.ts
@@ -43,13 +43,7 @@ function verdict(input: BuildModelCatalogInput) {
   const entry = defaultModel
     ? buildModelCatalogEntries(input).find((candidate) => candidate.id === defaultModel)
     : undefined;
-  if (!entry) return { ok: false, reason: 'not_in_live_list' };
-  if (entry.canUseAsChatDefault) return { ok: true };
-  const reason =
-    entry.unavailableReason === 'stale' || entry.unavailableReason === 'none'
-      ? 'unsupported_for_chat'
-      : entry.unavailableReason;
-  return { ok: false, reason };
+  return { ok: entry?.canUseAsChatDefault === true };
 }
 
 test('a live inventory annotates a model it omits and preserves higher-priority failures', () => {
@@ -65,13 +59,12 @@ test('a live inventory annotates a model it omits and preserves higher-priority
   // what the account can run (#1584). It stays selectable and the provider
   // gets to answer for itself.
   assert.equal(missing?.canUseAsChatDefault, true);
-  assert.equal(missing?.unavailableReason, 'not_in_live_list');
   assert.deepEqual(verdict(input), { ok: true });
 
-  assert.equal(buildModelCatalogEntries({ ...input, authOk: false })[0]?.unavailableReason, 'auth');
+  // Retirement is the one provider-level veto left.
   assert.equal(
-    buildModelCatalogEntries({ ...input, providerAvailable: false })[0]?.unavailableReason,
-    'provider_removed',
+    buildModelCatalogEntries({ ...input, providerRetired: true })[0]?.canUseAsChatDefault,
+    false,
   );
 });
 
@@ -82,7 +75,7 @@ test('chat-default validation blocks image-only models but accepts merged partia
     models: [{ id: 'gpt-image-1', capabilities: { imageGeneration: true, chat: false } }],
     modelSource: 'fetched' as const,
   };
-  assert.deepEqual(verdict(imageOnly), { ok: false, reason: 'unsupported_for_chat' });
+  assert.deepEqual(verdict(imageOnly), { ok: false });
 
   const partial = {
     providerType: 'openai' as const,
@@ -92,12 +85,7 @@ test('chat-default validation blocks image-only models but accepts merged partia
   };
   const [entry] = buildModelCatalogEntries(partial);
   assert.equal(entry?.canUseAsChatDefault, true);
-  assert.deepEqual(entry?.capabilities, {
-    reasoning: true,
-    functionCalling: true,
-    imageGeneration: true,
-    vision: true,
-  });
+  assert.equal(entry?.supportsVision, true);
   assert.deepEqual(verdict(partial), { ok: true });
 });
 
@@ -112,7 +100,7 @@ test('a declared output modality without text rules a model out of chat', () =>
     models: [{ id: 'gpt-image-2' }],
     modelSource: 'fetched' as const,
   };
-  assert.deepEqual(verdict(imageOnly), { ok: false, reason: 'unsupported_for_chat' });
+  assert.deepEqual(verdict(imageOnly), { ok: false });
 
   // Audio-only too, and a stray `reasoning: true` on a TTS model does not
   // rescue it: reasoning describes how it composes speech, not that it can
@@ -123,7 +111,7 @@ test('a declared output modality without text rules a model out of chat', () =>
     models: [{ id: 'gemini-3.1-flash-tts-preview' }],
     modelSource: 'fetched' as const,
   };
-  assert.deepEqual(verdict(audioOnly), { ok: false, reason: 'unsupported_for_chat' });
+  assert.deepEqual(verdict(audioOnly), { ok: false });
 });
 
 test('an empty output modality list is not evidence against chat', () => {
@@ -157,40 +145,6 @@ test('an explicit chat capability outranks the declared output modality', () =>
   assert.deepEqual(verdict(contradictory), { ok: true });
 });
 
-test('catalog entries preserve advertised parallel tool-call support', () => {
-  const [entry] = buildModelCatalogEntries({
-    providerType: 'openai-compatible',
-    defaultModel: 'relay-model',
-    models: [
-      {
-        id: 'relay-model',
-        capabilities: { functionCalling: true, parallelToolCalls: true },
-      },
-    ],
-    modelSource: 'fetched',
-  });
-  assert.deepEqual(entry?.capabilities, {
-    functionCalling: true,
-    parallelToolCalls: true,
-  });
-});
-
-test('stale provider inventory warns without blocking sends', () => {
-  const input = {
-    providerType: 'anthropic' as const,
-    defaultModel: 'claude-sonnet-4-5-20250929',
-    models: [{ id: 'claude-sonnet-4-5-20250929' }],
-    modelSource: 'fetched' as const,
-    modelsFetchedAt: 1_700_000_000_000,
-    now: 1_800_000_000_000,
-    staleAfterMs: 1,
-  };
-  const [entry] = buildModelCatalogEntries(input);
-  assert.equal(entry?.unavailableReason, 'stale');
-  assert.equal(entry?.canUseAsChatDefault, true);
-  assert.deepEqual(verdict(input), { ok: true });
-});
-
 test('the catalog and the readiness gate agree that no catalog is a veto', () => {
   // The picker must not offer a model the send gate refuses, nor hide one it
   // would accept. Since neither gate refuses on catalog membership any more,
@@ -224,13 +178,6 @@ test('the catalog and the readiness gate agree that no catalog is a veto', () =>
     assert.deepEqual(verdict(catalog(modelSource)), { ok: true }, modelSource);
     assert.deepEqual(readiness(modelSource), { ready: true, model: 'custom-default' }, modelSource);
   }
-  // They still differ on what they SAY: a live list that omits the model has
-  // something to report, a shipped snapshot has nothing.
-  assert.equal(
-    buildModelCatalogEntries(catalog('fetched'))[0]?.unavailableReason,
-    'not_in_live_list',
-  );
-  assert.equal(buildModelCatalogEntries(catalog('fallback'))[0]?.unavailableReason, 'none');
 });
 
 test('failed or pending discovery keeps the static fallback catalog visible', () => {
@@ -242,10 +189,10 @@ test('failed or pending discovery keeps the static fallback catalog visible', ()
   });
 
   assert.deepEqual(
-    entries.map(({ id, source, unavailableReason }) => [id, source, unavailableReason]),
+    entries.map(({ id, canUseAsChatDefault }) => [id, canUseAsChatDefault]),
     [
-      ['gpt-5.4', 'static_catalog', 'none'],
-      ['gpt-5-mini', 'static_catalog', 'none'],
+      ['gpt-5.4', true],
+      ['gpt-5-mini', true],
     ],
   );
 });
@@ -260,8 +207,8 @@ test('an explicitly fetched empty inventory remains authoritative', () => {
   });
 
   assert.deepEqual(
-    entries.map(({ id, unavailableReason }) => [id, unavailableReason]),
-    [['gpt-5.4', 'not_in_live_list']],
+    entries.map(({ id }) => id),
+    ['gpt-5.4'],
   );
 });
 
@@ -277,24 +224,14 @@ test('a persisted empty discovery result preserves the connection fallback throu
     updatedAt: 1,
   };
 
-  const entries = buildConnectionModelCatalogEntries({
-    connection,
-    fallbackModels: ['gpt-5.4', 'gpt-5-mini'],
-    providerAvailable: true,
-    authOk: true,
-  });
+  const entries = buildConnectionModelCatalogEntries({ connection });
 
-  assert.deepEqual(
-    entries.map(({ id, unavailableReason, provenance }) => [
-      id,
-      unavailableReason,
-      provenance.modelSource,
-    ]),
-    [
-      ['gpt-5.4', 'none', 'fallback'],
-      ['gpt-5-mini', 'none', 'fallback'],
-    ],
-  );
+  // The provider's own offerable list stands in for the empty stored one, and
+  // every id in it is selectable — including the persisted default, which the
+  // empty array would otherwise have left as the connection's only entry.
+  assert.ok(entries.length > 1);
+  assert.ok(entries.some(({ id }) => id === 'gpt-5.4'));
+  assert.ok(entries.every(({ canUseAsChatDefault }) => canUseAsChatDefault));
 });
 
 test('connection catalogs list every model the user saved without inventing availability', () => {
@@ -310,24 +247,17 @@ test('connection catalogs list every model the user saved without inventing avai
     updatedAt: 1,
   };
   const entries = buildConnectionModelCatalogEntries({
-    connection,
-    savedModelIds: ['session-model', 'glm-4.7', ' '],
+    connection: { ...connection, enabledModelIds: ['session-model', 'glm-4.7', ' '] },
   });
 
-  // All three are selectable; what differs is what the catalog knows about
-  // them. The two the live response omitted carry `not_in_live_list` so the
-  // picker can say so, but saying so is not refusing (#1584).
+  // All three are listed and all three are selectable: a live response that
+  // omitted two of them has not refused them (#1584). The blank id is dropped.
   assert.deepEqual(
-    entries.map(({ id, source, canUseAsChatDefault, unavailableReason }) => [
-      id,
-      source,
-      canUseAsChatDefault,
-      unavailableReason,
-    ]),
+    entries.map(({ id, canUseAsChatDefault }) => [id, canUseAsChatDefault]),
     [
-      ['saved-default', 'unknown', true, 'not_in_live_list'],
-      ['glm-4.7', 'provider_api', true, 'none'],
-      ['session-model', 'unknown', true, 'not_in_live_list'],
+      ['saved-default', true],
+      ['glm-4.7', true],
+      ['session-model', true],
     ],
   );
 });
@@ -353,7 +283,6 @@ test('every picker sees a model the user enabled but no catalog describes', () =
   });
   const declared = entries.find(({ id }) => id === 'deepseek-v4-pro-beta');
   assert.equal(declared?.canUseAsChatDefault, true);
-  assert.equal(declared?.unavailableReason, 'none');
 });
 
 test('catalog provenance follows the projected model facts marker used in production', () => {
@@ -416,7 +345,6 @@ test('fallback provider catalogs apply facts to known fallback models', () => {
   });
   const entry = entries.find((candidate) => candidate.id === 'nemotron-3-ultra-free');
   assert.equal(entry?.contextWindow, 200_000);
-  assert.equal(entry?.inputLimit, 200_000);
 });
 
 test('unknown persisted provider ids return an empty catalog', () => {
@@ -450,14 +378,7 @@ test('Alibaba Token Plan catalogs the formal Qwen3.8 model instead of its retire
     const model = entries.find((entry) => entry.id === modelId);
     assert.equal(model?.displayName, 'Qwen3.8 Max', providerType);
     assert.equal(model?.contextWindow, 1_000_000, providerType);
-    assert.equal(model?.maxOutputTokens, 131_072, providerType);
-    assert.equal(model?.structuredOutput, true, providerType);
-    assert.deepEqual(
-      model?.capabilities,
-      { vision: true, reasoning: true, functionCalling: true },
-      providerType,
-    );
-    assert.deepEqual(model?.modalities, { input: ['text', 'image', 'pdf'], output: ['text'] });
+    assert.equal(model?.supportsVision, true, providerType);
     assert.equal(model?.canUseAsChatDefault, true, providerType);
   }
 });
@@ -479,10 +400,7 @@ test('Alibaba (China) catalogs Qwen3.8 Max as the default model on the China end
   const model = entries.find((entry) => entry.id === 'qwen3.8-max');
   assert.equal(model?.displayName, 'Qwen3.8 Max');
   assert.equal(model?.contextWindow, 1_000_000);
-  assert.equal(model?.maxOutputTokens, 131_072);
-  assert.equal(model?.structuredOutput, true);
-  assert.deepEqual(model?.capabilities, { vision: true, reasoning: true, functionCalling: true });
-  assert.deepEqual(model?.modalities, { input: ['text', 'image', 'pdf'], output: ['text'] });
+  assert.equal(model?.supportsVision, true);
   assert.equal(model?.canUseAsChatDefault, true);
 });
 
@@ -503,18 +421,8 @@ test('DeepSeek catalogs the V4 vision model display metadata from a bare provide
     model?.description,
     'Experimental DeepSeek V4 Flash model for image understanding and multimodal agent tasks',
   );
-  assert.equal(model?.docsUrl, 'https://api-docs.deepseek.com/guides/vision/');
   assert.equal(model?.contextWindow, 1_000_000);
-  assert.equal(model?.maxOutputTokens, 384_000);
-  assert.equal(model?.structuredOutput, true);
-  assert.equal(model?.lastUpdated, '2026-08-21');
-  assert.deepEqual(model?.capabilities, {
-    reasoning: true,
-    functionCalling: true,
-    vision: true,
-    webSearch: true,
-  });
-  assert.deepEqual(model?.modalities, { input: ['text', 'image'], output: ['text'] });
+  assert.equal(model?.supportsVision, true);
   assert.equal(model?.canUseAsChatDefault, true);
 });
 
diff --git a/packages/core/src/__tests__/provider-catalog-contract.test.ts b/packages/core/src/__tests__/provider-catalog-contract.test.ts
index 2db51dde6a..0d052893c5 100644
--- a/packages/core/src/__tests__/provider-catalog-contract.test.ts
+++ b/packages/core/src/__tests__/provider-catalog-contract.test.ts
@@ -160,16 +160,10 @@ describe('retired provider contract', () => {
           defaultModel: PROVIDER_REGISTRY[type].fallbackModels[0] ?? '',
           models: undefined,
           modelSource: 'fallback',
-          modelsFetchedAt: undefined,
         },
-        // The caller would pass `true` for a live connection; retirement must
-        // win over it rather than depend on the caller getting it right.
-        providerAvailable: true,
-        authOk: true,
       });
       assert.ok(entries.length > 0, `${type} should still list its stored models`);
       for (const entry of entries) {
-        assert.equal(entry.unavailableReason, 'provider_removed');
         assert.equal(entry.canUseAsChatDefault, false);
       }
     }
diff --git a/packages/core/src/chat-model-choice.ts b/packages/core/src/chat-model-choice.ts
index fd2b0a04ef..7b12b20ab5 100644
--- a/packages/core/src/chat-model-choice.ts
+++ b/packages/core/src/chat-model-choice.ts
@@ -88,7 +88,7 @@ export function buildChatModelChoices(
         ...(provider.authKind === 'oauth_token' ? {} : { connectionName: connection.name }),
         isDefault: entry.isDefault,
         thinkingLevels: entry.thinkingLevels,
-        supportsVision: entry.capabilities.vision === true,
+        supportsVision: entry.supportsVision,
       });
     }
   }
diff --git a/packages/core/src/model-catalog.ts b/packages/core/src/model-catalog.ts
index 92100d553b..9f403a933c 100644
--- a/packages/core/src/model-catalog.ts
+++ b/packages/core/src/model-catalog.ts
@@ -25,13 +25,11 @@ import type {
   ProviderType,
 } from './llm-connections.js';
 import {
-  classifyConnectionModelInventory,
   CODEX_SUBSCRIPTION_UNSUPPORTED_CHATGPT_MODELS,
   connectionEnabledModelIds,
   PROVIDER_REGISTRY,
   providerDefaultsOf,
   providerSupportsModelDiscovery,
-  type ConnectionModelInventory,
   type HostResolvedConnectionCatalog,
 } from './llm-connections.js';
 import type { PricingConfig } from './usage-stats/types.js';
@@ -48,32 +46,6 @@ import {
 } from './model-thinking.js';
 import { pricingModelKey } from './usage-stats/pricing.js';
 
-export type ModelUnavailableReason =
-  | 'none'
-  | 'not_in_live_list'
-  | 'unsupported_for_chat'
-  | 'provider_removed'
-  | 'auth'
-  | 'stale';
-
-export type ModelCatalogLifecycle =
-  | 'active'
-  | 'beta'
-  | 'alpha'
-  | 'deprecated'
-  | 'retired'
-  | 'unknown';
-
-export interface KnownModelCapabilities {
-  chat?: true;
-  vision?: true;
-  reasoning?: true;
-  functionCalling?: true;
-  parallelToolCalls?: true;
-  imageGeneration?: true;
-  webSearch?: true;
-}
-
 export interface ModelCatalogPricing {
   inputUsdPer1M: number;
   outputUsdPer1M: number;
@@ -82,17 +54,28 @@ export interface ModelCatalogPricing {
   source: 'builtin' | 'user_override';
 }
 
+/**
+ * One model as the Host resolved it for one connection.
+ *
+ * Every field here has a reader. The entry crosses the wire and then the
+ * desktop IPC boundary, so a field nothing renders is paid for on every
+ * catalog read by every attached client — and the ones that were here
+ * (`providerType`, `connectionSlug`, `source`, `unavailableReason`,
+ * `lifecycle`, `docsUrl`, `inputLimit`, `maxOutputTokens`, `structuredOutput`,
+ * `lastUpdated`, `modalities`, `provenance`, and every capability but vision)
+ * had none. They are not needed today; when a surface actually asks for one,
+ * add it back with the reader that wants it. `makeEntry` still consults all of
+ * those facts to decide `canUseAsChatDefault` — they simply stop being shipped.
+ */
 export interface ModelCatalogEntry {
   id: string;
   displayName?: string;
   description?: string;
-  providerType: ProviderType;
-  connectionSlug?: string;
-  source: 'provider_api' | 'static_catalog' | 'unknown';
-  unavailableReason: ModelUnavailableReason;
+  /** False when this connection cannot hold a chat on this model. */
   canUseAsChatDefault: boolean;
   isDefault: boolean;
-  capabilities: KnownModelCapabilities;
+  /** Exact capability projection used by model-facing attachment composition. */
+  supportsVision: boolean;
   /**
    * Reasoning levels this model offers on this connection, in display order;
    * empty for a non-reasoning model. Part of the entry rather than a second
@@ -102,21 +85,17 @@ export interface ModelCatalogEntry {
    * thinking projection honoured.
    */
   thinkingLevels: readonly ThinkingLevel[];
-  lifecycle: ModelCatalogLifecycle;
-  docsUrl?: string;
   contextWindow?: number;
-  inputLimit?: number;
-  maxOutputTokens?: number;
   knowledgeCutoff?: string;
-  structuredOutput?: boolean;
-  lastUpdated?: string;
-  modalities?: ModelInfo['modalities'];
+  /**
+   * Per-1M rates for this model. The only field kept without a producer: no
+   * caller passes `pricing` yet, so it is always absent today. Cost accounting
+   * itself does not depend on it — `record-llm-call.ts` prices a call from
+   * `pricingModelKey` when the call is recorded. This is the seam for showing
+   * a rate beside a model in a picker, and stays until that surface exists or
+   * is ruled out.
+   */
   pricing?: ModelCatalogPricing;
-  provenance: {
-    modelSource?: ModelDiscoverySource;
-    modelsFetchedAt?: number;
-    pricingModelKey?: string;
-  };
 }
 
 export interface BuildConnectionModelCatalogInput {
@@ -128,32 +107,20 @@ export interface BuildConnectionModelCatalogInput {
     | 'enabledModelIds'
     | 'models'
     | 'modelSource'
-    | 'modelsFetchedAt'
     | 'relayModelProfiles'
   >;
-  /** Ids the catalog must list even when no inventory describes them (#1584). */
-  savedModelIds?: Iterable<string | undefined | null>;
-  fallbackModels?: string[];
-  now?: number;
-  staleAfterMs?: number;
-  providerAvailable?: boolean;
-  authOk?: boolean;
   pricing?: Iterable<PricingConfig>;
   pricingSource?: 'builtin' | 'user_override';
 }
 
 export interface BuildModelCatalogInput {
   providerType: ProviderType;
-  connectionSlug?: string;
   defaultModel?: string;
   models?: ModelInfo[];
   modelSource?: ModelDiscoverySource;
-  modelsFetchedAt?: number;
   fallbackModels?: string[];
-  now?: number;
-  staleAfterMs?: number;
-  providerAvailable?: boolean;
-  authOk?: boolean;
+  /** A provider Maka has retired: its models list but can no longer be chosen. */
+  providerRetired?: boolean;
   pricing?: Iterable<PricingConfig>;
   pricingSource?: 'builtin' | 'user_override';
   /** Ids the catalog must list even when no inventory describes them (#1584). */
@@ -162,22 +129,12 @@ export interface BuildModelCatalogInput {
   relayModelProfiles?: RelayModelProfiles;
 }
 
-const DEFAULT_STALE_AFTER_MS = 7 * 24 * 60 * 60 * 1000;
-
 export function buildModelCatalogEntries(input: BuildModelCatalogInput): ModelCatalogEntry[] {
   const liveModels = input.models;
   const modelSource =
     input.modelSource ??
     (liveModels !== undefined && liveModels.length > 0 ? 'fetched' : 'fallback');
-  // The RAW `modelSource`, not a source inferred from the array, distinguishes
-  // a failed discovery from an explicit empty provider response.
-  const inventory = classifyConnectionModelInventory({
-    providerType: input.providerType,
-    models: input.models,
-    modelSource: input.modelSource,
-  });
   const normalizedDefaultModel = input.defaultModel?.trim();
-  const source = inventory === 'live' ? 'provider_api' : 'static_catalog';
   // An empty array without a successful discovery source is the persisted
   // shape of a failed or not-yet-run discovery. It must not hide the static
   // fallback catalog from the picker. An empty fetched array is different: it
@@ -190,11 +147,7 @@ export function buildModelCatalogEntries(input: BuildModelCatalogInput): ModelCa
           ...displayNameForKnownModel(input.providerType, id),
         }));
   const savedModelIds = normalizedIdSet(input.savedModelIds);
-  const ctx: EntryContext = {
-    input,
-    modelSource,
-    normalizedDefaultModel,
-  };
+  const ctx: EntryContext = { input, normalizedDefaultModel };
   const seen = new Set<string>();
   const entries = rawModels
     .filter((model) => {
@@ -203,17 +156,17 @@ export function buildModelCatalogEntries(input: BuildModelCatalogInput): ModelCa
       seen.add(id);
       return true;
     })
-    .map((model) => makeEntry(ctx, model, source));
+    .map((model) => makeEntry(ctx, model));
 
   if (normalizedDefaultModel && !seen.has(normalizedDefaultModel)) {
-    entries.unshift(makeMissingEntry(ctx, normalizedDefaultModel, inventory, { isDefault: true }));
+    entries.unshift(makeEntry(ctx, { id: normalizedDefaultModel }, { isDefault: true }));
     seen.add(normalizedDefaultModel);
   }
 
   for (const id of savedModelIds) {
     if (seen.has(id)) continue;
     seen.add(id);
-    entries.push(makeMissingEntry(ctx, id, inventory));
+    entries.push(makeEntry(ctx, { id }));
   }
 
   return entries;
@@ -293,7 +246,7 @@ export function buildConnectionModelCatalogEntries(
   // Fallback providers have no live inventory, but a projected connection can
   // still carry enabled model-facts entries that are absent from the static
   // list. Keep both sets in the catalog so those user-declared models retain
-  // their metadata and provenance.
+  // their metadata.
   const models = supportsModelDiscovery
     ? connection.models?.filter(({ id }) => !broken.has(id))
     : [
@@ -310,22 +263,15 @@ export function buildConnectionModelCatalogEntries(
       ];
   return buildModelCatalogEntries({
     providerType: connection.providerType,
-    connectionSlug: connection.slug,
     defaultModel,
     models,
     modelSource: supportsModelDiscovery ? connection.modelSource : 'fallback',
-    modelsFetchedAt: supportsModelDiscovery ? connection.modelsFetchedAt : undefined,
-    fallbackModels: supportsModelDiscovery
-      ? (input.fallbackModels ?? fallbackModels)
-      : fallbackModels,
-    now: input.now,
-    staleAfterMs: input.staleAfterMs,
+    fallbackModels,
     // A retired provider's models stay listed so an existing connection still
-    // renders, but they resolve to `provider_removed` and stop being selectable.
-    // Without this the pickers would keep offering models that can no longer
-    // send — `runtimeAdapter: 'unavailable'` blocks the send, not the choice.
-    providerAvailable: defaults.retired === true ? false : input.providerAvailable,
-    authOk: input.authOk,
+    // renders, but they stop being selectable. Without this the pickers would
+    // keep offering models that can no longer send — `runtimeAdapter:
+    // 'unavailable'` blocks the send, not the choice.
+    providerRetired: defaults.retired === true,
     pricing: input.pricing,
     pricingSource: input.pricingSource,
     ...(connection.relayModelProfiles ? { relayModelProfiles: connection.relayModelProfiles } : {}),
@@ -336,9 +282,7 @@ export function buildConnectionModelCatalogEntries(
     // (#1584), and fixing it at one call site left the others broken. The raw
     // array, not `connectionEnabledModelIds`: that one folds in `defaultModel`,
     // which the builder already lists on its own.
-    savedModelIds: [...(connection.enabledModelIds ?? []), ...(input.savedModelIds ?? [])].filter(
-      (id) => !broken.has(id ?? ''),
-    ),
+    savedModelIds: (connection.enabledModelIds ?? []).filter((id) => !broken.has(id)),
   });
 }
 
@@ -451,9 +395,10 @@ function draftMatchesConnection(
 }
 
 /**
- * Every stored field a catalog entry can be built from. Comparing ids alone
+ * Every stored field `makeEntry` reads, and only those. Comparing ids alone
  * would keep showing the Host's entries for rows the user just re-fetched,
- * whose facts may differ under the same id.
+ * whose facts may differ under the same id; comparing fields no entry is built
+ * from would throw the Host's entries away over a change nothing can render.
  */
 function modelRowsEqual(left: readonly ModelInfo[], right: readonly ModelInfo[]): boolean {
   if (left.length !== right.length) return false;
@@ -462,19 +407,29 @@ function modelRowsEqual(left: readonly ModelInfo[], right: readonly ModelInfo[])
     return (
       model.id === other.id &&
       model.displayName === other.displayName &&
+      model.description === other.description &&
       model.contextWindow === other.contextWindow &&
-      model.inputLimit === other.inputLimit &&
-      model.maxOutputTokens === other.maxOutputTokens &&
+      model.knowledgeCutoff === other.knowledgeCutoff &&
+      modalitiesEqual(model.modalities, other.modalities) &&
       model.capabilities?.chat === other.capabilities?.chat &&
       model.capabilities?.vision === other.capabilities?.vision &&
       model.capabilities?.reasoning === other.capabilities?.reasoning &&
       model.capabilities?.functionCalling === other.capabilities?.functionCalling &&
-      model.capabilities?.parallelToolCalls === other.capabilities?.parallelToolCalls &&
       model.capabilities?.imageGeneration === other.capabilities?.imageGeneration
     );
   });
 }
 
+function modalitiesEqual(left: ModelInfo['modalities'], right: ModelInfo['modalities']): boolean {
+  if (left === undefined || right === undefined) return left === right;
+  return (
+    left.input.length === right.input.length &&
+    left.input.every((value, index) => value === right.input[index]) &&
+    left.output.length === right.output.length &&
+    left.output.every((value, index) => value === right.output[index])
+  );
+}
+
 /**
  * The per-build facts every entry in one catalog shares. Threading them as one
  * value keeps the entry builders' remaining parameters to what actually varies
@@ -482,38 +437,32 @@ function modelRowsEqual(left: readonly ModelInfo[], right: readonly ModelInfo[])
  */
 interface EntryContext {
   readonly input: BuildModelCatalogInput;
-  readonly modelSource: ModelDiscoverySource;
   readonly normalizedDefaultModel: string | undefined;
 }
 
-/**
- * The facts an entry cannot derive from its model row. A model the catalog
- * never listed has no row to derive them from: its unavailability is a
- * property of the inventory rather than of the model, and a missing default
- * is default by construction.
- */
+/** The one fact an entry cannot derive: a missing default is default by construction. */
 interface EntryOverrides {
-  readonly unavailableReason?: ModelUnavailableReason;
   readonly isDefault?: boolean;
 }
 
+/**
+ * One entry, from a model row or from a bare id no inventory describes. The
+ * bare-id case is the same construction: every field then resolves from the
+ * bundled metadata alone, which is what those entries carried when they were
+ * built by a separate function.
+ */
 function makeEntry(
   ctx: EntryContext,
   model: ModelInfo,
-  source: ModelCatalogEntry['source'],
   overrides: EntryOverrides = {},
 ): ModelCatalogEntry {
-  const { input, modelSource, normalizedDefaultModel } = ctx;
+  const { input, normalizedDefaultModel } = ctx;
   const normalizedModel = { ...model, id: model.id.trim() };
   const pricing = findPricing(input, normalizedModel.id);
   const metadata = lookupModelMetadata(input.providerType, normalizedModel.id);
   const contextWindow = normalizedModel.contextWindow ?? metadata.contextWindow;
-  const inputLimit = normalizedModel.inputLimit ?? metadata.inputLimit;
-  const maxOutputTokens = normalizedModel.maxOutputTokens ?? metadata.maxOutputTokens;
   const description = normalizedModel.description ?? metadata.description;
   const knowledgeCutoff = normalizedModel.knowledgeCutoff ?? metadata.knowledgeCutoff;
-  const structuredOutput = normalizedModel.structuredOutput ?? metadata.structuredOutput;
-  const lastUpdated = normalizedModel.lastUpdated ?? metadata.lastUpdated;
   const modalities = normalizedModel.modalities ?? metadata.modalities;
   // The user's per-model declaration outranks every catalog source, so both
   // capability reads that honour it — vision and thinking — resolve here
@@ -536,9 +485,14 @@ function makeEntry(
   // reads the modality. Passing the unmerged `normalizedModel.modalities`
   // meant a bundled image-only model reached the guard with no output
   // declaration at all.
-  const unavailableReason =
-    overrides.unavailableReason ??
-    deriveModelUnavailableReason(input, {
+  // Retirement and an explicit "cannot chat" are the only two vetoes. Absence
+  // from a live list is NOT one: a provider that did not mention a model has
+  // not refused it, and only the provider can refuse, when the request goes
+  // out (#1584). So an id the user enabled that no inventory describes stays
+  // selectable, and reaches here as a bare row whose metadata says nothing.
+  const canUseAsChatDefault =
+    input.providerRetired !== true &&
+    !isModelExplicitlyUnsupportedForChat({
       ...normalizedModel,
       capabilities,
       ...(modalities !== undefined ? { modalities } : {}),
@@ -547,51 +501,16 @@ function makeEntry(
     id: normalizedModel.id,
     ...displayNameForModel(input.providerType, normalizedModel),
     ...(description !== undefined ? { description } : {}),
-    providerType: input.providerType,
-    ...(input.connectionSlug ? { connectionSlug: input.connectionSlug } : {}),
-    source,
-    unavailableReason,
-    canUseAsChatDefault: canUseUnavailableReasonAsDefault(unavailableReason),
+    canUseAsChatDefault,
     isDefault: overrides.isDefault ?? normalizedModel.id === normalizedDefaultModel,
-    capabilities: normalizeCapabilities(capabilities),
+    supportsVision: capabilities.vision === true,
     thinkingLevels: thinkingVariantsForConnection(thinkingContext, normalizedModel.id),
-    lifecycle: metadata.lifecycle ?? 'unknown',
-    ...(metadata.docsUrl ? { docsUrl: metadata.docsUrl } : {}),
     ...(contextWindow !== undefined ? { contextWindow } : {}),
-    ...(inputLimit !== undefined ? { inputLimit } : {}),
-    ...(maxOutputTokens !== undefined ? { maxOutputTokens } : {}),
     ...(knowledgeCutoff !== undefined ? { knowledgeCutoff } : {}),
-    ...(structuredOutput !== undefined ? { structuredOutput } : {}),
-    ...(lastUpdated !== undefined ? { lastUpdated } : {}),
-    ...(modalities !== undefined ? { modalities } : {}),
     ...(pricing ? { pricing } : {}),
-    provenance: {
-      modelSource,
-      ...(input.modelsFetchedAt ? { modelsFetchedAt: input.modelsFetchedAt } : {}),
-      ...(pricing
-        ? { pricingModelKey: pricingModelKey(input.providerType, normalizedModel.id) }
-        : {}),
-    },
   };
 }
 
-/**
- * An entry for an id no catalog row describes. It is `makeEntry` over a bare
- * model row: every field then resolves from the bundled metadata alone, which
- * is exactly what these entries carried when they were built separately.
- */
-function makeMissingEntry(
-  ctx: EntryContext,
-  id: string,
-  inventory: ConnectionModelInventory,
-  overrides: Omit<EntryOverrides, 'unavailableReason'> = {},
-): ModelCatalogEntry {
-  return makeEntry(ctx, { id }, 'unknown', {
-    unavailableReason: missingEntryUnavailableReason(ctx.input, inventory),
-    ...overrides,
-  });
-}
-
 function mergeCapabilities(
   providerCapabilities: ModelInfo['capabilities'] | undefined,
   metadataCapabilities: ModelInfo['capabilities'] | undefined,
@@ -627,61 +546,6 @@ function displayNameForKnownModel(
   return displayName ? { displayName } : {};
 }
 
-function deriveModelUnavailableReason(
-  input: Pick<
-    BuildModelCatalogInput,
-    | 'providerType'
-    | 'providerAvailable'
-    | 'authOk'
-    | 'models'
-    | 'modelSource'
-    | 'modelsFetchedAt'
-    | 'now'
-    | 'staleAfterMs'
-  >,
-  model: ModelInfo,
-): ModelUnavailableReason {
-  const providerOrAuthReason = providerOrAuthUnavailableReason(input);
-  if (providerOrAuthReason) return providerOrAuthReason;
-  if (isModelExplicitlyUnsupportedForChat(model)) return 'unsupported_for_chat';
-  if (isStale(input)) return 'stale';
-  return 'none';
-}
-
-function providerOrAuthUnavailableReason(
-  input: Pick<BuildModelCatalogInput, 'providerAvailable' | 'authOk'>,
-): Extract<ModelUnavailableReason, 'provider_removed' | 'auth'> | null {
-  if (input.providerAvailable === false) return 'provider_removed';
-  if (input.authOk === false) return 'auth';
-  return null;
-}
-
-function missingEntryUnavailableReason(
-  input: Pick<BuildModelCatalogInput, 'providerAvailable' | 'authOk' | 'models'>,
-  inventory: ConnectionModelInventory,
-): ModelUnavailableReason {
-  const providerOrAuthReason = providerOrAuthUnavailableReason(input);
-  if (providerOrAuthReason) return providerOrAuthReason;
-  // Only a live list can say a model is absent. A snapshot describes the
-  // provider at release, so a model missing from it is simply one Maka has
-  // never heard of — not one this account cannot run (#1584).
-  return inventory === 'live' ? 'not_in_live_list' : 'none';
-}
-
-function isStale(
-  input: Pick<
-    BuildModelCatalogInput,
-    'providerType' | 'models' | 'modelSource' | 'modelsFetchedAt' | 'now' | 'staleAfterMs'
-  >,
-): boolean {
-  if (input.modelsFetchedAt === undefined) return false;
-  // Only a live list can go stale. A snapshot is as current as the build.
-  if (classifyConnectionModelInventory(input) !== 'live') return false;
-  const now = input.now ?? Date.now();
-  const staleAfterMs = input.staleAfterMs ?? DEFAULT_STALE_AFTER_MS;
-  return now - input.modelsFetchedAt > staleAfterMs;
-}
-
 /**
  * Whether a declared output modality rules the model out of chat.
  *
@@ -718,27 +582,6 @@ export function isModelExplicitlyUnsupportedForChat(model: ModelInfo): boolean {
   );
 }
 
-function normalizeCapabilities(caps: ModelInfo['capabilities']): KnownModelCapabilities {
-  if (!caps) return {};
-  return {
-    ...(caps.chat === true ? { chat: true as const } : {}),
-    ...(caps.vision === true ? { vision: true as const } : {}),
-    ...(caps.reasoning === true ? { reasoning: true as const } : {}),
-    ...(caps.functionCalling === true ? { functionCalling: true as const } : {}),
-    ...(caps.parallelToolCalls === true ? { parallelToolCalls: true as const } : {}),
-    ...(caps.imageGeneration === true ? { imageGeneration: true as const } : {}),
-    ...(caps.webSearch === true ? { webSearch: true as const } : {}),
-  };
-}
-
-function canUseUnavailableReasonAsDefault(reason: ModelUnavailableReason): boolean {
-  // `stale` and `not_in_live_list` are both things worth saying and neither is
-  // a fact about what the account can run. A provider that did not mention a
-  // model in its last response has not refused it; only the provider itself
-  // can do that, when the request goes out (#1584).
-  return reason === 'none' || reason === 'stale' || reason === 'not_in_live_list';
-}
-
 function normalizedIdSet(ids: Iterable<string | undefined | null> | undefined): Set<string> {
   const result = new Set<string>();
   for (const id of ids ?? []) {
diff --git a/packages/core/src/runtime-policy/model-catalog-entry-codec.ts b/packages/core/src/runtime-policy/model-catalog-entry-codec.ts
index e85fbfb671..870d4c253d 100644
--- a/packages/core/src/runtime-policy/model-catalog-entry-codec.ts
+++ b/packages/core/src/runtime-policy/model-catalog-entry-codec.ts
@@ -18,42 +18,10 @@
  */
 
 import { isThinkingLevel, type ThinkingLevel } from '../model-thinking.js';
-import type {
-  KnownModelCapabilities,
-  ModelCatalogEntry,
-  ModelCatalogLifecycle,
-  ModelCatalogPricing,
-} from '../model-catalog.js';
-import { decodeConnectionModel, decodeProviderType } from './connection-catalog-codec.js';
-import {
-  booleanValue,
-  domainError,
-  exactRecord,
-  integerValue,
-  nonEmptyStringValue,
-  stringValue,
-} from './domain-codec.js';
+import type { ModelCatalogEntry, ModelCatalogPricing } from '../model-catalog.js';
+import { decodeConnectionModel } from './connection-catalog-codec.js';
+import { booleanValue, domainError, exactRecord, stringValue } from './domain-codec.js';
 
-const ENTRY_SOURCES = ['provider_api', 'static_catalog', 'unknown'] as const;
-const UNAVAILABLE_REASONS = [
-  'none',
-  'not_in_live_list',
-  'unsupported_for_chat',
-  'provider_removed',
-  'auth',
-  'stale',
-] as const;
-const LIFECYCLES = ['active', 'beta', 'alpha', 'deprecated', 'retired', 'unknown'] as const;
-const CAPABILITY_KEYS = [
-  'chat',
-  'vision',
-  'reasoning',
-  'functionCalling',
-  'parallelToolCalls',
-  'imageGeneration',
-  'webSearch',
-] as const satisfies readonly (keyof KnownModelCapabilities)[];
-const MODEL_SOURCES = ['fetched', 'fallback'] as const;
 const PRICING_SOURCES = ['builtin', 'user_override'] as const;
 
 /**
@@ -70,91 +38,33 @@ export function decodeModelCatalogEntry(value: unknown): ModelCatalogEntry {
       'id',
       'displayName',
       'description',
-      'providerType',
-      'connectionSlug',
-      'source',
-      'unavailableReason',
       'canUseAsChatDefault',
       'isDefault',
-      'capabilities',
+      'supportsVision',
       'thinkingLevels',
-      'lifecycle',
-      'docsUrl',
       'contextWindow',
-      'inputLimit',
-      'maxOutputTokens',
       'knowledgeCutoff',
-      'structuredOutput',
-      'lastUpdated',
-      'modalities',
       'pricing',
-      'provenance',
-    ],
-    [
-      'id',
-      'providerType',
-      'source',
-      'unavailableReason',
-      'canUseAsChatDefault',
-      'isDefault',
-      'capabilities',
-      'thinkingLevels',
-      'lifecycle',
-      'provenance',
     ],
+    ['id', 'canUseAsChatDefault', 'isDefault', 'supportsVision', 'thinkingLevels'],
   );
   // The fields an entry shares with a stored model row keep one decoder, so a
   // bound that moves moves for both. `decodeConnectionModel` rejects unknown
   // fields, so it is handed exactly the subset it owns.
   const shared = decodeConnectionModel({
     id: item.id,
-    ...pick(item, [
-      'displayName',
-      'description',
-      'contextWindow',
-      'inputLimit',
-      'maxOutputTokens',
-      'knowledgeCutoff',
-      'structuredOutput',
-      'lastUpdated',
-      'modalities',
-    ]),
+    ...pick(item, ['displayName', 'description', 'contextWindow', 'knowledgeCutoff']),
   });
   return {
     ...shared,
-    providerType: decodeProviderType(item.providerType),
-    ...(item.connectionSlug === undefined
-      ? {}
-      : { connectionSlug: nonEmptyStringValue(item.connectionSlug, 'entry connection slug', 128) }),
-    source: oneOf(item.source, ENTRY_SOURCES, 'entry source'),
-    unavailableReason: oneOf(
-      item.unavailableReason,
-      UNAVAILABLE_REASONS,
-      'entry unavailable reason',
-    ),
     canUseAsChatDefault: booleanValue(item.canUseAsChatDefault, 'entry chat default eligibility'),
     isDefault: booleanValue(item.isDefault, 'entry default flag'),
-    capabilities: decodeKnownCapabilities(item.capabilities),
+    supportsVision: booleanValue(item.supportsVision, 'entry vision support'),
     thinkingLevels: decodeThinkingLevels(item.thinkingLevels),
-    lifecycle: oneOf<ModelCatalogLifecycle>(item.lifecycle, LIFECYCLES, 'entry lifecycle'),
-    ...(item.docsUrl === undefined
-      ? {}
-      : { docsUrl: nonEmptyStringValue(item.docsUrl, 'entry docs URL', 2048) }),
     ...(item.pricing === undefined ? {} : { pricing: decodePricing(item.pricing) }),
-    provenance: decodeProvenance(item.provenance),
   };
 }
 
-function decodeKnownCapabilities(value: unknown): KnownModelCapabilities {
-  const raw = exactRecord(value, 'entry capabilities', CAPABILITY_KEYS, []);
-  const capabilities: Record<string, true> = {};
-  for (const key of Object.keys(raw)) {
-    if (raw[key] !== true) throw domainError(`entry capability ${key} must be true when present`);
-    capabilities[key] = true;
-  }
-  return capabilities;
-}
-
 function decodeThinkingLevels(value: unknown): readonly ThinkingLevel[] {
   if (!Array.isArray(value)) throw domainError('entry thinking levels must be an array');
   const levels = value.map((level) => {
@@ -187,35 +97,6 @@ function decodePricing(value: unknown): ModelCatalogPricing {
   };
 }
 
-function decodeProvenance(value: unknown): ModelCatalogEntry['provenance'] {
-  const item = exactRecord(
-    value,
-    'entry provenance',
-    ['modelSource', 'modelsFetchedAt', 'pricingModelKey'],
-    [],
-  );
-  return {
-    ...(item.modelSource === undefined
-      ? {}
-      : { modelSource: oneOf(item.modelSource, MODEL_SOURCES, 'entry model source') }),
-    ...(item.modelsFetchedAt === undefined
-      ? {}
-      : {
-          modelsFetchedAt: integerValue(
-            item.modelsFetchedAt,
-            'entry models fetched at',
-            0,
-            Number.MAX_SAFE_INTEGER,
-          ),
-        }),
-    ...(item.pricingModelKey === undefined
-      ? {}
-      : {
-          pricingModelKey: nonEmptyStringValue(item.pricingModelKey, 'entry pricing key', 512),
-        }),
-  };
-}
-
 function priceValue(value: unknown, context: string): number {
   if (typeof value !== 'number' || !Number.isFinite(value) || value < 0) {
     throw domainError(`${context} must be a non-negative finite number`);
diff --git a/packages/runtime-host/src/__tests__/runtime-policy-coordinator.test.ts b/packages/runtime-host/src/__tests__/runtime-policy-coordinator.test.ts
index 5e44ff06cf..18a79e81d1 100644
--- a/packages/runtime-host/src/__tests__/runtime-policy-coordinator.test.ts
+++ b/packages/runtime-host/src/__tests__/runtime-policy-coordinator.test.ts
@@ -1142,9 +1142,6 @@ function expectedCatalogItems(snapshot: ConnectionCatalogSnapshot): ConnectionCa
       enabledModelIds: [...enabledModelIds],
       models: [...models],
       ...(connection.modelSource === undefined ? {} : { modelSource: connection.modelSource }),
-      ...(connection.modelsFetchedAt === undefined
-        ? {}
-        : { modelsFetchedAt: connection.modelsFetchedAt }),
       ...(relayModelProfiles === undefined ? {} : { relayModelProfiles }),
     });
     items.push({
diff --git a/packages/runtime-host/src/server/runtime-policy-coordinator.ts b/packages/runtime-host/src/server/runtime-policy-coordinator.ts
index 652483a108..0aee45b7b9 100644
--- a/packages/runtime-host/src/server/runtime-policy-coordinator.ts
+++ b/packages/runtime-host/src/server/runtime-policy-coordinator.ts
@@ -465,9 +465,6 @@ function projectCatalogItems(snapshot: ConnectionCatalogSnapshot): ConnectionCat
       enabledModelIds: [...enabledModelIds],
       models: [...models],
       ...(connection.modelSource === undefined ? {} : { modelSource: connection.modelSource }),
-      ...(connection.modelsFetchedAt === undefined
-        ? {}
-        : { modelsFetchedAt: connection.modelsFetchedAt }),
       ...(relayModelProfiles === undefined ? {} : { relayModelProfiles }),
     });
     items.push({


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@maka.apache.org
For additional commands, e-mail: commits-help@maka.apache.org