Skip to content

feat(ai): add declarative external assistants with built-in providers - #1337

Merged
NewtTheWolf merged 5 commits into
mainfrom
feat/ai-assistant-plugins
Nov 12, 2026
Merged

NewtTheWolf merged 5 commits into
mainfrom
feat/ai-assistant-plugins

Conversation

@debba

@debba debba commented Oct 29, 2026 •

Copy link
Copy Markdown
Collaborator

External AI providers can be added as declarative HTTP packages, with credentials, configuration, proxy routing and timeouts owned by Tabularis. Existing users retain built-in providers without a registry download.

Behavior

  • OpenAI, Anthropic, Ollama, OpenRouter, Requesty, MiniMax, OpenAI Compatible and API Route remain embedded, including their icons. ChatGPT account login remains built in. Gemini is an external package.
  • AI settings provide install/select and removal through the existing registry and package lifecycle. Plugin Center supports external updates. Built-ins cannot be replaced, shadowed or removed by packages, and AI packages never enter database-driver registration.
  • Legacy provider IDs, credentials, saved models, custom endpoints, Ollama port and newer inline-completion/crash-analysis paths are preserved. Migration copies missing settings locally and retains the last successful model catalog.
  • Model discovery distinguishes failed requests from an intentionally empty successful result. Failed HTTP/transport/response parsing retains the latest successful catalog and timestamp; provider updates merge into the latest cache under its mutex and use atomic file replacement. Package install/update/removal invalidates only that provider's entry.
  • API Route and OpenAI Compatible retain explicit saved/selected-model requirements. Provider authentication/environment metadata is declarative. The HTTP runtime supports custom headers, nested JSON templates, bounded selectors, preferred/discovered models, filters and fallback requests. Model-resource URL interpolation cannot change the origin or inject a query/fragment.
  • The application and Tabularium [Bug]: Cloning db connection #35 contain the same operator preset and Gemini example. The old seven-replacement-package rollout is obsolete.

Validation on the updated revision

  • pnpm lint, pnpm typecheck, production frontend build and pnpm test:all pass: 2,527 Rust library tests (19 ignored) and 8,901 frontend tests. Ignored live-database suites remain ignored.
  • Server-only cargo check --no-default-features and the native desktop build with e2e-testing pass. Existing shared web-RPC/AI/inline tests are included; Linux native UI and IPC were exercised directly.
  • Regression coverage includes failed/malformed/forced discovery, successful empty catalogs, recovery, overlapping provider refreshes, retained migration catalogs, explicit model selection, and preservation of other providers' catalogs on removal. The discovery and removal bugs were reproduced with failing tests before correction.
  • Real Linux Tauri app with isolated profiles and SQLite: contextual install/select of the original Gemini 1.0.0 archive, Plugin Center update to original 1.0.1, remove/reinstall, failure preserving the previous selection, install while AI disabled, min-runtime incompatibility, and offline availability of eight built-ins and the installed provider.
  • A local development-only Gemini artifact changed the endpoints/version to a controlled HTTP fixture. Native discovery filtered out embedding-only models, generateContent used x-goog-api-key, thought parts were skipped, and generated SQL executed to IT=2 / US=1. Native inline completion returned text and cancelled after about 130 ms; a one-second provider override timed out at about one second.
  • The final desktop removal/reinstall repeated the scoped-cache checks: the other provider's model and timestamp stayed unchanged. No live Gemini account was used. A non-fatal Monaco cancellation was recorded on provider removal; this is not presented as a clean-console run.
  • Original published Gemini ZIP/standalone manifest contents, SHA-256 and sizes were checked. An initially malformed unsigned-integrity fixture exercised the existing envelope-hash fallback; the correct unsigned schema was then retried successfully. Signed JWS attestations were not exercised.
  • Current Tabularium manifest tests pass (66), including Gemini against merged core-plus-kind schema. GitNexus impact/detect-changes and the actual diff against current main were checked. Locale changes retain current-main formatting and contain 19 semantic additions per locale.

