mirror of
https://github.com/getcompanion-ai/co-mono.git
synced 2026-04-20 19:02:10 +00:00
feat(coding-agent): flush registerProvider immediately after bindCore, add unregisterProvider
This commit is contained in:
parent
4ba3e5be22
commit
975de88eb1
6 changed files with 90 additions and 3 deletions
|
|
@ -2,6 +2,14 @@
|
||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- `pi.unregisterProvider(name)` removes a dynamically registered provider and its models from the registry without requiring `/reload`. Built-in models that were overridden by the provider are restored.
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- `pi.registerProvider()` now takes effect immediately when called after the initial extension load phase (e.g. from a command handler). Previously the registration sat in a pending queue that was never flushed until the next `/reload`.
|
||||||
|
|
||||||
## [0.53.0] - 2026-02-17
|
## [0.53.0] - 2026-02-17
|
||||||
|
|
||||||
### Breaking Changes
|
### Breaking Changes
|
||||||
|
|
|
||||||
|
|
@ -1161,6 +1161,8 @@ pi.events.emit("my:event", { ... });
|
||||||
|
|
||||||
Register or override a model provider dynamically. Useful for proxies, custom endpoints, or team-wide model configurations.
|
Register or override a model provider dynamically. Useful for proxies, custom endpoints, or team-wide model configurations.
|
||||||
|
|
||||||
|
Calls made during the extension factory function are queued and applied once the runner initialises. Calls made after that — for example from a command handler following a user setup flow — take effect immediately without requiring a `/reload`.
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
// Register a new provider with custom models
|
// Register a new provider with custom models
|
||||||
pi.registerProvider("my-proxy", {
|
pi.registerProvider("my-proxy", {
|
||||||
|
|
@ -1221,6 +1223,21 @@ pi.registerProvider("corporate-ai", {
|
||||||
|
|
||||||
See [custom-provider.md](custom-provider.md) for advanced topics: custom streaming APIs, OAuth details, model definition reference.
|
See [custom-provider.md](custom-provider.md) for advanced topics: custom streaming APIs, OAuth details, model definition reference.
|
||||||
|
|
||||||
|
### pi.unregisterProvider(name)
|
||||||
|
|
||||||
|
Remove a previously registered provider and its models. Built-in models that were overridden by the provider are restored. Has no effect if the provider was not registered.
|
||||||
|
|
||||||
|
Like `registerProvider`, this takes effect immediately when called after the initial load phase, so a `/reload` is not required.
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
pi.registerCommand("my-setup-teardown", {
|
||||||
|
description: "Remove the custom proxy provider",
|
||||||
|
handler: async (_args, _ctx) => {
|
||||||
|
pi.unregisterProvider("my-proxy");
|
||||||
|
},
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
## State Management
|
## State Management
|
||||||
|
|
||||||
Extensions with state should store it in tool result `details` for proper branching support:
|
Extensions with state should store it in tool result `details` for proper branching support:
|
||||||
|
|
|
||||||
|
|
@ -109,7 +109,7 @@ export function createExtensionRuntime(): ExtensionRuntime {
|
||||||
throw new Error("Extension runtime not initialized. Action methods cannot be called during extension loading.");
|
throw new Error("Extension runtime not initialized. Action methods cannot be called during extension loading.");
|
||||||
};
|
};
|
||||||
|
|
||||||
return {
|
const runtime: ExtensionRuntime = {
|
||||||
sendMessage: notInitialized,
|
sendMessage: notInitialized,
|
||||||
sendUserMessage: notInitialized,
|
sendUserMessage: notInitialized,
|
||||||
appendEntry: notInitialized,
|
appendEntry: notInitialized,
|
||||||
|
|
@ -125,7 +125,18 @@ export function createExtensionRuntime(): ExtensionRuntime {
|
||||||
setThinkingLevel: notInitialized,
|
setThinkingLevel: notInitialized,
|
||||||
flagValues: new Map(),
|
flagValues: new Map(),
|
||||||
pendingProviderRegistrations: [],
|
pendingProviderRegistrations: [],
|
||||||
|
// Pre-bind: queue registrations so bindCore() can flush them once the
|
||||||
|
// model registry is available. bindCore() replaces both with direct calls.
|
||||||
|
registerProvider: (name, config) => {
|
||||||
|
runtime.pendingProviderRegistrations.push({ name, config });
|
||||||
|
},
|
||||||
|
unregisterProvider: (name) => {
|
||||||
|
const idx = runtime.pendingProviderRegistrations.findIndex((r) => r.name === name);
|
||||||
|
if (idx !== -1) runtime.pendingProviderRegistrations.splice(idx, 1);
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
return runtime;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -246,7 +257,11 @@ function createExtensionAPI(
|
||||||
},
|
},
|
||||||
|
|
||||||
registerProvider(name: string, config: ProviderConfig) {
|
registerProvider(name: string, config: ProviderConfig) {
|
||||||
runtime.pendingProviderRegistrations.push({ name, config });
|
runtime.registerProvider(name, config);
|
||||||
|
},
|
||||||
|
|
||||||
|
unregisterProvider(name: string) {
|
||||||
|
runtime.unregisterProvider(name);
|
||||||
},
|
},
|
||||||
|
|
||||||
events: eventBus,
|
events: eventBus,
|
||||||
|
|
|
||||||
|
|
@ -259,11 +259,16 @@ export class ExtensionRunner {
|
||||||
this.compactFn = contextActions.compact;
|
this.compactFn = contextActions.compact;
|
||||||
this.getSystemPromptFn = contextActions.getSystemPrompt;
|
this.getSystemPromptFn = contextActions.getSystemPrompt;
|
||||||
|
|
||||||
// Process provider registrations queued during extension loading
|
// Flush provider registrations queued during extension loading
|
||||||
for (const { name, config } of this.runtime.pendingProviderRegistrations) {
|
for (const { name, config } of this.runtime.pendingProviderRegistrations) {
|
||||||
this.modelRegistry.registerProvider(name, config);
|
this.modelRegistry.registerProvider(name, config);
|
||||||
}
|
}
|
||||||
this.runtime.pendingProviderRegistrations = [];
|
this.runtime.pendingProviderRegistrations = [];
|
||||||
|
|
||||||
|
// From this point on, provider registration/unregistration takes effect immediately
|
||||||
|
// without requiring a /reload.
|
||||||
|
this.runtime.registerProvider = (name, config) => this.modelRegistry.registerProvider(name, config);
|
||||||
|
this.runtime.unregisterProvider = (name) => this.modelRegistry.unregisterProvider(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
bindCommandContext(actions?: ExtensionCommandContextActions): void {
|
bindCommandContext(actions?: ExtensionCommandContextActions): void {
|
||||||
|
|
|
||||||
|
|
@ -1071,6 +1071,11 @@ export interface ExtensionAPI {
|
||||||
* If `oauth` is provided: registers OAuth provider for /login support.
|
* If `oauth` is provided: registers OAuth provider for /login support.
|
||||||
* If `streamSimple` is provided: registers a custom API stream handler.
|
* If `streamSimple` is provided: registers a custom API stream handler.
|
||||||
*
|
*
|
||||||
|
* During initial extension load this call is queued and applied once the
|
||||||
|
* runner has bound its context. After that it takes effect immediately, so
|
||||||
|
* it is safe to call from command handlers or event callbacks without
|
||||||
|
* requiring a `/reload`.
|
||||||
|
*
|
||||||
* @example
|
* @example
|
||||||
* // Register a new provider with custom models
|
* // Register a new provider with custom models
|
||||||
* pi.registerProvider("my-proxy", {
|
* pi.registerProvider("my-proxy", {
|
||||||
|
|
@ -1112,6 +1117,21 @@ export interface ExtensionAPI {
|
||||||
*/
|
*/
|
||||||
registerProvider(name: string, config: ProviderConfig): void;
|
registerProvider(name: string, config: ProviderConfig): void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unregister a previously registered provider.
|
||||||
|
*
|
||||||
|
* Removes all models belonging to the named provider and restores any
|
||||||
|
* built-in models that were overridden by it. Has no effect if the provider
|
||||||
|
* was not registered by this extension API.
|
||||||
|
*
|
||||||
|
* Like `registerProvider`, this takes effect immediately when called after
|
||||||
|
* the initial load phase.
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* pi.unregisterProvider("my-proxy");
|
||||||
|
*/
|
||||||
|
unregisterProvider(name: string): void;
|
||||||
|
|
||||||
/** Shared event bus for extension communication. */
|
/** Shared event bus for extension communication. */
|
||||||
events: EventBus;
|
events: EventBus;
|
||||||
}
|
}
|
||||||
|
|
@ -1247,6 +1267,14 @@ export interface ExtensionRuntimeState {
|
||||||
flagValues: Map<string, boolean | string>;
|
flagValues: Map<string, boolean | string>;
|
||||||
/** Provider registrations queued during extension loading, processed when runner binds */
|
/** Provider registrations queued during extension loading, processed when runner binds */
|
||||||
pendingProviderRegistrations: Array<{ name: string; config: ProviderConfig }>;
|
pendingProviderRegistrations: Array<{ name: string; config: ProviderConfig }>;
|
||||||
|
/**
|
||||||
|
* Register or unregister a provider.
|
||||||
|
*
|
||||||
|
* Before bindCore(): queues registrations / removes from queue.
|
||||||
|
* After bindCore(): calls ModelRegistry directly for immediate effect.
|
||||||
|
*/
|
||||||
|
registerProvider: (name: string, config: ProviderConfig) => void;
|
||||||
|
unregisterProvider: (name: string) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -540,6 +540,20 @@ export class ModelRegistry {
|
||||||
this.applyProviderConfig(providerName, config);
|
this.applyProviderConfig(providerName, config);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unregister a previously registered provider.
|
||||||
|
*
|
||||||
|
* Removes the provider from the registry and reloads models from disk so that
|
||||||
|
* built-in models overridden by this provider are restored to their original state.
|
||||||
|
* Has no effect if the provider was never registered.
|
||||||
|
*/
|
||||||
|
unregisterProvider(providerName: string): void {
|
||||||
|
if (!this.registeredProviders.has(providerName)) return;
|
||||||
|
this.registeredProviders.delete(providerName);
|
||||||
|
this.customProviderApiKeys.delete(providerName);
|
||||||
|
this.refresh();
|
||||||
|
}
|
||||||
|
|
||||||
private applyProviderConfig(providerName: string, config: ProviderConfigInput): void {
|
private applyProviderConfig(providerName: string, config: ProviderConfigInput): void {
|
||||||
// Register OAuth provider if provided
|
// Register OAuth provider if provided
|
||||||
if (config.oauth) {
|
if (config.oauth) {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue