Skip to content
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
b26b865
refactor(core): build every model catalog entry through one projection
Astro-Han Aug 31, 2026
1b114eb
refactor(core): resolve a model's thinking and vision inside its cata…
Astro-Han Aug 31, 2026
ba74830
feat(runtime-host): make the Host the authority for the model catalog
Astro-Han Aug 31, 2026
95da124
refactor(core): give the provider registry one exported name
Astro-Han Aug 31, 2026
313f5de
refactor(core): drop the two catalog entry fields nothing reads
Astro-Han Aug 31, 2026
97813c6
refactor(core): drop the chat-default validator no caller had
Astro-Han Aug 31, 2026
8484ae7
refactor(core): drop the model-choice provenance nothing produces or …
Astro-Han Sep 1, 2026
79e4253
refactor(core): route the last registry lookups through providerDefau…
Astro-Han Sep 1, 2026
2a231b3
refactor(core): drop the availability field that only restated the re…
Astro-Han Sep 1, 2026
f701554
Merge origin/main into refactor/model-catalog-host-authority
Astro-Han Sep 1, 2026
455a121
test(runtime-host): declare the catalog entry count in two hosted-tar…
Astro-Han Sep 1, 2026
ab572cb
fix(core): admit the fallback rows a resolved catalog actually carries
Astro-Han Sep 1, 2026
69094af
fix(core): let the auth contract's unknown-provider branch actually run
Astro-Han Sep 1, 2026
39a7a51
fix(cli): read the TUI's opening context window from the resolved cat…
Astro-Han Sep 1, 2026
696149b
fix(core): drop unservable Codex ids from the stored selection too
Astro-Han Sep 1, 2026
53fc939
fix(desktop): show the Host's entries while the connection editor is …
Astro-Han Sep 1, 2026
3d6307e
Merge remote-tracking branch 'origin/main' into refactor/model-catalo…
Astro-Han Sep 1, 2026
1edbf85
Merge remote-tracking branch 'origin/main' into refactor/model-catalo…
Astro-Han Sep 1, 2026
3073ce0
refactor(core): ship only the catalog-entry fields something reads
Astro-Han Sep 1, 2026
d540823
Merge remote-tracking branch 'origin/main' into refactor/model-catalo…
Astro-Han Sep 1, 2026
75ed23e
refactor(core): give a provider's shipped baseline one authority
Astro-Han Sep 1, 2026
5576ed4
refactor(core): make provider recognition go through its one owner
Astro-Han Sep 1, 2026
478adba
Merge remote-tracking branch 'origin/main' into refactor/model-catalo…
Astro-Han Sep 1, 2026
dc680ac
chore: regenerate the astryx surface inventory after the merge
Astro-Han Sep 1, 2026
16b1719
refactor(core): let the Host alone decide what a picker may offer
Astro-Han Sep 1, 2026
638820d
refactor(core): keep only the admission answer in the auth contract
Astro-Han Sep 1, 2026
52d32ff
refactor(core): drop the registry fields and helpers nothing reads
Astro-Han Sep 1, 2026
8eefe46
refactor(cli): stop threading TUI fields no screen reads
Astro-Han Sep 1, 2026
052430d
refactor(desktop): read the renderer's two model constants from core
Astro-Han Sep 1, 2026
902c967
refactor(core): home the offerable-models answer on the connection mo…
Astro-Han Sep 1, 2026
ecf7e9a
fix(desktop): name models from the Host's entries everywhere a client…
Astro-Han Sep 1, 2026
7e78145
refactor(storage): stop persisting the registry's shipped inventory
Astro-Han Sep 1, 2026
ff29a65
refactor(core): drop the catalog pricing seam nothing produces
Astro-Han Sep 1, 2026
e5a0d62
refactor(core): derive a provider's wire from its adapter instead of …
Astro-Han Sep 1, 2026
2b0f47b
refactor(core): ask the one question the inventory taxonomy answered
Astro-Han Sep 1, 2026
9976b24
refactor(core): make an action's availability a boolean
Astro-Han Sep 1, 2026
b1b64c8
refactor(cli): make a model choice carry its account identity
Astro-Han Sep 1, 2026
012a21c
refactor(runtime-host): keep discovery bookkeeping off the catalog wire
Astro-Han Sep 1, 2026
64c306c
refactor(runtime-host): keep override provenance off the catalog wire
Astro-Han Sep 1, 2026
fcb8e58
refactor(core): keep only the auth actions something admits
Astro-Han Sep 1, 2026
c90dd2c
docs(runtime-host): state the catalog authority on the seam that carr…
Astro-Han Sep 1, 2026
d7081ca
docs(runtime-host): say the catalog rule in plain words
Astro-Han Sep 1, 2026
42d3adc
docs(runtime-host): drop the author line from the catalog comment
Astro-Han Sep 1, 2026
3d524e9
refactor(desktop): stop sending an enabled-model list the Host discards
Astro-Han Sep 1, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion apps/desktop/renderer-architecture.json
Original file line number Diff line number Diff line change
Expand Up @@ -4361,7 +4361,7 @@
"./subagent-preset-presentation.js": 1,
"@astryxdesign/core": 1,
"@maka/core/llm-connections": 2,
"@maka/core/model-thinking": 2,
"@maka/core/model-thinking": 1,
"@maka/core/settings": 1,
"@maka/core/subagent-settings": 1,
"@maka/ui": 1,
Expand Down
13 changes: 10 additions & 3 deletions apps/desktop/src/main/__tests__/model-catalog-choices.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,19 @@

