From b76a7f453bd48e686d4e97f22e39c75ffad7818f Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Mon, 13 Jul 2026 06:17:20 -0700 Subject: [PATCH] refactor(auto-reply): trim internal export surface (#106409) * refactor(auto-reply): trim internal export surface * chore(deadcode): update auto-reply export baseline --- scripts/deadcode-exports.baseline.mjs | 20 ------------------ src/auto-reply/reply-payload.test.ts | 14 +++++++------ src/auto-reply/reply-payload.ts | 11 ---------- .../reply/agent-runner-cli-dispatch.test.ts | 12 +++++++---- .../reply/agent-runner-cli-dispatch.ts | 4 ++-- .../reply/agent-runner-execution.test.ts | 14 +++++++------ .../reply/agent-runner-execution.ts | 2 +- .../reply/agent-runner-utils.test.ts | 21 ++++++++++++------- src/auto-reply/reply/agent-runner-utils.ts | 11 +++------- ...t-runner.final-media-runreplyagent.test.ts | 5 ++++- .../agent-runner.runreplyagent.e2e.test.ts | 5 ++++- .../reply/commands-subagents-routing.test.ts | 3 ++- .../reply/commands-subagents/shared.ts | 2 +- .../reply/effective-reply-route.test.ts | 11 +++++----- src/auto-reply/reply/effective-reply-route.ts | 4 ++-- src/auto-reply/reply/inbound-dedupe.test.ts | 8 +++---- src/auto-reply/reply/inbound-dedupe.ts | 2 +- .../reply/progress-narrator.test.ts | 5 ++++- src/auto-reply/reply/progress-narrator.ts | 2 +- .../provider-request-error-classifier.test.ts | 13 ++++++++---- .../provider-request-error-classifier.ts | 8 +++---- src/auto-reply/reply/queue.ts | 6 +----- .../reply/reply-operation-run-state.ts | 2 +- src/auto-reply/reply/session-fork.ts | 4 ++-- .../reply/stranded-reply-recovery.test.ts | 7 +++---- .../reply/stranded-reply-recovery.ts | 2 +- .../chat.directive-tags.test.ts | 15 +++++++------ src/talk/agent-consult-runtime.test.ts | 9 ++++---- 28 files changed, 104 insertions(+), 118 deletions(-) diff --git a/scripts/deadcode-exports.baseline.mjs b/scripts/deadcode-exports.baseline.mjs index c152a3f0e28d..e79f8226b4b5 100644 --- a/scripts/deadcode-exports.baseline.mjs +++ b/scripts/deadcode-exports.baseline.mjs @@ -1254,22 +1254,16 @@ export const KNIP_UNUSED_EXPORT_BASELINE = [ "src/auto-reply/command-detection.runtime-types.ts: IsControlCommandMessage", "src/auto-reply/command-detection.runtime-types.ts: ShouldComputeCommandAuthorized", "src/auto-reply/commands-registry.runtime-types.ts: ShouldHandleTextCommands", - "src/auto-reply/reply-payload.ts: buildPairingQrReplyChannelData", "src/auto-reply/reply/abort.ts: getAbortMemory", "src/auto-reply/reply/abort.ts: getAbortMemorySizeForTest", "src/auto-reply/reply/abort.ts: resetAbortMemoryForTest", "src/auto-reply/reply/abort.ts: testing", "src/auto-reply/reply/acp-reset-target.ts: testing", - "src/auto-reply/reply/agent-runner-cli-dispatch.ts: ReasoningProgressPayload", - "src/auto-reply/reply/agent-runner-cli-dispatch.ts: ReasoningTextPayload", - "src/auto-reply/reply/agent-runner-execution.ts: AgentRunLoopResult", "src/auto-reply/reply/agent-runner-execution.ts: buildContextOverflowRecoveryText", "src/auto-reply/reply/agent-runner-execution.ts: computeContextAwareReserveTokensFloor", "src/auto-reply/reply/agent-runner-execution.ts: MAX_LIVE_SWITCH_RETRIES", "src/auto-reply/reply/agent-runner-memory.ts: setAgentRunnerMemoryTestDeps", "src/auto-reply/reply/agent-runner-session-reset.ts: setAgentRunnerSessionResetTestDeps", - "src/auto-reply/reply/agent-runner-utils.ts: buildEmbeddedRunBaseParams", - "src/auto-reply/reply/agent-runner-utils.ts: resolveProviderScopedAuthProfile", "src/auto-reply/reply/block-reply-pipeline.ts: createBlockReplyPayloadKey", "src/auto-reply/reply/commands-diagnostics.ts: createDiagnosticsCommandHandler", "src/auto-reply/reply/commands-login.ts: testing", @@ -1277,7 +1271,6 @@ export const KNIP_UNUSED_EXPORT_BASELINE = [ "src/auto-reply/reply/commands-name.ts: parseNameCommand", "src/auto-reply/reply/commands-setunset.ts: parseSetUnsetCommand", "src/auto-reply/reply/commands-setunset.ts: parseSetUnsetCommandAction", - "src/auto-reply/reply/commands-subagents/shared.ts: COMMAND", "src/auto-reply/reply/commands-tasks.ts: buildTasksReply", "src/auto-reply/reply/completion-delivery-policy.ts: resolveCompletionChatType", "src/auto-reply/reply/delivery-hints.ts: MESSAGE_TOOL_ONLY_DELIVERY_HINT", @@ -1286,8 +1279,6 @@ export const KNIP_UNUSED_EXPORT_BASELINE = [ "src/auto-reply/reply/dispatch-from-config.ts: testing", "src/auto-reply/reply/dispatch-from-config.types.ts: DispatchReplyFromConfig", "src/auto-reply/reply/dispatcher-registry.ts: clearAllDispatchers", - "src/auto-reply/reply/effective-reply-route.ts: EffectiveReplyRouteContext", - "src/auto-reply/reply/effective-reply-route.ts: EffectiveReplyRouteEntry", "src/auto-reply/reply/get-reply-directives-apply.ts: formatModelOverrideResetEvent", "src/auto-reply/reply/get-reply-fast-path.ts: markCompleteReplyConfig", "src/auto-reply/reply/get-reply-fast-path.ts: withFastReplyConfig", @@ -1296,23 +1287,15 @@ export const KNIP_UNUSED_EXPORT_BASELINE = [ "src/auto-reply/reply/get-reply-run.ts: resolvePromptSilentReplyConversationType", "src/auto-reply/reply/history.ts: appendHistoryEntry", "src/auto-reply/reply/inbound-dedupe.ts: buildInboundDedupeKey", - "src/auto-reply/reply/inbound-dedupe.ts: InboundDedupeClaimResult", "src/auto-reply/reply/mentions.types.ts: BuildMentionRegexes", "src/auto-reply/reply/mentions.types.ts: MatchesMentionPatterns", "src/auto-reply/reply/mentions.types.ts: MatchesMentionWithExplicit", "src/auto-reply/reply/progress-narrator.ts: createProgressNarrator", - "src/auto-reply/reply/progress-narrator.ts: ProgressNarrationInput", "src/auto-reply/reply/prompt-prelude.ts: buildReplyPromptBodies", - "src/auto-reply/reply/provider-request-error-classifier.ts: PROVIDER_AUTHENTICATION_ERROR_USER_MESSAGE", - "src/auto-reply/reply/provider-request-error-classifier.ts: PROVIDER_INTERNAL_ERROR_USER_MESSAGE", - "src/auto-reply/reply/provider-request-error-classifier.ts: PROVIDER_MODEL_UNAVAILABLE_USER_MESSAGE", - "src/auto-reply/reply/provider-request-error-classifier.ts: PROVIDER_RATE_LIMIT_OR_QUOTA_ERROR_USER_MESSAGE", - "src/auto-reply/reply/queue.ts: resetRecentQueuedMessageIdDedupe", "src/auto-reply/reply/queue/cleanup.ts: testing", "src/auto-reply/reply/queue/drain.ts: resolveFollowupAuthorizationKey", "src/auto-reply/reply/queue/enqueue.ts: resetRecentQueuedMessageIdDedupe", "src/auto-reply/reply/reply-dispatcher.runtime-types.ts: CreateReplyDispatcherWithTyping", - "src/auto-reply/reply/reply-operation-run-state.ts: ReplyOptionsWithOperationRunState", "src/auto-reply/reply/reply-payloads-dedupe.ts: getMatchingMessagingToolReplyTargets", "src/auto-reply/reply/reply-run-registry.ts: __testing", "src/auto-reply/reply/reply-run-registry.ts: testing", @@ -1320,13 +1303,10 @@ export const KNIP_UNUSED_EXPORT_BASELINE = [ "src/auto-reply/reply/reply-threading.ts: resolveConfiguredReplyToMode", "src/auto-reply/reply/reply-threading.ts: resolveReplyToModeWithThreading", "src/auto-reply/reply/reply-usage-state.ts: clearReplyUsageStateForTest", - "src/auto-reply/reply/session-fork.ts: ForkSessionEntryFromParentParams", - "src/auto-reply/reply/session-fork.ts: ForkSessionEntryFromParentResult", "src/auto-reply/reply/session-reset-prompt.ts: buildBareSessionResetPrompt", "src/auto-reply/reply/stage-sandbox-media.ts: appendScpStderrTail", "src/auto-reply/reply/stage-sandbox-media.ts: SCP_STDERR_TAIL_CHARS", "src/auto-reply/reply/stage-sandbox-media.ts: testing", - "src/auto-reply/reply/stranded-reply-recovery.ts: STRANDED_REPLY_RETRY_MARKER", "src/auto-reply/usage-bar/template.ts: clearUsageBarTemplateCacheForTest", "src/cli/channel-options.ts: testing", "src/cli/command-path-policy.ts: resolveCliCatalogCommandPath", diff --git a/src/auto-reply/reply-payload.test.ts b/src/auto-reply/reply-payload.test.ts index eb3edaba1ae8..9fa919a5d1d1 100644 --- a/src/auto-reply/reply-payload.test.ts +++ b/src/auto-reply/reply-payload.test.ts @@ -1,13 +1,15 @@ // Reply payload tests cover internal reply metadata contracts. import { describe, expect, it } from "vitest"; -import { buildPairingQrReplyChannelData, readPairingQrReplyChannelData } from "./reply-payload.js"; +import { readPairingQrReplyChannelData } from "./reply-payload.js"; describe("pairing QR reply channel data", () => { - it("builds and reads the private pairing QR payload metadata", () => { - const channelData = buildPairingQrReplyChannelData({ - setupCode: "setup-code", - expiresAtMs: 1_800_000_000_000, - }); + it("reads the private pairing QR payload metadata", () => { + const channelData = { + openclawPairingQr: { + setupCode: "setup-code", + expiresAtMs: 1_800_000_000_000, + }, + }; expect(readPairingQrReplyChannelData({ channelData })).toEqual({ setupCode: "setup-code", diff --git a/src/auto-reply/reply-payload.ts b/src/auto-reply/reply-payload.ts index 53c291ee8499..12186768b9dd 100644 --- a/src/auto-reply/reply-payload.ts +++ b/src/auto-reply/reply-payload.ts @@ -86,17 +86,6 @@ function normalizePairingQrExpiresAtMs(value: unknown): number | undefined { return typeof value === "number" && Number.isFinite(value) && value > 0 ? value : undefined; } -export function buildPairingQrReplyChannelData( - params: PairingQrReplyChannelData, -): Record { - return { - [PAIRING_QR_REPLY_CHANNEL_DATA_KEY]: { - setupCode: params.setupCode, - expiresAtMs: params.expiresAtMs, - }, - }; -} - export function readPairingQrReplyChannelData( payload: Pick, ): PairingQrReplyChannelData | undefined { diff --git a/src/auto-reply/reply/agent-runner-cli-dispatch.test.ts b/src/auto-reply/reply/agent-runner-cli-dispatch.test.ts index 66386666510d..64944e7aedda 100644 --- a/src/auto-reply/reply/agent-runner-cli-dispatch.test.ts +++ b/src/auto-reply/reply/agent-runner-cli-dispatch.test.ts @@ -9,16 +9,20 @@ import { onAgentEvent, resetAgentEventsForTest, } from "../../infra/agent-events.js"; -import type { - ReasoningProgressPayload, - ReasoningTextPayload, -} from "./agent-runner-cli-dispatch.js"; import { createCliToolSummaryTracker, keepCliSessionBindingOnlyWhenReused, runCliAgentWithLifecycle, } from "./agent-runner-cli-dispatch.js"; +type RunCliAgentWithLifecycleParams = Parameters[0]; +type ReasoningTextPayload = Parameters< + NonNullable +>[0]; +type ReasoningProgressPayload = Parameters< + NonNullable +>[0]; + const cliDispatchState = vi.hoisted(() => ({ runCliAgentMock: vi.fn(), })); diff --git a/src/auto-reply/reply/agent-runner-cli-dispatch.ts b/src/auto-reply/reply/agent-runner-cli-dispatch.ts index 8f85a55db22a..135cfee6e7cc 100644 --- a/src/auto-reply/reply/agent-runner-cli-dispatch.ts +++ b/src/auto-reply/reply/agent-runner-cli-dispatch.ts @@ -147,12 +147,12 @@ function createAssistantTextBridge(params: { }); } -export type ReasoningTextPayload = { +type ReasoningTextPayload = { text: string; isReasoningSnapshot?: boolean; }; -export type ReasoningProgressPayload = { +type ReasoningProgressPayload = { progressTokens: number; }; diff --git a/src/auto-reply/reply/agent-runner-execution.test.ts b/src/auto-reply/reply/agent-runner-execution.test.ts index 2c9b128f4c7d..81b4238e7a23 100644 --- a/src/auto-reply/reply/agent-runner-execution.test.ts +++ b/src/auto-reply/reply/agent-runner-execution.test.ts @@ -3,6 +3,7 @@ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import { OAuthRefreshFailureError } from "../../agents/auth-profiles/oauth-refresh-failure.js"; import { testing as cliBackendsTesting } from "../../agents/cli-backends.js"; import { formatBillingErrorMessage } from "../../agents/embedded-agent-helpers.js"; +import { AUTH_INVALID_TOKEN_USER_TEXT } from "../../agents/embedded-agent-helpers/errors.js"; import { FailoverError } from "../../agents/failover-error.js"; import { LiveSessionModelSwitchError } from "../../agents/live-model-switch-error.js"; import { MissingProviderAuthError } from "../../agents/model-auth.js"; @@ -35,16 +36,17 @@ import { resolveRunAfterAutoFallbackPrimaryProbeRecheck, } from "./agent-runner-execution.js"; import { HEARTBEAT_EXTERNAL_RUN_FAILURE_TEXT } from "./agent-runner-failure-copy.js"; -import { - PROVIDER_AUTHENTICATION_ERROR_USER_MESSAGE, - PROVIDER_CONVERSATION_STATE_ERROR_USER_MESSAGE, - PROVIDER_INTERNAL_ERROR_USER_MESSAGE, - PROVIDER_RATE_LIMIT_OR_QUOTA_ERROR_USER_MESSAGE, -} from "./provider-request-error-classifier.js"; +import { PROVIDER_CONVERSATION_STATE_ERROR_USER_MESSAGE } from "./provider-request-error-classifier.js"; import type { FollowupRun } from "./queue.js"; import { createReplyOperation, type ReplyOperation } from "./reply-run-registry.js"; import type { TypingSignaler } from "./typing-mode.js"; +const PROVIDER_AUTHENTICATION_ERROR_USER_MESSAGE = `⚠️ ${AUTH_INVALID_TOKEN_USER_TEXT}`; +const PROVIDER_RATE_LIMIT_OR_QUOTA_ERROR_USER_MESSAGE = + "⚠️ The model provider returned HTTP 429 before replying. This can mean rate limiting, exhausted quota, or an account balance/billing issue. Check the selected provider/model, API key, and provider billing/quota dashboard, then try again."; +const PROVIDER_INTERNAL_ERROR_USER_MESSAGE = + "⚠️ The model provider returned a temporary internal error before replying. Try again in a moment, or switch to another model if it keeps happening."; + const state = vi.hoisted(() => ({ runEmbeddedAgentMock: vi.fn(), runCliAgentMock: vi.fn(), diff --git a/src/auto-reply/reply/agent-runner-execution.ts b/src/auto-reply/reply/agent-runner-execution.ts index 7b6a5d0b3c19..9d58636ffaea 100644 --- a/src/auto-reply/reply/agent-runner-execution.ts +++ b/src/auto-reply/reply/agent-runner-execution.ts @@ -418,7 +418,7 @@ export type RuntimeFallbackAttempt = { }; /** Result of running an agent turn through fallback/retry handling. */ -export type AgentRunLoopResult = +type AgentRunLoopResult = | { kind: "success"; runId: string; diff --git a/src/auto-reply/reply/agent-runner-utils.test.ts b/src/auto-reply/reply/agent-runner-utils.test.ts index 2c18c80ee13c..271fa34afeac 100644 --- a/src/auto-reply/reply/agent-runner-utils.test.ts +++ b/src/auto-reply/reply/agent-runner-utils.test.ts @@ -22,13 +22,20 @@ vi.mock("../../utils/provider-utils.js", () => ({ isReasoningTagProvider: (...args: unknown[]) => hoisted.isReasoningTagProviderMock(...args), })); -const { - buildThreadingToolContext, - buildEmbeddedRunBaseParams, - buildEmbeddedRunExecutionParams, - resolveModelFallbackOptions, - resolveProviderScopedAuthProfile, -} = await import("./agent-runner-utils.js"); +const { buildThreadingToolContext, buildEmbeddedRunExecutionParams, resolveModelFallbackOptions } = + await import("./agent-runner-utils.js"); +const { resolveProviderScopedAuthProfile } = await import("./agent-runner-auth-profile.js"); +const { buildEmbeddedRunBaseParams: buildEmbeddedRunBaseParamsCore } = + await import("./agent-runner-run-params.js"); + +function buildEmbeddedRunBaseParams( + params: Omit[0], "isReasoningTagProvider">, +) { + return buildEmbeddedRunBaseParamsCore({ + ...params, + isReasoningTagProvider: hoisted.isReasoningTagProviderMock, + }); +} function makeRun(overrides: Partial = {}): FollowupRun["run"] { return { diff --git a/src/auto-reply/reply/agent-runner-utils.ts b/src/auto-reply/reply/agent-runner-utils.ts index c14f3faabec1..3a6623fed6a6 100644 --- a/src/auto-reply/reply/agent-runner-utils.ts +++ b/src/auto-reply/reply/agent-runner-utils.ts @@ -26,11 +26,8 @@ import { import type { SessionEntry } from "../../config/sessions.js"; import { isReasoningTagProvider } from "../../utils/provider-utils.js"; import type { TemplateContext } from "../templating.js"; -import { - resolveProviderScopedAuthProfile, - resolveRunAuthProfile, -} from "./agent-runner-auth-profile.js"; -export { resolveProviderScopedAuthProfile, resolveRunAuthProfile }; +import { resolveRunAuthProfile } from "./agent-runner-auth-profile.js"; +export { resolveRunAuthProfile }; import { buildEmbeddedRunBaseParams as buildEmbeddedRunBaseParamsCore } from "./agent-runner-run-params.js"; export { resolveModelFallbackOptions } from "./agent-runner-run-params.js"; import { hasInboundAudio } from "./inbound-media.js"; @@ -231,9 +228,7 @@ export function resolveRunFastModeForFallbackCandidate(params: { }; } /** Builds base embedded run params with auth and provider runtime hints. */ -export function buildEmbeddedRunBaseParams( - params: Parameters[0], -) { +function buildEmbeddedRunBaseParams(params: Parameters[0]) { return buildEmbeddedRunBaseParamsCore({ ...params, isReasoningTagProvider, diff --git a/src/auto-reply/reply/agent-runner.final-media-runreplyagent.test.ts b/src/auto-reply/reply/agent-runner.final-media-runreplyagent.test.ts index 4572d75154f0..de9f9a1f53ad 100644 --- a/src/auto-reply/reply/agent-runner.final-media-runreplyagent.test.ts +++ b/src/auto-reply/reply/agent-runner.final-media-runreplyagent.test.ts @@ -2,7 +2,6 @@ import path from "node:path"; import { beforeEach, describe, expect, it, vi } from "vitest"; import type { TemplateContext } from "../templating.js"; -import type { AgentRunLoopResult } from "./agent-runner-execution.js"; import type { FollowupRun, QueueSettings } from "./queue.js"; import type { ReplyOperation } from "./reply-run-registry.js"; import { createMockFollowupRun, createMockTypingController } from "./test-helpers.js"; @@ -102,6 +101,10 @@ vi.mock("./session-run-accounting.js", () => ({ const { runReplyAgent } = await import("./agent-runner.js"); +type AgentRunLoopResult = Awaited< + ReturnType +>; + function createReplyOperation(): ReplyOperation { return { result: undefined, diff --git a/src/auto-reply/reply/agent-runner.runreplyagent.e2e.test.ts b/src/auto-reply/reply/agent-runner.runreplyagent.e2e.test.ts index ef652d7031b0..cc352570fb9e 100644 --- a/src/auto-reply/reply/agent-runner.runreplyagent.e2e.test.ts +++ b/src/auto-reply/reply/agent-runner.runreplyagent.e2e.test.ts @@ -28,7 +28,6 @@ import { import { REPLY_OPERATION_RUN_STATE, type ReplyOperationRunState, - type ReplyOptionsWithOperationRunState, } from "./reply-operation-run-state.js"; import { createReplyOperation, @@ -38,6 +37,10 @@ import { import { consumeReplyUsageState } from "./reply-usage-state.js"; import { createMockTypingController } from "./test-helpers.js"; +type ReplyOptionsWithOperationRunState = { + [REPLY_OPERATION_RUN_STATE]?: ReplyOperationRunState; +}; + type AgentRunParams = { sessionId?: string; sessionFile?: string; diff --git a/src/auto-reply/reply/commands-subagents-routing.test.ts b/src/auto-reply/reply/commands-subagents-routing.test.ts index 8afb60609eb5..64a55f515b31 100644 --- a/src/auto-reply/reply/commands-subagents-routing.test.ts +++ b/src/auto-reply/reply/commands-subagents-routing.test.ts @@ -16,9 +16,10 @@ import { stopWithText, } from "./commands-subagents-dispatch.js"; import { handleSubagentsCommand } from "./commands-subagents.js"; -import { COMMAND } from "./commands-subagents/shared.js"; import type { HandleCommandsParams } from "./commands-types.js"; +const COMMAND = "/subagents"; + const listControlledSubagentRunsMock = vi.hoisted(() => vi.fn(() => [])); vi.mock("./commands-subagents-control.runtime.js", () => ({ diff --git a/src/auto-reply/reply/commands-subagents/shared.ts b/src/auto-reply/reply/commands-subagents/shared.ts index e9ed3102cca7..0d903b590ed2 100644 --- a/src/auto-reply/reply/commands-subagents/shared.ts +++ b/src/auto-reply/reply/commands-subagents/shared.ts @@ -28,7 +28,7 @@ import { export type { ChatMessage } from "../commands-subagents-text.js"; -export const COMMAND = "/subagents"; +const COMMAND = "/subagents"; const COMMAND_FOCUS = "/focus"; const COMMAND_UNFOCUS = "/unfocus"; const COMMAND_AGENTS = "/agents"; diff --git a/src/auto-reply/reply/effective-reply-route.test.ts b/src/auto-reply/reply/effective-reply-route.test.ts index 70d82574d753..402520a8fcb5 100644 --- a/src/auto-reply/reply/effective-reply-route.test.ts +++ b/src/auto-reply/reply/effective-reply-route.test.ts @@ -1,11 +1,10 @@ // Tests effective reply route selection from context, session, and fallback state. import { describe, expect, it } from "vitest"; -import { - isSystemEventProvider, - resolveEffectiveReplyRoute, - type EffectiveReplyRouteContext, - type EffectiveReplyRouteEntry, -} from "./effective-reply-route.js"; +import { isSystemEventProvider, resolveEffectiveReplyRoute } from "./effective-reply-route.js"; + +type EffectiveReplyRouteParams = Parameters[0]; +type EffectiveReplyRouteContext = EffectiveReplyRouteParams["ctx"]; +type EffectiveReplyRouteEntry = NonNullable; const ctx = (params: EffectiveReplyRouteContext): EffectiveReplyRouteContext => params; const entry = (params: EffectiveReplyRouteEntry): EffectiveReplyRouteEntry => params; diff --git a/src/auto-reply/reply/effective-reply-route.ts b/src/auto-reply/reply/effective-reply-route.ts index 58de0888041c..2257af09c12e 100644 --- a/src/auto-reply/reply/effective-reply-route.ts +++ b/src/auto-reply/reply/effective-reply-route.ts @@ -7,7 +7,7 @@ import { INTERNAL_MESSAGE_CHANNEL, normalizeMessageChannel } from "../../utils/m import type { FinalizedMsgContext } from "../templating.js"; /** Current finalized context fields used for reply route resolution. */ -export type EffectiveReplyRouteContext = Pick< +type EffectiveReplyRouteContext = Pick< FinalizedMsgContext, | "Provider" | "Surface" @@ -19,7 +19,7 @@ export type EffectiveReplyRouteContext = Pick< >; /** Persisted session fields used as route fallback/inheritance. */ -export type EffectiveReplyRouteEntry = Pick< +type EffectiveReplyRouteEntry = Pick< SessionEntry, "deliveryContext" | "lastChannel" | "lastTo" | "lastAccountId" | "route" | "chatType" | "origin" >; diff --git a/src/auto-reply/reply/inbound-dedupe.test.ts b/src/auto-reply/reply/inbound-dedupe.test.ts index 3379ec5bb9a4..d598eb6a03d4 100644 --- a/src/auto-reply/reply/inbound-dedupe.test.ts +++ b/src/auto-reply/reply/inbound-dedupe.test.ts @@ -2,11 +2,9 @@ import { importFreshModule } from "openclaw/plugin-sdk/test-fixtures"; import { afterEach, describe, expect, it } from "vitest"; import type { MsgContext } from "../templating.js"; -import { - buildInboundDedupeKey, - resetInboundDedupe, - type InboundDedupeClaimResult, -} from "./inbound-dedupe.js"; +import { buildInboundDedupeKey, resetInboundDedupe } from "./inbound-dedupe.js"; + +type InboundDedupeClaimResult = ReturnType; const sharedInboundContext: MsgContext = { Provider: "discord", diff --git a/src/auto-reply/reply/inbound-dedupe.ts b/src/auto-reply/reply/inbound-dedupe.ts index 4b5596d696de..56589ba1bd14 100644 --- a/src/auto-reply/reply/inbound-dedupe.ts +++ b/src/auto-reply/reply/inbound-dedupe.ts @@ -29,7 +29,7 @@ const inboundDedupeInFlight = resolveGlobalSingleton( () => new Set(), ); -export type InboundDedupeClaimResult = +type InboundDedupeClaimResult = | { status: "invalid" } | { status: "duplicate"; key: string } | { status: "inflight"; key: string } diff --git a/src/auto-reply/reply/progress-narrator.test.ts b/src/auto-reply/reply/progress-narrator.test.ts index d2613a627ac1..e11a7bb414a4 100644 --- a/src/auto-reply/reply/progress-narrator.test.ts +++ b/src/auto-reply/reply/progress-narrator.test.ts @@ -18,9 +18,12 @@ vi.mock("../../logging/subsystem.js", async (importOriginal) => { import { attachProgressNarratorToReplyOptions, createProgressNarrator, - type ProgressNarrationInput, } from "./progress-narrator.js"; +type ProgressNarrationInput = Parameters< + NonNullable[0]["generate"]> +>[0]; + const cfg = {} as OpenClawConfig; // The narrator runs generations on a detached promise; drain microtasks so diff --git a/src/auto-reply/reply/progress-narrator.ts b/src/auto-reply/reply/progress-narrator.ts index 3a18619cda00..828fc33785e3 100644 --- a/src/auto-reply/reply/progress-narrator.ts +++ b/src/auto-reply/reply/progress-narrator.ts @@ -39,7 +39,7 @@ const NARRATION_SYSTEM_PROMPT = [ "Reply with the status text only.", ].join(" "); -export type ProgressNarrationInput = { +type ProgressNarrationInput = { userMessage: string; activityNotes: readonly string[]; previousText: string; diff --git a/src/auto-reply/reply/provider-request-error-classifier.test.ts b/src/auto-reply/reply/provider-request-error-classifier.test.ts index b4768dd5e616..c655da2c571e 100644 --- a/src/auto-reply/reply/provider-request-error-classifier.test.ts +++ b/src/auto-reply/reply/provider-request-error-classifier.test.ts @@ -1,15 +1,20 @@ /** Tests provider request error classification for retry/fallback decisions. */ import { describe, expect, it } from "vitest"; +import { AUTH_INVALID_TOKEN_USER_TEXT } from "../../agents/embedded-agent-helpers/errors.js"; import { FailoverError } from "../../agents/failover-error.js"; import { classifyProviderRequestError, - PROVIDER_AUTHENTICATION_ERROR_USER_MESSAGE, PROVIDER_CONVERSATION_STATE_ERROR_USER_MESSAGE, - PROVIDER_INTERNAL_ERROR_USER_MESSAGE, - PROVIDER_MODEL_UNAVAILABLE_USER_MESSAGE, - PROVIDER_RATE_LIMIT_OR_QUOTA_ERROR_USER_MESSAGE, } from "./provider-request-error-classifier.js"; +const PROVIDER_AUTHENTICATION_ERROR_USER_MESSAGE = `⚠️ ${AUTH_INVALID_TOKEN_USER_TEXT}`; +const PROVIDER_RATE_LIMIT_OR_QUOTA_ERROR_USER_MESSAGE = + "⚠️ The model provider returned HTTP 429 before replying. This can mean rate limiting, exhausted quota, or an account balance/billing issue. Check the selected provider/model, API key, and provider billing/quota dashboard, then try again."; +const PROVIDER_INTERNAL_ERROR_USER_MESSAGE = + "⚠️ The model provider returned a temporary internal error before replying. Try again in a moment, or switch to another model if it keeps happening."; +const PROVIDER_MODEL_UNAVAILABLE_USER_MESSAGE = + "⚠️ The configured model is unavailable from the provider — it may have been renamed, retired, or is not offered on this account. This needs a config update (agents.defaults.model); retrying or starting a new session won't fix it."; + describe("provider request error classifier", () => { it("classifies provider HTTP 401 authentication failures", () => { const message = diff --git a/src/auto-reply/reply/provider-request-error-classifier.ts b/src/auto-reply/reply/provider-request-error-classifier.ts index 9b7656086163..cf89061e6382 100644 --- a/src/auto-reply/reply/provider-request-error-classifier.ts +++ b/src/auto-reply/reply/provider-request-error-classifier.ts @@ -26,20 +26,20 @@ type ProviderRequestErrorClassification = { export const PROVIDER_CONVERSATION_STATE_ERROR_USER_MESSAGE = "⚠️ The model provider rejected the conversation state. Please try again, or use /new to start a fresh session."; -export const PROVIDER_RATE_LIMIT_OR_QUOTA_ERROR_USER_MESSAGE = +const PROVIDER_RATE_LIMIT_OR_QUOTA_ERROR_USER_MESSAGE = "⚠️ The model provider returned HTTP 429 before replying. This can mean rate limiting, exhausted quota, or an account balance/billing issue. Check the selected provider/model, API key, and provider billing/quota dashboard, then try again."; -export const PROVIDER_INTERNAL_ERROR_USER_MESSAGE = +const PROVIDER_INTERNAL_ERROR_USER_MESSAGE = "⚠️ The model provider returned a temporary internal error before replying. Try again in a moment, or switch to another model if it keeps happening."; -export const PROVIDER_AUTHENTICATION_ERROR_USER_MESSAGE = `⚠️ ${AUTH_INVALID_TOKEN_USER_TEXT}`; +const PROVIDER_AUTHENTICATION_ERROR_USER_MESSAGE = `⚠️ ${AUTH_INVALID_TOKEN_USER_TEXT}`; /** * User-facing copy for a configured model the provider no longer serves. * Distinct from generic failures because retrying or starting a new session * cannot help: the model id itself must be changed in config. */ -export const PROVIDER_MODEL_UNAVAILABLE_USER_MESSAGE = +const PROVIDER_MODEL_UNAVAILABLE_USER_MESSAGE = "⚠️ The configured model is unavailable from the provider — it may have been renamed, retired, or is not offered on this account. This needs a config update (agents.defaults.model); retrying or starting a new session won't fix it."; /** Classifies provider request failures that are actionable for users. */ diff --git a/src/auto-reply/reply/queue.ts b/src/auto-reply/reply/queue.ts index 8f2756af6e99..7b92d38d2dc4 100644 --- a/src/auto-reply/reply/queue.ts +++ b/src/auto-reply/reply/queue.ts @@ -3,11 +3,7 @@ export { clearSessionQueues } from "./queue/cleanup.js"; export type { ClearSessionQueueResult } from "./queue/cleanup.js"; export { scheduleFollowupDrain } from "./queue/drain.js"; -export { - enqueueFollowupRun, - getFollowupQueueDepth, - resetRecentQueuedMessageIdDedupe, -} from "./queue/enqueue.js"; +export { enqueueFollowupRun, getFollowupQueueDepth } from "./queue/enqueue.js"; export { resolveQueueSettings } from "./queue/settings-runtime.js"; export { refreshQueuedFollowupSession } from "./queue/state.js"; export type { FollowupRun, QueueSettings } from "./queue/types.js"; diff --git a/src/auto-reply/reply/reply-operation-run-state.ts b/src/auto-reply/reply/reply-operation-run-state.ts index ff51d41275fc..e91e88e6fc6a 100644 --- a/src/auto-reply/reply/reply-operation-run-state.ts +++ b/src/auto-reply/reply/reply-operation-run-state.ts @@ -10,7 +10,7 @@ export type ReplyOperationRunState = { // heartbeat cleanup never infers it from whichever operation is active later. export const REPLY_OPERATION_RUN_STATE = Symbol("openclaw.replyOperationRunState"); -export type ReplyOptionsWithOperationRunState = { +type ReplyOptionsWithOperationRunState = { [REPLY_OPERATION_RUN_STATE]?: ReplyOperationRunState; }; diff --git a/src/auto-reply/reply/session-fork.ts b/src/auto-reply/reply/session-fork.ts index af78a3c36900..77f3875a4fd2 100644 --- a/src/auto-reply/reply/session-fork.ts +++ b/src/auto-reply/reply/session-fork.ts @@ -47,7 +47,7 @@ type ForkSessionFromParentParams = { type ForkedParentSessionEntry = ParentForkedSessionTranscript; -export type ForkSessionEntryFromParentResult = +type ForkSessionEntryFromParentResult = | { status: "forked"; fork: ForkedParentSessionEntry; @@ -66,7 +66,7 @@ export type ForkSessionEntryFromParentResult = | { status: "missing-parent" } | { status: "failed" }; -export type ForkSessionEntryFromParentParams = Omit & { +type ForkSessionEntryFromParentParams = Omit & { parentSessionKey: string; parentStoreKeys?: readonly string[]; sessionKey: string; diff --git a/src/auto-reply/reply/stranded-reply-recovery.test.ts b/src/auto-reply/reply/stranded-reply-recovery.test.ts index 254d9a7492da..5b53705d8e71 100644 --- a/src/auto-reply/reply/stranded-reply-recovery.test.ts +++ b/src/auto-reply/reply/stranded-reply-recovery.test.ts @@ -1,11 +1,10 @@ import { describe, expect, it, vi } from "vitest"; import { completeFollowupRunLifecycle, markFollowupRunEnqueued } from "./queue/types.js"; -import { - buildStrandedReplyRetryFollowupRun, - STRANDED_REPLY_RETRY_MARKER, -} from "./stranded-reply-recovery.js"; +import { buildStrandedReplyRetryFollowupRun } from "./stranded-reply-recovery.js"; import { createMockFollowupRun } from "./test-helpers.js"; +const STRANDED_REPLY_RETRY_MARKER = "stranded-reply-retry"; + describe("buildStrandedReplyRetryFollowupRun lifecycle ownership", () => { it("does not share the client turn's queuedLifecycle with the system retry", () => { const onComplete = vi.fn(); diff --git a/src/auto-reply/reply/stranded-reply-recovery.ts b/src/auto-reply/reply/stranded-reply-recovery.ts index 86d0f8551b35..5c637ed465e6 100644 --- a/src/auto-reply/reply/stranded-reply-recovery.ts +++ b/src/auto-reply/reply/stranded-reply-recovery.ts @@ -3,7 +3,7 @@ import { markReplyPayloadForSourceSuppressionDelivery } from "../reply-payload.j import type { ReplyPayload } from "../types.js"; import type { FollowupRun } from "./queue/types.js"; -export const STRANDED_REPLY_RETRY_MARKER = "stranded-reply-retry"; +const STRANDED_REPLY_RETRY_MARKER = "stranded-reply-retry"; const STRANDED_REPLY_DELIVERY_FAILURE_TEXT = "I generated a reply but could not deliver it to this chat. Please try again."; diff --git a/src/gateway/server-methods/chat.directive-tags.test.ts b/src/gateway/server-methods/chat.directive-tags.test.ts index 74b483100cb0..0f090db31021 100644 --- a/src/gateway/server-methods/chat.directive-tags.test.ts +++ b/src/gateway/server-methods/chat.directive-tags.test.ts @@ -15,10 +15,7 @@ import { import { ErrorCodes } from "../../../packages/gateway-protocol/src/index.js"; import { CHAT_SEND_SESSION_KEY_MAX_LENGTH } from "../../../packages/gateway-protocol/src/schema.js"; import type { ModelCatalogEntry } from "../../agents/model-catalog.types.js"; -import { - buildPairingQrReplyChannelData, - setReplyPayloadMetadata, -} from "../../auto-reply/reply-payload.js"; +import { setReplyPayloadMetadata } from "../../auto-reply/reply-payload.js"; import type { MsgContext } from "../../auto-reply/templating.js"; import { appendTranscriptMessage, @@ -2651,10 +2648,12 @@ describe("chat directive tag stripping for non-streaming final payloads", () => kind: "final", payload: { text: "Scan this QR code with the OpenClaw iOS app:", - channelData: buildPairingQrReplyChannelData({ - setupCode, - expiresAtMs: Date.now() + 10 * 60_000, - }), + channelData: { + openclawPairingQr: { + setupCode, + expiresAtMs: Date.now() + 10 * 60_000, + }, + }, sensitiveMedia: true, }, }, diff --git a/src/talk/agent-consult-runtime.test.ts b/src/talk/agent-consult-runtime.test.ts index 97dd73850455..a78f79adc434 100644 --- a/src/talk/agent-consult-runtime.test.ts +++ b/src/talk/agent-consult-runtime.test.ts @@ -1,9 +1,5 @@ import { afterEach, describe, expect, it, vi } from "vitest"; import type { RunEmbeddedAgentParams } from "../agents/embedded-agent-runner/run/params.js"; -import type { - ForkSessionEntryFromParentParams, - ForkSessionEntryFromParentResult, -} from "../auto-reply/reply/session-fork.js"; import type { SessionEntry } from "../config/sessions/types.js"; import { MODEL_SELECTION_LOCKED_MESSAGE } from "../sessions/model-overrides.js"; import { runExclusiveSessionLifecycleMutation } from "../sessions/session-lifecycle-admission.js"; @@ -17,6 +13,11 @@ import { resolveRealtimeVoiceAgentConsultToolsAllow, } from "./agent-consult-tool.js"; +type ForkSessionEntryFromParent = + typeof import("../auto-reply/reply/session-fork.js").forkSessionEntryFromParent; +type ForkSessionEntryFromParentParams = Parameters[0]; +type ForkSessionEntryFromParentResult = Awaited>; + function createAgentRuntime(payloads: unknown[] = [{ text: "Speak this." }]) { const sessionStore: Record< string,