A version-mapped history of the Code on the Go plugin API — which release week
first shipped each plugin capability. It exists so a plugin author can choose a
correct plugin.min_ide_version.
- Audience: plugin developers (and maintainers changing the API).
- Companions: PLUGIN_AUTHORING.md (how to write a plugin), plugin-api.md (what counts as the API + compatibility policy).
Set plugin.min_ide_version to the highest version below among the
capabilities your plugin actually uses. Only min is enforced at install today;
max is parsed but advisory.
<!-- src/main/AndroidManifest.xml -->
<meta-data android:name="plugin.min_ide_version" android:value="26.30" />
<meta-data android:name="plugin.max_ide_version" android:value="99.0.0" />Versions are bare YY.WW — two-digit ISO year, two-digit ISO week (26.30 =
2026, week 30). Not the R1 / R2 marketing names, and there is no separate integer
"API level" — the YY.WW string is the whole contract.
Newest first. Most changes are additive; the ones that are not carry a
breaking row saying what breaks and what to do about it. Read the breaking
rows at or below your min_ide_version before you bump it.
Legend: added = new capability, safe to adopt · breaking = existing plugins
need a source change, a recompile, or both · tooling = API-stability
milestone. [verified] = read from the checked-in ABI dump. [reconstructed]
= diffed from plugin-api/src history (predates the dump; symbol-accurate).
- added — Plugin-contributed agent tools (ADFA-2592) [verified]
Any
.cgpcan add tools to the AI agent, whose tool set was previously fixed at ai-core compile time. The contract has to live in the host: each plugin is loaded by its own class loader with the host as parent, so a type packaged in one.cgpis not resolvable from another — and duplicating it into each plugin compiles cleanly, then fails on device withClassCastException. ai-core implements the registry and publishes it underSharedServices, exactly as it doesLlmInferenceService; a provider registers onactivate()and unregisters ondeactivate(). Host runtime behaviour,PluginManager, the loader andPluginPermissionare unchanged — a provider declares the permissions its own work needs.ToolSourceRegistry(registerToolSource,unregisterToolSource,getToolSources,notifyToolsChanged,CONTRACT_VERSION),ToolSourceRegistry.ToolSource/.ToolSpec/.ToolInvocation/.ToolOutcome. Values crossing this boundary must be JDK types, and the registry hands each source a sanitized copy of the argument map rather than its own.unregisterToolSourcetakes theToolSourceinstance, not a provider id, so a reused provider id cannot remove another plugin's source — but the registry is no trust boundary between plugins:getToolSourceshands out the registered instances and registering under a taken id replaces it.ToolSpec.requiresApproval()defaults to true, inverted relative to the agent's own tools: those are contained by its path guard, a contributed tool by nothing. - added — Optional LLM backend capabilities (ADFA-5095) [verified]
An LLM backend declares what it supports by the interfaces it implements, so a
backend can ship as its own plugin and implement only what it can do. The
consumer asks with
instanceofbefore it calls; a backend that implements none of these is still a validLlmBackend.LlmInferenceService.HistoryCapableBackend(generateStreamingWithHistory),ToolCallingBackend(generateStreamingWithTools),CancellableBackend(cancelStreaming),ConfigurableBackend(getSettingsFragmentClassName— the backend's own settingsFragment, loaded with the backend's classloader). - added — Backend-owned prompt and sampling (ADFA-5095) [verified]
A backend supplies the system prompt and temperature its model needs, instead of
the consumer hardcoding them per provider. Both are
defaultand return null for "no preference";getDefaultTemperature()is a boxedFloat, so null-check before assigning it to the primitiveLlmConfig.temperature.LlmBackend.getSystemPrompt(SystemPromptRequest),LlmBackend.getDefaultTemperature(),SystemPromptRequest. - breaking — Tool results correlated by call id and tool name (ADFA-5095) [verified]
A tool's output travels back into the next turn as a message of its own, so a
turn's several calls are matched by correlator rather than by position. Both
correlators travel with the result because providers key results differently —
by call id, or by function name — and a backend can only forward what it was
given.
ChatMessage.toolResult(String, String, String),ChatMessage.toolCallId/toolName,ChatMessage.Role.TOOL. What breaks:Rolegains a fourth constant, so an exhaustive Kotlinwhenover it with noelsestops compiling. A plugin already built against the three-constant enum has the worse failure: thewhenthrowsNoWhenBranchMatchedExceptionwith a null message, which reads as an unattributable crash inside the plugin rather than as anything to do withRole. ATOOLmessage reaches a backend that never callstoolResult— the consumer builds it and passes it in the history — so handling it is not optional for backends. What to do: add aTOOLbranch (routing it as a user turn is fine for a backend with no native function calling) and republish; a.cgpthat is only reinstalled, not rebuilt, stays exposed. - added — Preferred backend id (ADFA-5095) [verified]
A backend can ask which backend the user selected, so one that would otherwise
spend seconds and gigabytes preparing itself knows whether it is about to be
used — without reading another plugin's preferences.
LlmInferenceService.getPreferredBackendId()(default, null when unset). - breaking — Nullability annotated across the LLM surface (ADFA-5095)
Every parameter, return and field on
LlmInferenceServiceand the types nested in it now carries@NonNullor@Nullable, so the contract is stated rather than inferred. What breaks: an unannotated Java type reaches Kotlin as a platform type (String!) that dereferences without a check; annotated@Nullableit becomesString?, and every existing dereference stops compiling with "only safe (?.) or non-null asserted (!!.) calls are allowed". This hits callers, not just implementors —LlmResponse.text/.error,ToolCallRequest.argsandToolDefinition.parametersSchemaare the ones consumers touch, and@NonNullacrossLlmBackendtightens what an implementor may return. Bytecode is unchanged, so an installed.cgpkeeps running; the break is at compile time in the plugin repo. What to do:?.,.orEmpty()or an explicit null check at each site — the annotations describe values the API could already return.
- tooling — Plugin API & builder resolvable by Maven coordinate on-device (ADFA-4911)
The plugin API and the builder Gradle plugin are injected into the on-device
local Maven repository at onboarding, so a plugin resolves them by coordinate,
offline, without committing
libs/*.jar:compileOnly("com.itsaky.androidide:plugin-api:1.0.0")andplugins { id("com.itsaky.androidide.plugins.build") version "1.0.0" }.plugin-api:1.0.0bundles:plugin-api+common+eventbus-events+idetooltips. (No-libs/project detection lands separately in ADFA-4913.)
- added — Project-search providers (ADFA-4723,
88c20f3) [verified] Plugins contribute their own project-search sources that render as dedicated result sections.ProjectSearchExtension.searchProject(ProjectSearchRequest): CompletableFuture,ProjectSearchRequest,ProjectSearchResult,ProjectSearchSection.
- added — Cross-plugin services & lifecycle (ADFA-4584,
875853d) [verified] Register services other plugins consume, observe plugin lifecycle, query whether a plugin is active or its version, read app preferences.PluginContext.registerService/unregisterService/getPluginService/getProvidedServices/addPluginLifecycleListener/isPluginActive/getPluginVersion/getAppSharedPreferences,PluginLifecycleListener,SharedServices. - added — Editor inline suggestions (ghost text) (ADFA-4584,
875853d) [verified] Show/dismiss inline completions and observe editor content changes.IdeEditorService.showInlineSuggestion/dismissInlineSuggestion/addContentChangeListener,EditorContentChangeListener. - added — Dynamic toolbar icons (ADFA-4584,
875853d) [verified]UIExtension.getIconProvider/setIconProvider,IdeUIService.refreshToolbarActions().
- added — Per-module context & task execution (ADFA-4582,
cc2f592) [verified] Resolve a context for a specific Gradle module and run build tasks against it.IdeProjectService.getModuleContext(String): ModuleContext,IdeBuildService.executeTasks(vararg String): CompletableFuture. - tooling — API-stability baseline (ADFA-3588,
440e7dd) [verified] First release where the whole public surface is frozen into a checked-in ABI dump (plugin-api/api/plugin-api.api) guarded by the binary-compatibility validator. From here on every API change is a reviewable diff. Adds the@InternalPluginApiopt-out marker.
- added — Archive & environment services (ADFA-3787,
88d2f4a) [reconstructed] Extract archives (xz/gzip/tar/zip), locate IDE-managed directories (SDK, NDK, home, tmp), write binary/streamed files. Adds theide.environment.writepermission.IdeArchiveService,IdeEnvironmentService,ArchiveFormat,ExtractResult,PluginPermission.IDE_ENVIRONMENT_WRITE,ResourceManager.openPluginResource/openPluginAsset,IdeFileService.writeBinary/writeStream/delete.
- added — Day / night plugin icons (ADFA-3694,
e5383d2) [reconstructed] Ship separate light/dark icons; the manager renders the one matching the theme. Manifest keysplugin.icon_day/plugin.icon_night;PluginMetadata.iconDayPath/iconNightPath. - added — Code snippets (ADFA-3546,
683b551) [reconstructed] Contribute reusable snippets in TextMate syntax.SnippetExtension,IdeSnippetService,SnippetContribution.
- added — Build actions & custom commands (ADFA-3580,
98b9ba1) [reconstructed] Contribute actions to the build toolbar; run shell commands or Gradle tasks with streamed output; includes the toolbar-action IDs a plugin may hide.BuildActionExtension,IdeCommandService,CommandExecution,PluginBuildAction,BuildActionCategory,ToolbarActionIds,CommandSpec(ShellCommand/GradleTask),CommandResult.
- added — Project-template contribution (#1122,
93ae25c) [reconstructed] Contribute new-project templates in the.cgtformat.IdeTemplateService,CgtTemplateBuilder. - added — Feature-flag access (ADFA-2808,
d924652) [reconstructed]IdeFeatureFlagService.isExperimentsEnabled().
- added — File-open handling (ADFA-3162,
0681d66) [reconstructed] Intercept file opens and contribute file-tab menu items — the basis for custom viewers like the APK viewer.FileOpenExtension(canHandleFileOpen/handleFileOpen/getFileTabMenuItems),FileTabMenuItem.
- added — Material 3 theming (ADFA-1718,
a004fc5) [reconstructed] Read the active theme and react to theme changes.IdeThemeService,ThemeChangeListener.
- added — Sidebar slots (ADFA-2139,
9fa0f17) [reconstructed] Declare how many sidebar slots a plugin needs and query availability.IdeSidebarService(getAvailableSidebarSlots/canAddSidebarItems/getMaxSidebarItems), manifest keyplugin.sidebar_items(Int). - added — Plugin system foundation (genesis) (#406,
6fdbe8e) [reconstructed] The plugin system itself: lifecycle, the context + service registry handed to every plugin, the first extension points and core IDE services, and the manifest contract includingplugin.min_ide_version/plugin.max_ide_version(present from day one).IPlugin(initialize/activate/deactivate/dispose),PluginContext,ServiceRegistry,ResourceManager,PluginLogger,PluginMetadata,PluginPermission,UIExtension,EditorExtension,EditorTabExtension,ProjectExtension,DocumentationExtension,IdeProjectService,IdeEditorService,IdeUIService,IdeBuildService,IdeFileService,IdeEditorTabService,IdeTooltipService, and the manifest<meta-data>contract.
- Pre-26.28 has no ABI dump. The validator arrived in 26.28. Earlier entries
(
[reconstructed]) were recovered by diffing eachplugin-api/srccommit — the symbol lists are accurate, but read from source rather than a frozen contract. - Early weeks collapse onto 26.02. The oldest release tag is
26.02, so both the September genesis and the December sidebar work report26.02as their first shipped version — not because they were written that week, but because no earlier release was ever tagged. A plugin can safely floor at26.02for anything in that band. - This is a history, not a compatibility guarantee. The plugin API is deliberately still evolving — see plugin-api.md.
- App-side/manager-only symbols (e.g.
IdeNavigationRailView,PluginValidation,.codeonthego/scripts.json) are intentionally omitted — plugins don't compile against them.
The source of truth is the ABI dump, so each new release's additions can be listed mechanically. For the window between two release tags:
git log --oneline 26.29..26.30 -- plugin-api/api/plugin-api.api
git diff 26.29 26.30 -- plugin-api/api/plugin-api.apiMap any commit to the release that first shipped it:
git tag --list --contains <sha> | grep -E '^[0-9]{2}\.[0-9]{2}$' | sort -V | head -1