import { strict as assert } from 'node:assert';
import { describe, it } from 'node:test';
import type { IdentifiedLlmConnection } from '@maka/core/llm-connections';
import type {
IdentifiedLlmConnection,
ProjectedLlmConnection,
} from '@maka/core/llm-connections';
import { resolveConnectionModelCatalog } from '@maka/core/model-catalog';
import { buildChatModelChoices } from '@maka/core/chat-model-choice';
import { pickNewChatModel } from '../../renderer/shell-chat-model-selection.js';

function connection(
overrides: Partial<IdentifiedLlmConnection> &
Pick<IdentifiedLlmConnection, 'slug' | 'providerType'>,
): IdentifiedLlmConnection {
return {
): ProjectedLlmConnection {
const stored: IdentifiedLlmConnection = {
connectionId: `connection-${overrides.slug}`,
name: overrides.slug,
defaultModel: '',
Expand All @@ -37,6 +41,9 @@ function connection(
updatedAt: 1,
...overrides,
};
// The Host resolves the catalog and projects it; tests build connections the
// same way so they exercise what a client actually receives.
return { ...stored, catalogEntries: resolveConnectionModelCatalog(stored) };
}

describe('model catalog picker helpers', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ import {
type AddProviderField,
} from '../../renderer/settings/provider-add-submission.js';
import {
PROVIDER_DEFAULTS,
PROVIDER_REGISTRY,
providerSupportsModelDiscovery,
type CreateConnectionInput,
type IdentifiedLlmConnection,
Expand Down Expand Up @@ -89,8 +89,8 @@ test('no provider type demands a model id at creation', () => {
// Stated across the catalog rather than for the two relays alone: the rule
// that came back would be a per-provider `if`, and asserting only where it
// used to live would let it reappear next door.
for (const providerType of Object.keys(PROVIDER_DEFAULTS) as ProviderType[]) {
const defaults = PROVIDER_DEFAULTS[providerType];
for (const providerType of Object.keys(PROVIDER_REGISTRY) as ProviderType[]) {
const defaults = PROVIDER_REGISTRY[providerType];
if (defaults.status === 'phase3-experimental') continue;
const issue = validateAddProviderDraft(
draft({
Expand Down Expand Up @@ -160,7 +160,7 @@ test('a successful catalog fetch reports no error', async () => {
});

test('a provider without discovery is not asked, and reports no error', async () => {
const withoutDiscovery = (Object.keys(PROVIDER_DEFAULTS) as ProviderType[]).find(
const withoutDiscovery = (Object.keys(PROVIDER_REGISTRY) as ProviderType[]).find(
(providerType) => !providerSupportsModelDiscovery(providerType),
);
assert.ok(withoutDiscovery, 'expected at least one provider with no discovery endpoint');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@

import assert from 'node:assert/strict';
import test from 'node:test';
import { PROVIDER_DEFAULTS } from '@maka/core/llm-connections';
import { PROVIDER_REGISTRY } from '@maka/core/llm-connections';
import {
endpointCarriesCredentials,
providerEndpointPresentation,
Expand All @@ -34,12 +34,12 @@ test('fixed Alibaba access paths expose their distinct effective endpoints read-
const tokenPlanChina = providerEndpointPresentation({ providerType: 'alibaba-token-plan-cn' });

assert.deepEqual(api, {
value: PROVIDER_DEFAULTS.alibaba.baseUrl,
value: PROVIDER_REGISTRY.alibaba.baseUrl,
editable: false,
emptyState: 'missing',
});
assert.deepEqual(tokenPlanChina, {
value: PROVIDER_DEFAULTS['alibaba-token-plan-cn'].baseUrl,
value: PROVIDER_REGISTRY['alibaba-token-plan-cn'].baseUrl,
editable: false,
emptyState: 'missing',
});
Expand Down Expand Up @@ -118,7 +118,7 @@ test('custom relays and local runtimes retain endpoint editing', () => {
assert.deepEqual(
providerEndpointPresentation({ providerType: 'ollama' }),
{
value: PROVIDER_DEFAULTS.ollama.baseUrl,
value: PROVIDER_REGISTRY.ollama.baseUrl,
editable: true,
emptyState: 'missing',
},
Expand All @@ -140,7 +140,7 @@ test('derived and OAuth endpoints remain visible but read-only', () => {
assert.deepEqual(
providerEndpointPresentation({ providerType: 'openai-codex' }),
{
value: PROVIDER_DEFAULTS['openai-codex'].baseUrl,
value: PROVIDER_REGISTRY['openai-codex'].baseUrl,
editable: false,
emptyState: 'managed',
},
Expand All @@ -161,7 +161,7 @@ test('providers with model-level endpoint overrides say so when showing the defa
assert.deepEqual(
providerEndpointPresentation({ providerType: 'zenmux' }),
{
value: PROVIDER_DEFAULTS.zenmux.baseUrl,
value: PROVIDER_REGISTRY.zenmux.baseUrl,
editable: false,
emptyState: 'missing',
modelOverrides: true,
Expand All @@ -170,7 +170,7 @@ test('providers with model-level endpoint overrides say so when showing the defa
assert.deepEqual(
providerEndpointPresentation({ providerType: 'cohere' }),
{
value: PROVIDER_DEFAULTS.cohere.baseUrl,
value: PROVIDER_REGISTRY.cohere.baseUrl,
editable: false,
emptyState: 'missing',
modelOverrides: true,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,11 @@

import assert from 'node:assert/strict';
import { describe, it } from 'node:test';
import type { ConnectionCatalogSnapshot, ConnectionTarget } from '@maka/core/runtime-policy';
import type { ConnectionTarget } from '@maka/core/runtime-policy';
import type {
RuntimeHostConnectionCatalogEntry as ConnectionCatalogEntry,
RuntimeHostConnectionCatalogSnapshot as ConnectionCatalogSnapshot,
} from '@maka/runtime-host/client';
import {
synchronizeRuntimeHostAccountConnection,
type RuntimeHostAccountConnectionClient,
Expand All @@ -42,6 +46,7 @@ function catalogWithoutDefault(): ConnectionCatalogSnapshot {
providerType: 'openai-codex',
enabled: true,
enabledModelIds: ['gpt-5-codex', 'gpt-5-codex-mini'],
catalogEntries: [],
models: [{ id: 'gpt-5-codex' }, { id: 'gpt-5-codex-mini' }],
modelSource: 'fallback',
modelsFetchedAt: 0,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,12 @@ import assert from 'node:assert/strict';
import test from 'node:test';
import type { AppSettings } from '@maka/core/settings';
import type {
ConnectionCatalogSnapshot,
CredentialLocator,
} from '@maka/core/runtime-policy';
import { gatherRuntimeHostConfig } from '../runtime-host-config-ipc-main.js';
import type {
RuntimeHostConnectionCatalogEntry as ConnectionCatalogEntry,
RuntimeHostConnectionCatalogSnapshot as ConnectionCatalogSnapshot,
} from '@maka/runtime-host/client';import { gatherRuntimeHostConfig } from '../runtime-host-config-ipc-main.js';

const CATALOG: ConnectionCatalogSnapshot = {
revision: 1,
Expand All @@ -41,6 +43,7 @@ const CATALOG: ConnectionCatalogSnapshot = {
providerType: 'deepseek',
enabled: true,
enabledModelIds: ['deepseek-v4-pro'],
catalogEntries: [],
models: [{ id: 'deepseek-v4-pro' }],
},
],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,10 @@
import assert from 'node:assert/strict';
import test from 'node:test';
import { OPENCODE_FREE_DEFAULT_ENABLED_MODELS } from '@maka/core/llm-connections';
import type { ConnectionCatalogSnapshot } from '@maka/core/runtime-policy';
import type {
RuntimeHostConnectionCatalogEntry as ConnectionCatalogEntry,
RuntimeHostConnectionCatalogSnapshot as ConnectionCatalogSnapshot,
} from '@maka/runtime-host/client';
import {
projectHostConnections,
projectHostConnectionTest,
Expand Down Expand Up @@ -75,6 +78,7 @@ test('retries connection delete after a stale revision instead of failing perman
providerType: 'openai-compatible',
baseUrl: 'https://openrouter.ai/api/v1',
enabled: true,
catalogEntries: [],
enabledModelIds: ['model-1'],
models: [{ id: 'model-1' }],
},
Expand Down Expand Up @@ -285,6 +289,7 @@ test('preserves the provider default inventory beside the recommended model', as
providerType: 'opencode-free',
enabled: true,
enabledModelIds: createdModels,
catalogEntries: [],
models: [],
},
],
Expand Down Expand Up @@ -328,6 +333,8 @@ test('projects the Host default target without inventing a second Connection aut
defaultModel: 'model-1',
enabledModelIds: ['model-1', 'model-2'],
models: [{ id: 'model-1' }, { id: 'model-2' }],
// Carried through from the Host projection, not rebuilt here.
catalogEntries: [],
createdAt: 0,
updatedAt: 4,
},
Expand Down Expand Up @@ -395,6 +402,7 @@ function catalog(): ConnectionCatalogSnapshot {
baseUrl: 'https://openrouter.ai/api/v1',
enabled: true,
enabledModelIds: ['model-1', 'model-2'],
catalogEntries: [],
models: [{ id: 'model-1' }, { id: 'model-2' }],
},
],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,12 @@ import assert from 'node:assert/strict';
import test from 'node:test';
import type { IpcMainInvokeEvent } from 'electron';
import type {
ConnectionCatalogEntry,
ConnectionCatalogSnapshot,
CredentialStatus,
} from '@maka/core/runtime-policy';
import {
import type {
RuntimeHostConnectionCatalogEntry as ConnectionCatalogEntry,
RuntimeHostConnectionCatalogSnapshot as ConnectionCatalogSnapshot,
} from '@maka/runtime-host/client';import {
registerRuntimeHostGitHubCopilotIpc,
type RuntimeHostGitHubCopilotIpcDeps,
} from '../runtime-host-github-copilot-ipc-main.js';
Expand Down Expand Up @@ -57,6 +58,7 @@ test('imports a local GitHub credential through the shared Host account path', a
...draft,
connectionId: CONNECTION_ID,
revision: 1,
catalogEntries: [],
models: [],
};
catalog = {
Expand Down
29 changes: 19 additions & 10 deletions apps/desktop/src/main/__tests__/runtime-host-oauth-ipc-main.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,11 @@
import assert from 'node:assert/strict';
import test from 'node:test';
import type { IpcMainInvokeEvent } from 'electron';
import { PROVIDER_DEFAULTS } from '@maka/core/llm-connections';
import type { ConnectionCatalogSnapshot } from '@maka/core/runtime-policy';
import { PROVIDER_REGISTRY } from '@maka/core/llm-connections';
import type {
RuntimeHostConnectionCatalogEntry as ConnectionCatalogEntry,
RuntimeHostConnectionCatalogSnapshot as ConnectionCatalogSnapshot,
} from '@maka/runtime-host/client';
import {
RUNTIME_HOST_OAUTH_IPC_CHANNELS,
registerRuntimeHostOAuthIpc,
Expand Down Expand Up @@ -54,7 +57,7 @@ test('adapts every Host OAuth provider through one Desktop flow', async () => {
const presentation = new RuntimeHostOAuthPresentation(async (url) => {
opened.push(url);
});
const modelId = PROVIDER_DEFAULTS[provider].fallbackModels[0];
const modelId = PROVIDER_REGISTRY[provider].fallbackModels[0];
assert.ok(modelId);
let phase: 'awaiting_authorization' | 'authenticated' | 'cancelled' =
'awaiting_authorization';
Expand All @@ -71,7 +74,8 @@ test('adapts every Host OAuth provider through one Desktop flow', async () => {
name: 'OpenAI Codex',
providerType: provider,
enabled: true,
enabledModelIds: [...PROVIDER_DEFAULTS[provider].fallbackModels],
enabledModelIds: [...PROVIDER_REGISTRY[provider].fallbackModels],
catalogEntries: [],
models: [],
},
],
Expand Down Expand Up @@ -215,7 +219,8 @@ test('provider-scoped OAuth IPC rejects a Connection ID owned by another provide
name: 'xAI Grok',
providerType: 'xai-oauth' as const,
enabled: true,
enabledModelIds: [...PROVIDER_DEFAULTS['xai-oauth'].fallbackModels],
enabledModelIds: [...PROVIDER_REGISTRY['xai-oauth'].fallbackModels],
catalogEntries: [],
models: [],
};
let starts = 0;
Expand Down Expand Up @@ -334,7 +339,8 @@ test('a second OAuth start cannot replace or cancel a pending active attempt', a
name: 'OpenAI Codex 2',
providerType: provider,
enabled: true,
enabledModelIds: [...PROVIDER_DEFAULTS[provider].fallbackModels],
enabledModelIds: [...PROVIDER_REGISTRY[provider].fallbackModels],
catalogEntries: [],
models: [],
},
{
Expand All @@ -344,7 +350,8 @@ test('a second OAuth start cannot replace or cancel a pending active attempt', a
name: 'OpenAI Codex 3',
providerType: provider,
enabled: true,
enabledModelIds: [...PROVIDER_DEFAULTS[provider].fallbackModels],
enabledModelIds: [...PROVIDER_REGISTRY[provider].fallbackModels],
catalogEntries: [],
models: [],
},
];
Expand All @@ -355,7 +362,8 @@ test('a second OAuth start cannot replace or cancel a pending active attempt', a
name: 'xAI Grok',
providerType: 'xai-oauth' as const,
enabled: true,
enabledModelIds: [...PROVIDER_DEFAULTS['xai-oauth'].fallbackModels],
enabledModelIds: [...PROVIDER_REGISTRY['xai-oauth'].fallbackModels],
catalogEntries: [],
models: [],
};
const presentation = new RuntimeHostOAuthPresentation(async () => undefined);
Expand Down Expand Up @@ -526,7 +534,7 @@ test('completion rejects a terminal projection that changes Connection identity'

test('keeps a committed OAuth login successful when model discovery fails without replacing the existing default', async () => {
const provider = 'openai-codex' as const;
const modelId = PROVIDER_DEFAULTS[provider].fallbackModels[0];
const modelId = PROVIDER_REGISTRY[provider].fallbackModels[0];
assert.ok(modelId);
const existing = {
connectionId: '00000000-0000-4000-8000-000000000002',
Expand All @@ -535,7 +543,8 @@ test('keeps a committed OAuth login successful when model discovery fails withou
name: 'OpenAI Codex',
providerType: provider,
enabled: true,
enabledModelIds: [...PROVIDER_DEFAULTS[provider].fallbackModels],
enabledModelIds: [...PROVIDER_REGISTRY[provider].fallbackModels],
catalogEntries: [],
models: [],
};
const created = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import { parseHTML } from 'linkedom';
import { act, createElement, Fragment, useCallback, useRef } from 'react';
import { createRoot, type Root } from 'react-dom/client';
import type { ChatModelChoice } from '@maka/core/chat-model-choice';
import type { IdentifiedLlmConnection } from '@maka/core/llm-connections';
import type { ProjectedLlmConnection } from '@maka/core/llm-connections';
import type { SessionSummary } from '@maka/core/session';
import {
Composer,
Expand All @@ -48,7 +48,7 @@ const originalGlobals = {
.IS_REACT_ACT_ENVIRONMENT,
};

const CONNECTION: IdentifiedLlmConnection = {
const CONNECTION: ProjectedLlmConnection = {
connectionId: 'connection-openrouter',
slug: 'openrouter',
providerType: 'openrouter',
Expand All @@ -58,6 +58,7 @@ const CONNECTION: IdentifiedLlmConnection = {
enabledModelIds: ['openai/gpt-5'],
createdAt: 1,
updatedAt: 1,
catalogEntries: [],
};
const CHOICE: ChatModelChoice = {
connectionId: CONNECTION.connectionId,
Expand Down
10 changes: 5 additions & 5 deletions apps/desktop/src/main/connections-ipc-validation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import {
type UpdateConnectionInput,
} from '@maka/core/llm-connections';
import { normalizeOptionalRequestBodyOverlay, normalizeRequestHeaders } from '@maka/core/runtime-policy';
import { PROVIDER_DEFAULTS } from '@maka/core/llm-connections';
import { PROVIDER_REGISTRY } from '@maka/core/llm-connections';
import { normalizeRelayModelProfiles } from '@maka/core/model-thinking';

const IPC_CONNECTION_SLUG_MAX_LENGTH = 64;
Expand Down Expand Up @@ -64,7 +64,7 @@ export function normalizeCreateConnectionInputForIpc(value: unknown): CreateConn
typeof input.name !== 'string' ||
input.name.length === 0 ||
typeof input.providerType !== 'string' ||
!(input.providerType in PROVIDER_DEFAULTS)
!(input.providerType in PROVIDER_REGISTRY)
) {
throw new Error('Invalid Connection input');
}
Expand Down Expand Up @@ -110,8 +110,8 @@ export function normalizeConnectionPatchSecretsForIpc(value: unknown): UpdateCon
}

export function normalizeConnectionBaseUrlForIpc<T extends CreateConnectionInput>(input: T): T {
if (PROVIDER_DEFAULTS[input.providerType].authKind === 'oauth_token') {
return { ...input, baseUrl: PROVIDER_DEFAULTS[input.providerType].baseUrl };
if (PROVIDER_REGISTRY[input.providerType].authKind === 'oauth_token') {
return { ...input, baseUrl: PROVIDER_REGISTRY[input.providerType].baseUrl };
}
if (input.baseUrl === undefined) return input;
return {
Expand All @@ -124,7 +124,7 @@ export function normalizeConnectionBaseUrlValueForIpc(
providerType: CreateConnectionInput['providerType'],
value: string,
): string {
const defaults = PROVIDER_DEFAULTS[providerType];
const defaults = PROVIDER_REGISTRY[providerType];
if (defaults.authKind === 'oauth_token') return defaults.baseUrl;
const result = normalizeConnectionBaseUrl(value);
if (!result.ok) throw new Error(result.error);
Expand Down
Loading
Loading