Release gates

The production registry rollout is now complete for Gemini, as recorded below. OAuth, signing, streaming, pagination and multimodal request transformations remain outside this v1 runtime contract.

Published Gemini example

Google Gemini on Tabularium is now a concrete external ai-assistant example. Gemini 1.0.2 ships the standalone .tabularium manifest and gemini-1.0.2-universal.zip with:

{
  "id": "gemini",
  "name": "Google Gemini",
  "version": "1.0.2",
  "kind": "ai-assistant",
  "min_runtime_version": "0.32.1-4"
}

This is an excerpt; the complete manifest also declares the ai_assistant HTTP contract. 0.32.1-4 is the next nightly expected on November 13, 2026, following the currently published 0.32.1-3. The plugin is published now and requires that upcoming runtime or a newer compatible release; the nightly itself has not shipped yet.

The authenticated operator has applied the ai-assistant kind preset. Gemini 1.0.2 is approved and indexed, its universal ZIP resolves through the registry, and its version, runtime floor, SHA-256, size and signed integrity metadata are present. The release workflow passed and the repository webhook is installed for future releases. Gemini 1.0.0/1.0.1 remain historical development previews without this runtime floor.

To try it on a compatible runtime: Settings → AI → Available providers → Google Gemini → Install and use, then configure a Gemini API key or GEMINI_API_KEY. This publication check does not add a live Gemini generation test to the validation reported above.

@NewtTheWolf NewtTheWolf left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for moving provider-specific HTTP behavior into declarative packages while keeping credentials and desktop/web dispatch in the host. I reviewed f2b1215f5744f1735aa65cd8edc36015d0d12676, including installation/recovery, legacy migration, model discovery and the settings/Plugin Center changes.

Validation on that revision: 95 targeted frontend tests and 14 targeted Rust tests passed. Frontend build, typecheck, targeted ESLint and git diff --check passed. All nine package manifests validate against the bundled schema and all nine universal ZIPs build. The runtime tests exercise local HTTP fixtures, optional credentials, model filters, migration retry and interrupted updates; they are not live-provider or desktop E2E tests.

I have no confirmed code blocker from this pass, but this is not a merge approval. GitNexus classifies the PR's 235 changed symbols and 34 affected flows as critical, with shared credentials and startup migration among the affected paths. The PR is still a draft and GitHub currently reports merge conflicts. Tabularium #35 is also open, and the package publishing/runtime-version/desktop verification checklist remains outstanding.

I will run the real-app install/select/generate/update/remove and offline-upgrade checks once the PR is rebased and the registry rollout is ready, then revisit merge readiness. Holding this open in the meantime.

@debba debba changed the title feat(ai): move providers to declarative assistant plugins feat(ai): add declarative external assistants with built-in providers Nov 11, 2026
@debba

debba commented Nov 11, 2026

Copy link
Copy Markdown
Collaborator Author

@NewtTheWolf, here are the remaining Tabularium rollout steps for this PR.

This checklist supersedes the seven-replacement-package rollout described in Tabularium #35: the seven existing providers are built in and do not need external publication.

  • Coordinate the registry rollout with this application PR and Tabularium [Bug]: Cloning db connection #35.
  • Apply/review the ai-assistant operator kind preset via the authenticated admin interface/API, including extension schema and public catalogue page. Deploying Tabularium alone does not apply the preset.
  • Validate the Gemini standalone manifest against the registry's merged core + kind schema, including its HTTPS icon, native model discovery recipe and URL template.
  • Assign the actual first supporting Tabularis runtime version, publish a successor Gemini release with min_runtime_version, and only then make the production listing available. The current 1.0.1 release is a development prerelease without that gate.
  • Register TabularisDB/tabularis-gemini-plugin as slug gemini, kind ai-assistant; ingest the standalone .tabularium and its universal ZIP asset, with matching release version, size, SHA-256 and release integrity metadata.
  • Confirm icon rendering, AI-kind filtering, model discovery, contextual install/select, update/remove/reinstall and offline availability of built-ins against the deployed registry.
  • Verify native desktop parity and provider timeout behavior before leaving draft/releasing the application.

@debba
debba marked this pull request as ready for review November 12, 2026 19:39
await refreshProviders();
await updateSetting('aiModel', '');
await updateSetting('aiProvider', id);
if (!settings.aiEnabled) await updateSetting('aiEnabled', true);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

WARNING: Stale settings.aiEnabled and non-atomic selection after install

settings.aiEnabled is read from the render-time closure after two awaited updateSetting calls, so it can be stale. aiProvider is also persisted before aiEnabled; if the last write fails, a provider is selected while AI stays disabled. Enable AI unconditionally or use the updater form of updateSetting.

{t(
p.id === 'chatgpt'
p.authentication === 'account'
? 'settings.ai.planAccess'

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

WARNING: Missing i18n keys in 9 locales

settings.ai.planAccess, keyAccess and localAccess exist only in en.json (769-771) and it.json (768-770). They are absent from de, es, fr, ja, ko, pt-BR, ru, tl and zh, so every provider tile falls back to the raw key/English there. .rules/general.md requires translating strings into packages/web-ui/src/i18n/locales.

"providerMissing": "{{provider}} is not installed. Install it from the Plugin Center to continue using your saved configuration.",
"availableProviders": "Available providers",
"installProviderHelp": "Externe KI-Anbieter direkt in diesen Einstellungen installieren oder entfernen.",
"installAndUse": "Install and use",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

WARNING: New provider strings are untranslated English

The new settings.ai/settings.plugins keys (installAndUse, retryMigration, builtinProvider, availableProviders, providerRegistryOffline, ...) are copied in English into de, es, fr, ja, ko, pt-BR, ru, tl and zh (only it is translated). .rules/general.md requires translating into the locale files.

try {
await client.call('migrate_ai_providers', undefined);
} catch (failure) {
setError(String(failure));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

WARNING: Migration errors are never shown

setError(String(failure)) is immediately cleared by the refresh() in finally, which calls setError(null) on success. A failed migrate_ai_providers therefore leaves the retry UI with no error. Also refresh has no request-id guard, so out-of-order concurrent refreshes (mount + two events + manual) can let an older catalog win, and String(failure) yields Error: ... instead of the message.

isTheme
? 'update.badges.themeUpdate'
: kind === 'ai-assistant'
? 'settings.plugins.kindAiAssistant'

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SUGGESTION: Wrong label for the update indicator

For ai-assistant the update tooltip/aria-label resolves to settings.plugins.kindAiAssistant (a kind name, "AI assistant"), not an update message. Add an update.badges.aiAssistantUpdate key.

let variables = json!({"settings":settings,"model":request["model"],"prompt":request["prompt"],"system_prompt":request["system_prompt"]});
let mut builder = Client::builder()
.timeout(Duration::from_secs(30))
.redirect(reqwest::redirect::Policy::none());

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

WARNING: Plain http:// and unrestricted hosts allowed for credentialed requests

recipe::request_url accepts http as well as https, and nothing prevents a package manifest (including {{settings.*}} URLs) from sending the API key header (send, lines 152-159) over cleartext or to private/loopback hosts. Redirects are disabled, which is good, but consider requiring https unless the host is loopback (Ollama / local OpenAI-compatible) when an API key is attached.

return Err(error.to_string());
}
if existed {
std::fs::remove_dir_all(backup).map_err(|e| e.to_string())?;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

WARNING: Installed update reported as failed and leaves recovery state

If remove_dir_all(backup) fails after the new directory is already in place, the function returns Err even though the update succeeded, and the leftover .backup-{id} makes every later install fail with "A previous AI assistant update needs recovery" (line 158) until recover_package runs. This cleanup failure should be logged and ignored rather than failing the install.

.map(str::to_owned)
.or_else(|| value.as_u64().map(|number| number.to_string()))
.ok_or("Invalid AI assistant URL setting")?;
output.push_str(text.trim_end_matches('/'));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SUGGESTION: Settings URL value is not validated for characters

{{settings.*}} values are appended verbatim (only trailing / trimmed) before Url::parse, so a user setting containing ?/#/path segments changes the request shape, and a ? in a prefix later makes {{model}} interpolation fail. Fragments/credentials are rejected after parsing, but consider documenting or validating that settings used in URLs are bare origins/paths.

@kilo-code-bot

kilo-code-bot Bot commented Nov 12, 2026 •

Copy link
Copy Markdown
Contributor

Code Review Summary

Status: 8 Issues Found | Recommendation: Address before merge

Overview

Severity Count
CRITICAL 0
WARNING 6
SUGGESTION 2
Issue Details (click to expand)

WARNING

File Line Issue
packages/web-ui/src/components/settings/AiTab.tsx 385 settings.aiEnabled read from a stale closure after awaits; aiProvider persisted before aiEnabled, so a partial failure leaves a provider selected with AI disabled
packages/web-ui/src/components/settings/AiTab.tsx 358 settings.ai.planAccess/keyAccess/localAccess exist only in en/it; missing in de, es, fr, ja, ko, pt-BR, ru, tl, zh
packages/web-ui/src/i18n/locales/de.json 779 New provider strings copied in English into 9 locales (only it translated), against .rules/general.md
packages/web-ui/src/hooks/useAiProviders.ts 50 Migration error is cleared by the following refresh(), so it is never shown; no request-id guard on concurrent refreshes
src-tauri/src/ai_assistants/http.rs 45 API key can be sent over plain http:// / arbitrary hosts from package manifests
src-tauri/src/ai_assistants/install.rs 171 Backup cleanup failure after a successful swap returns Err and leaves .backup-{id}, blocking later installs

SUGGESTION

File Line Issue
packages/web-ui/src/components/plugins/PluginCard.tsx 114 Update tooltip/aria-label for AI assistants uses the kind name instead of an update message
src-tauri/src/ai_assistants/recipe.rs 128 {{settings.*}} values interpolated into URLs are not validated for ?/#/path characters
Files Reviewed (key files, 70+ changed in total)
  • src-tauri/src/ai_assistants/http.rs - 1 issue
  • src-tauri/src/ai_assistants/install.rs - 1 issue
  • src-tauri/src/ai_assistants/recipe.rs - 1 issue
  • src-tauri/src/ai_assistants/manifest.rs, migration.rs, mod.rs
  • src-tauri/src/ai.rs, src-tauri/src/application/ai.rs
  • packages/web-ui/src/components/settings/AiTab.tsx - 2 issues
  • packages/web-ui/src/hooks/useAiProviders.ts - 1 issue
  • packages/web-ui/src/components/plugins/PluginCard.tsx - 1 issue
  • packages/web-ui/src/i18n/locales/*.json - 1 issue
  • plugins/ai-assistants/packages/*/.tabularium, manifest.schema.json, package.py

Gaps: the full patch output was truncated and some read-only commands were rejected, so the remaining frontend components, tests and generated files were reviewed by sub-agents and spot checks rather than line by line.

Fix these issues in Kilo Cloud


Reviewed by claude-sonnet-5.5 · Input: 282 · Output: 10.6K · Cached: 2.4M

Review guidance: REVIEW.md from base branch main

@NewtTheWolf NewtTheWolf left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for keeping the existing providers available offline and adding the contextual install flow. I re-reviewed 33df995ade79e032aedd7d7b48e4d4281b9ae496 and checked the companion registry contract.

I reproduced a model-cache bug in ai::get_models / ai_assistants::http::execute. Seed an expired entry with a previously discovered account model, make that provider's discovery endpoint return HTTP 503, then restore the endpoint and call get_models(false) again. Both results are empty and only the first call reaches the endpoint. Discovery swallows the error into Ok(default_models), then the host overwrites the previous catalog and advances fetched_at. With cache_seconds: 86400, that failed result stays fresh for a day. The independent regression fails with left: [], right: ["last-successful-account-model"].

Please distinguish successful discovery, including a deliberately empty successful response, from a failed request. Keep the last successful catalog on transient HTTP/transport/parse failure, do not mark the failed refresh successful, and cover recovery plus overlapping refreshes in regression tests.

The branch also needs integration with current main: there are conflicts in the AI runtime, settings/tests, application/plugin paths, desktop command wrapper and events, plus the removed parity file. Preserve the API Route provider and its cache/optional-key/inline-completion behavior added since this branch's base when resolving those conflicts. pnpm lint currently fails Prettier on plugins/ai-assistants/manifest.schema.json and tabularium-kind.json.

What passed on this head: production build, typecheck, 126 focused frontend tests and 2,230 Rust library tests (13 ignored). I also built and ran the real Linux Tauri app: seven embedded providers and eight icons were present; OpenAI Compatible generated SQL through an isolated local HTTP fixture and the app executed it against SQLite, returning IT=2 and US=1. A fixture HTTP 503 displayed a sanitized error and left the SQL unchanged. No live LLM account was used.

The published Gemini 1.0.1 ZIP and standalone manifest match, and their sizes/SHA-256 match GitHub's release metadata. The manifest validates against Tabularium #35's merged core-plus-kind schema. That prerelease has no min_runtime_version. The connected registry still returns 404 for ai-assistant and an empty AI-provider list, so production install/update/remove/reinstall and native live-Gemini parity remain unverified. Your new Gemini-only rollout checklist supersedes the seven-replacement-package plan still written in Tabularium #35. Keeping this open for the cache correction, main integration and coordinated release gates.

@NewtTheWolf NewtTheWolf left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated and verified 9518ced3cb53219b1f7888836d9b5390c3972fe9 with your permission. The failed-discovery cache bug is fixed: failed refreshes retain the latest successful catalog and its age, successful empty results clear it, and overlapping updates merge per provider. I also reproduced and fixed the lifecycle path that erased unrelated catalogs on assistant removal. Both failures now have passing regressions.

Current main is integrated, including API Route, inline completion, crash analysis and plugin-settings sync. API Route is an embedded declarative provider with the same environment/credential identity and explicit-model behavior. Migration preserves existing catalogs, and both new and existing AI entry points compile in desktop and server-only builds.

Lint, typecheck, build and pnpm test:all pass: 2,527 Rust library tests (19 ignored), 8,901 frontend tests. The real Linux desktop completed install/select, original Gemini 1.0.0→1.0.1 update, remove/reinstall, failure preservation, version gating and the scoped-cache checks. A local Gemini protocol fixture generated SQL that the app executed to IT=2 / US=1; inline completion/cancellation and a one-second provider timeout also worked. No live Gemini account or signed-JWS path was tested. A non-fatal Monaco cancellation is documented in the test evidence rather than hidden as a clean-console claim.

Code approved. The production Gemini rollout stays gated: the connected registry kind/listing is still absent, and the actual first shipping runtime version must be assigned before a public successor release/listing. I updated Tabularium #35's source preset and rollout documentation without changing production.

@NewtTheWolf

Copy link
Copy Markdown
Collaborator

Verified 9518ced3cb53219b1f7888836d9b5390c3972fe9 in the real Linux desktop app and with the full test suite: install/select/update/remove/reinstall, failed-install selection preservation, scoped model-cache preservation, local Gemini protocol/SQLite round trip, inline cancellation and provider timeout. The original release assets were checked independently; no live Gemini account or production registry write was used. All current checks pass. Merging this tested revision; the public Gemini listing remains gated on the actual shipping runtime version and authenticated operator rollout.

@NewtTheWolf
NewtTheWolf merged commit 47af059 into main Nov 12, 2026
15 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants