summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorAdam Malczewski <[email protected]>2026-05-29 14:24:55 +0900
committerAdam Malczewski <[email protected]>2026-05-29 14:24:55 +0900
commitaa230050f4edb7bfc8d3e4d59d95c68c36264b41 (patch)
treeee9c4b4d2087618f41bf7bdcaed6a145fc0f0125
parent520c9e30cc58b40d3b1ee9e7895f003c4f873206 (diff)
downloaddispatch-aa230050f4edb7bfc8d3e4d59d95c68c36264b41.tar.gz
dispatch-aa230050f4edb7bfc8d3e4d59d95c68c36264b41.zip
fix: refresh agent config on send; widen fallback retry detection
- Refresh agent config from API before sending a message so edits in AgentBuilder (changed keyId/modelId/agentModels) take effect immediately on existing tabs instead of using stale snapshots - Broaden isRetryable check to also match 'usage limit' and 'exhausted' so fallback keys are actually tried on quota errors
-rw-r--r--packages/api/src/agent-manager.ts4
-rw-r--r--packages/frontend/src/lib/tabs.svelte.ts44
2 files changed, 46 insertions, 2 deletions
diff --git a/packages/api/src/agent-manager.ts b/packages/api/src/agent-manager.ts
index 88503f3..9ed2f51 100644
--- a/packages/api/src/agent-manager.ts
+++ b/packages/api/src/agent-manager.ts
@@ -1248,7 +1248,9 @@ export class AgentManager {
const isRetryable =
attemptError.includes("status=429") ||
attemptError.toLowerCase().includes("rate limit") ||
- attemptError.toLowerCase().includes("rate_limit");
+ attemptError.toLowerCase().includes("rate_limit") ||
+ attemptError.toLowerCase().includes("usage limit") ||
+ attemptError.toLowerCase().includes("exhausted");
if (isRetryable && this.modelRegistry && tabAgent.keyId) {
this.modelRegistry.markKeyExhausted(tabAgent.keyId, attemptError);
diff --git a/packages/frontend/src/lib/tabs.svelte.ts b/packages/frontend/src/lib/tabs.svelte.ts
index b2c4480..9b31b3f 100644
--- a/packages/frontend/src/lib/tabs.svelte.ts
+++ b/packages/frontend/src/lib/tabs.svelte.ts
@@ -1194,6 +1194,41 @@ export function createTabStore() {
}
}
+ async function refreshAgentConfig(tabId: string): Promise<void> {
+ const tab = getTabById(tabId);
+ if (!tab?.agentSlug || !tab.agentScope) return;
+ try {
+ const res = await fetch(`${config.apiBase}/agents`);
+ if (!res.ok) return;
+ const data = (await res.json()) as {
+ agents?: Array<{
+ slug: string;
+ scope: string;
+ models: Array<{ key_id: string; model_id: string }>;
+ cwd?: string;
+ }>;
+ };
+ const agents = data.agents ?? [];
+ const freshAgent = agents.find((a) => a.slug === tab.agentSlug && a.scope === tab.agentScope);
+ if (!freshAgent) return;
+ const firstModel = freshAgent.models[0];
+ const patch: Partial<Tab> = {
+ agentModels: freshAgent.models,
+ workingDirectory: freshAgent.cwd || null,
+ };
+ if (firstModel) {
+ patch.keyId = firstModel.key_id;
+ patch.modelId = firstModel.model_id;
+ } else {
+ patch.keyId = null;
+ patch.modelId = null;
+ }
+ updateTab(tabId, patch);
+ } catch {
+ // Silently fall back to stale values
+ }
+ }
+
async function fetchSkillContent(scope: string, name: string): Promise<string | null> {
try {
const res = await fetch(
@@ -1208,9 +1243,16 @@ export function createTabStore() {
}
async function sendMessage(text: string): Promise<void> {
- const tab = getActiveTab();
+ let tab = getActiveTab();
if (!tab) return;
+ // Refresh agent config to pick up any changes made in AgentBuilder
+ if (tab.agentSlug && tab.agentScope) {
+ await refreshAgentConfig(tab.id);
+ tab = getActiveTab();
+ if (!tab) return;
+ }
+
// Fetch content for checked skills and build the message to send
let messageToSend = text;
const checkedKeys = Object.entries(appSettings.skillChecks)