refactor(cli): remove bundled cli text providers

This commit is contained in:
Peter Steinberger
2026-04-05 18:42:59 +01:00
parent 79d6713d81
commit 05d351c430
127 changed files with 87 additions and 8258 deletions

View File

@@ -5,11 +5,8 @@ import {
resolveSendableOutboundReplyParts,
} from "openclaw/plugin-sdk/reply-payload";
import { resolveBootstrapWarningSignaturesSeen } from "../../agents/bootstrap-budget.js";
import { runCliAgent } from "../../agents/cli-runner.js";
import { getCliSessionBinding } from "../../agents/cli-session.js";
import { LiveSessionModelSwitchError } from "../../agents/live-model-switch-error.js";
import { runWithModelFallback, isFallbackSummaryError } from "../../agents/model-fallback.js";
import { isCliProvider } from "../../agents/model-selection.js";
import {
BILLING_ERROR_USER_MESSAGE,
isCompactionFailureError,
@@ -697,126 +694,6 @@ export async function runAgentTurnWithFallback(params: {
);
}
if (isCliProvider(provider, params.followupRun.run.config)) {
const startedAt = Date.now();
notifyAgentRunStart();
emitAgentEvent({
runId,
stream: "lifecycle",
data: {
phase: "start",
startedAt,
},
});
const cliSessionBinding = getCliSessionBinding(
params.getActiveSessionEntry(),
provider,
);
const authProfileId =
provider === params.followupRun.run.provider
? params.followupRun.run.authProfileId
: undefined;
return (async () => {
let lifecycleTerminalEmitted = false;
try {
const result = await runCliAgent({
sessionId: params.followupRun.run.sessionId,
sessionKey: params.sessionKey,
agentId: params.followupRun.run.agentId,
sessionFile: params.followupRun.run.sessionFile,
workspaceDir: params.followupRun.run.workspaceDir,
config: params.followupRun.run.config,
prompt: params.commandBody,
provider,
model,
thinkLevel: params.followupRun.run.thinkLevel,
timeoutMs: params.followupRun.run.timeoutMs,
runId,
extraSystemPrompt: params.followupRun.run.extraSystemPrompt,
ownerNumbers: params.followupRun.run.ownerNumbers,
cliSessionId: cliSessionBinding?.sessionId,
cliSessionBinding,
authProfileId,
bootstrapPromptWarningSignaturesSeen,
bootstrapPromptWarningSignature:
bootstrapPromptWarningSignaturesSeen[
bootstrapPromptWarningSignaturesSeen.length - 1
],
images: params.opts?.images,
imageOrder: params.opts?.imageOrder,
messageProvider: params.followupRun.run.messageProvider,
agentAccountId: params.followupRun.run.agentAccountId,
abortSignal: params.replyOperation?.abortSignal ?? params.opts?.abortSignal,
replyOperation: params.replyOperation,
});
bootstrapPromptWarningSignaturesSeen = resolveBootstrapWarningSignaturesSeen(
result.meta?.systemPromptReport,
);
// CLI backends don't emit streaming assistant events, so we need to
// emit one with the final text so server-chat can populate its buffer
// and send the response to TUI/WebSocket clients.
const cliText = result.payloads?.[0]?.text?.trim();
if (cliText) {
emitAgentEvent({
runId,
stream: "assistant",
data: { text: cliText },
});
}
emitAgentEvent({
runId,
stream: "lifecycle",
data: {
phase: "end",
startedAt,
endedAt: Date.now(),
},
});
lifecycleTerminalEmitted = true;
return result;
} catch (err) {
if (rollbackFallbackCandidateSelection) {
try {
await rollbackFallbackCandidateSelection();
} catch (rollbackError) {
logVerbose(
`failed to roll back fallback candidate selection (non-fatal): ${String(rollbackError)}`,
);
}
}
emitAgentEvent({
runId,
stream: "lifecycle",
data: {
phase: "error",
startedAt,
endedAt: Date.now(),
error: String(err),
},
});
lifecycleTerminalEmitted = true;
throw err;
} finally {
// Defensive backstop: never let a CLI run complete without a terminal
// lifecycle event, otherwise downstream consumers can hang.
if (!lifecycleTerminalEmitted) {
emitAgentEvent({
runId,
stream: "lifecycle",
data: {
phase: "error",
startedAt,
endedAt: Date.now(),
error: "CLI run completed without lifecycle terminal event",
},
});
}
}
})();
}
const { embeddedContext, senderContext, runBaseParams } = buildEmbeddedRunExecutionParams(
{
run: params.followupRun.run,

View File

@@ -2,7 +2,6 @@ import fs from "node:fs";
import { lookupContextTokens } from "../../agents/context.js";
import { DEFAULT_CONTEXT_TOKENS } from "../../agents/defaults.js";
import { resolveModelAuthMode } from "../../agents/model-auth.js";
import { isCliProvider } from "../../agents/model-selection.js";
import { queueEmbeddedPiMessage } from "../../agents/pi-embedded.js";
import { hasNonzeroUsage } from "../../agents/usage.js";
import {
@@ -558,12 +557,6 @@ export async function runReplyAgent(params: {
});
}
}
const cliSessionId = isCliProvider(providerUsed, cfg)
? runResult.meta?.agentMeta?.sessionId?.trim()
: undefined;
const cliSessionBinding = isCliProvider(providerUsed, cfg)
? runResult.meta?.agentMeta?.cliSessionBinding
: undefined;
const contextTokensUsed =
agentCfgContextTokens ??
lookupContextTokens(modelUsed) ??
@@ -581,9 +574,7 @@ export async function runReplyAgent(params: {
providerUsed,
contextTokensUsed,
systemPromptReport: runResult.meta?.systemPromptReport,
cliSessionId,
cliSessionBinding,
usageIsContextSnapshot: isCliProvider(providerUsed, cfg),
usageIsContextSnapshot: false,
});
// Drain any late tool/block deliveries before deciding there's "nothing to send".

View File

@@ -42,12 +42,7 @@ import { resolveSubagentLabel } from "./subagents-utils.js";
// Some usage endpoints only work with CLI/session OAuth tokens, not API keys.
// Skip those probes when the active auth mode cannot satisfy the endpoint.
const USAGE_OAUTH_ONLY_PROVIDERS = new Set([
"anthropic",
"github-copilot",
"google-gemini-cli",
"openai-codex",
]);
const USAGE_OAUTH_ONLY_PROVIDERS = new Set(["anthropic", "github-copilot", "openai-codex"]);
function shouldLoadUsageSummary(params: {
provider?: string;

View File

@@ -8,7 +8,6 @@ import { resolveBootstrapWarningSignaturesSeen } from "../../agents/bootstrap-bu
import { lookupContextTokens } from "../../agents/context.js";
import { DEFAULT_CONTEXT_TOKENS } from "../../agents/defaults.js";
import { runWithModelFallback } from "../../agents/model-fallback.js";
import { isCliProvider } from "../../agents/model-selection.js";
import { runEmbeddedPiAgent } from "../../agents/pi-embedded.js";
import type { SessionEntry } from "../../config/sessions.js";
import type { TypingMode } from "../../config/types.js";
@@ -305,11 +304,7 @@ export function createFollowupRunner(params: {
providerUsed: fallbackProvider,
contextTokensUsed,
systemPromptReport: runResult.meta?.systemPromptReport,
cliSessionBinding: runResult.meta?.agentMeta?.cliSessionBinding,
usageIsContextSnapshot: isCliProvider(
fallbackProvider ?? queued.run.provider,
queued.run.config,
),
usageIsContextSnapshot: false,
logLabel: "followup",
});
}

View File

@@ -1,4 +1,3 @@
import { setCliSessionBinding, setCliSessionId } from "../../agents/cli-session.js";
import {
deriveSessionTotalTokens,
hasNonzeroUsage,
@@ -14,39 +13,6 @@ import {
import { logVerbose } from "../../globals.js";
import { estimateUsageCost, resolveModelCostConfig } from "../../utils/usage-format.js";
function applyCliSessionIdToSessionPatch(
params: {
providerUsed?: string;
cliSessionId?: string;
cliSessionBinding?: import("../../config/sessions.js").CliSessionBinding;
},
entry: SessionEntry,
patch: Partial<SessionEntry>,
): Partial<SessionEntry> {
const cliProvider = params.providerUsed ?? entry.modelProvider;
if (params.cliSessionBinding && cliProvider) {
const nextEntry = { ...entry, ...patch };
setCliSessionBinding(nextEntry, cliProvider, params.cliSessionBinding);
return {
...patch,
cliSessionIds: nextEntry.cliSessionIds,
cliSessionBindings: nextEntry.cliSessionBindings,
claudeCliSessionId: nextEntry.claudeCliSessionId,
};
}
if (params.cliSessionId && cliProvider) {
const nextEntry = { ...entry, ...patch };
setCliSessionId(nextEntry, cliProvider, params.cliSessionId);
return {
...patch,
cliSessionIds: nextEntry.cliSessionIds,
cliSessionBindings: nextEntry.cliSessionBindings,
claudeCliSessionId: nextEntry.claudeCliSessionId,
};
}
return patch;
}
function resolveNonNegativeNumber(value: number | undefined): number | undefined {
return typeof value === "number" && Number.isFinite(value) && value >= 0 ? value : undefined;
}
@@ -86,8 +52,6 @@ export async function persistSessionUsageUpdate(params: {
promptTokens?: number;
usageIsContextSnapshot?: boolean;
systemPromptReport?: SessionSystemPromptReport;
cliSessionId?: string;
cliSessionBinding?: import("../../config/sessions.js").CliSessionBinding;
logLabel?: string;
}): Promise<void> {
const { storePath, sessionKey } = params;
@@ -158,7 +122,7 @@ export async function persistSessionUsageUpdate(params: {
// context utilization is stale/unknown.
patch.totalTokens = totalTokens;
patch.totalTokensFresh = typeof totalTokens === "number";
return applyCliSessionIdToSessionPatch(params, entry, patch);
return patch;
},
});
} catch (err) {
@@ -180,7 +144,7 @@ export async function persistSessionUsageUpdate(params: {
systemPromptReport: params.systemPromptReport ?? entry.systemPromptReport,
updatedAt: Date.now(),
};
return applyCliSessionIdToSessionPatch(params, entry, patch);
return patch;
},
});
} catch (err) {

View File

@@ -569,9 +569,6 @@ export async function initSessionState(params: {
persistedAuthProfileOverrideSource ?? baseEntry?.authProfileOverrideSource,
authProfileOverrideCompactionCount:
persistedAuthProfileOverrideCompactionCount ?? baseEntry?.authProfileOverrideCompactionCount,
cliSessionIds: baseEntry?.cliSessionIds,
cliSessionBindings: baseEntry?.cliSessionBindings,
claudeCliSessionId: baseEntry?.claudeCliSessionId,
label: persistedLabel ?? baseEntry?.label,
spawnedBy: persistedSpawnedBy ?? baseEntry?.spawnedBy,
spawnedWorkspaceDir: persistedSpawnedWorkspaceDir ?? baseEntry?.spawnedWorkspaceDir,