diff --git a/apps/server/src/provider/KiroProvider.test.ts b/apps/server/src/provider/KiroProvider.test.ts index 1877c131e3158..a4468b465deae 100644 --- a/apps/server/src/provider/KiroProvider.test.ts +++ b/apps/server/src/provider/KiroProvider.test.ts @@ -200,6 +200,8 @@ describe("KiroProvider", () => { // The version advisory is what the compatibility check and update UI read. assert.equal(snapshot.versionAdvisory?.currentVersion, "2.27.0"); assert.isFalse(snapshot.versionAdvisory?.canUpdate); + // Kiro rejects app text generation; the dispatcher falls back only when the snapshot says so. + assert.isFalse(snapshot.supportsTextGeneration); }), ); diff --git a/apps/server/src/textGeneration/CodexTextGeneration.test.ts b/apps/server/src/textGeneration/CodexTextGeneration.test.ts index e6e43d606e27f..300ac48ba9385 100644 --- a/apps/server/src/textGeneration/CodexTextGeneration.test.ts +++ b/apps/server/src/textGeneration/CodexTextGeneration.test.ts @@ -623,6 +623,7 @@ const PREFLIGHT_PRIMARY_STATES = [ "signed out, fallback failed", "cannot generate text", "model cannot generate text", + "disabled", ] as const; it.effect.each(PREFLIGHT_PRIMARY_STATES)("text generation preflight: %s", (primaryState) => @@ -667,6 +668,9 @@ it.effect.each(PREFLIGHT_PRIMARY_STATES)("text generation preflight: %s", (prima ...(primaryState === "signed out, fallback failed" && instanceId === fallback ? { status: "error", auth: { status: "unknown" } } : {}), + ...(primaryState === "disabled" && instanceId === primary + ? { enabled: false, status: "disabled" } + : {}), ...(primaryState === "cannot generate text" && instanceId === primary ? { supportsTextGeneration: false } : {}), diff --git a/apps/server/src/textGeneration/TextGeneration.ts b/apps/server/src/textGeneration/TextGeneration.ts index 818afb4fae553..71e11306e8a5d 100644 --- a/apps/server/src/textGeneration/TextGeneration.ts +++ b/apps/server/src/textGeneration/TextGeneration.ts @@ -107,6 +107,7 @@ export const make = Effect.gen(function* () { // Only a failed probe counts: a pending one is also not installed yet. const unusable = textGeneratorCannotRun || + selected?.enabled === false || (selected?.enabled === true && (!isProviderTextGenerationCapable(selected) || selected.models.find((model) => model.slug === selection.model) @@ -152,6 +153,12 @@ export const make = Effect.gen(function* () { "Review guides need a coding agent that can read the checkout. None is ready. Pick Codex or Claude as the review guide model in Settings → Source control.", }); } + if (selected?.enabled === false && selection.instanceId === selected.instanceId) { + return yield* new TextGenerationError({ + operation, + detail: `${selected.displayName ?? selection.instanceId} is disabled and no enabled provider can write this text. Pick another model in Settings → Threads → Text generation.`, + }); + } return yield* installationGate.execute( selection.instanceId, resolveInstance(registry, operation, selection.instanceId).pipe( diff --git a/docs/user/text-generation.md b/docs/user/text-generation.md index 7aa0884fa75d0..119db17d87ae9 100644 --- a/docs/user/text-generation.md +++ b/docs/user/text-generation.md @@ -5,6 +5,9 @@ descriptions, branch names, and voice transcript cleanup. Pick the model for thi **Settings → Threads → Text generation model**. **Settings → Source control** and **Settings → Voice** can each pick a different one. +A disabled provider never writes this text. When the chosen one is off, T3 Code uses another +enabled provider, or tells you none is available. + By default, a coding agent such as Codex or Claude writes this text, starting a session for each request. A **text generator** — a model API that only writes text — is usually faster and cheaper. diff --git a/packages/provider-kiro/src/server/status.ts b/packages/provider-kiro/src/server/status.ts index 46f3361147fe3..8f20877292f87 100644 --- a/packages/provider-kiro/src/server/status.ts +++ b/packages/provider-kiro/src/server/status.ts @@ -82,8 +82,8 @@ const snapshot = ( probe: ProviderProbeResult, listed?: ReadonlyArray, agents?: ProviderOptionDescriptor, -) => - buildServerProvider({ +): ServerProviderDraft => ({ + ...buildServerProvider({ // Gives the snapshot its version advisory; Kiro runs no enrichment pass. driver: KIRO_DRIVER_KIND, presentation: KIRO_PRESENTATION, @@ -93,7 +93,10 @@ const snapshot = ( // `/compact` runs `_kiro/session/compact`; see kiroCompactSession. slashCommands: [COMPACT_SLASH_COMMAND], probe, - }); + }), + // See the unsupported text generation in driver.ts; this lets the dispatcher fall back. + supportsTextGeneration: false, +}); export const buildInitialKiroProviderSnapshot = Effect.fn( "KiroProvider.buildInitialKiroProviderSnapshot",