From 508791fd153ea35c340f4ed4af2b6df5b4fdef72 Mon Sep 17 00:00:00 2001 From: Tak Hoffman <781889+Takhoffman@users.noreply.github.com> Date: Thu, 5 Feb 2026 22:46:45 -0600 Subject: [PATCH] Agents: avoid duplicate compaction --- ...ded-pi-agent.auth-profile-rotation.test.ts | 1 + .../run.overflow-compaction.test.ts | 26 +- src/agents/pi-embedded-runner/run.ts | 14 +- .../run/attempt.cache-ttl.test.ts | 366 ++++++++++++++++++ src/agents/pi-embedded-runner/run/attempt.ts | 24 +- src/agents/pi-embedded-runner/run/types.ts | 1 + ...i-embedded-subscribe.handlers.lifecycle.ts | 7 +- .../pi-embedded-subscribe.handlers.types.ts | 1 + src/agents/pi-embedded-subscribe.ts | 2 + 9 files changed, 421 insertions(+), 21 deletions(-) create mode 100644 src/agents/pi-embedded-runner/run/attempt.cache-ttl.test.ts diff --git a/src/agents/pi-embedded-runner.run-embedded-pi-agent.auth-profile-rotation.test.ts b/src/agents/pi-embedded-runner.run-embedded-pi-agent.auth-profile-rotation.test.ts index 51cfc40ac8..fa54213849 100644 --- a/src/agents/pi-embedded-runner.run-embedded-pi-agent.auth-profile-rotation.test.ts +++ b/src/agents/pi-embedded-runner.run-embedded-pi-agent.auth-profile-rotation.test.ts @@ -58,6 +58,7 @@ const makeAttempt = (overrides: Partial): EmbeddedRunA messagingToolSentTexts: [], messagingToolSentTargets: [], cloudCodeAssistFormatError: false, + didAutoCompaction: false, ...overrides, }); diff --git a/src/agents/pi-embedded-runner/run.overflow-compaction.test.ts b/src/agents/pi-embedded-runner/run.overflow-compaction.test.ts index c913192a6a..cf7b9fd934 100644 --- a/src/agents/pi-embedded-runner/run.overflow-compaction.test.ts +++ b/src/agents/pi-embedded-runner/run.overflow-compaction.test.ts @@ -171,6 +171,7 @@ function makeAttemptResult( messagingToolSentTexts: [], messagingToolSentTargets: [], cloudCodeAssistFormatError: false, + didAutoCompaction: false, ...overrides, }; } @@ -216,10 +217,12 @@ describe("overflow compaction in run loop", () => { expect(mockedRunEmbeddedAttempt).toHaveBeenCalledTimes(2); expect(log.warn).toHaveBeenCalledWith( expect.stringContaining( - "context overflow detected (attempt 1/3); attempting auto-compaction", + "[openclaw-overflow-compaction] context overflow detected (attempt 1/3); attempting manual compaction", ), ); - expect(log.info).toHaveBeenCalledWith(expect.stringContaining("auto-compaction succeeded")); + expect(log.info).toHaveBeenCalledWith( + expect.stringContaining("[openclaw-overflow-compaction] manual compaction succeeded"), + ); // Should not be an error result expect(result.meta.error).toBeUndefined(); }); @@ -241,7 +244,9 @@ describe("overflow compaction in run loop", () => { expect(mockedRunEmbeddedAttempt).toHaveBeenCalledTimes(1); expect(result.meta.error?.kind).toBe("context_overflow"); expect(result.payloads?.[0]?.isError).toBe(true); - expect(log.warn).toHaveBeenCalledWith(expect.stringContaining("auto-compaction failed")); + expect(log.warn).toHaveBeenCalledWith( + expect.stringContaining("[openclaw-overflow-compaction] manual compaction failed"), + ); }); it("retries compaction up to 3 times before giving up", async () => { @@ -323,4 +328,19 @@ describe("overflow compaction in run loop", () => { expect(mockedRunEmbeddedAttempt).toHaveBeenCalledTimes(1); expect(result.meta.error?.kind).toBe("compaction_failure"); }); + + it("skips manual compaction if auto-compaction already ran", async () => { + const overflowError = new Error("request_too_large: Request size exceeds model context window"); + + mockedRunEmbeddedAttempt.mockResolvedValue( + makeAttemptResult({ promptError: overflowError, didAutoCompaction: true }), + ); + + const result = await runEmbeddedPiAgent(baseParams); + + expect(mockedCompactDirect).not.toHaveBeenCalled(); + expect(mockedRunEmbeddedAttempt).toHaveBeenCalledTimes(1); + expect(result.meta.error?.kind).toBe("context_overflow"); + expect(result.payloads?.[0]?.isError).toBe(true); + }); }); diff --git a/src/agents/pi-embedded-runner/run.ts b/src/agents/pi-embedded-runner/run.ts index c8ca9b5a19..fbc051f61a 100644 --- a/src/agents/pi-embedded-runner/run.ts +++ b/src/agents/pi-embedded-runner/run.ts @@ -399,14 +399,16 @@ export async function runEmbeddedPiAgent( `error=${errorText.slice(0, 200)}`, ); const isCompactionFailure = isCompactionFailureError(errorText); - // Attempt auto-compaction on context overflow (not compaction_failure) + // Attempt manual overflow compaction on context overflow (not compaction_failure). + // If Pi already auto-compacted during this attempt, skip our manual compaction to avoid duplicates. if ( !isCompactionFailure && - overflowCompactionAttempts < MAX_OVERFLOW_COMPACTION_ATTEMPTS + overflowCompactionAttempts < MAX_OVERFLOW_COMPACTION_ATTEMPTS && + !attempt.didAutoCompaction ) { overflowCompactionAttempts++; log.warn( - `context overflow detected (attempt ${overflowCompactionAttempts}/${MAX_OVERFLOW_COMPACTION_ATTEMPTS}); attempting auto-compaction for ${provider}/${modelId}`, + `[openclaw-overflow-compaction] context overflow detected (attempt ${overflowCompactionAttempts}/${MAX_OVERFLOW_COMPACTION_ATTEMPTS}); attempting manual compaction for ${provider}/${modelId}`, ); const compactResult = await compactEmbeddedPiSessionDirect({ sessionId: params.sessionId, @@ -430,11 +432,13 @@ export async function runEmbeddedPiAgent( ownerNumbers: params.ownerNumbers, }); if (compactResult.compacted) { - log.info(`auto-compaction succeeded for ${provider}/${modelId}; retrying prompt`); + log.info( + `[openclaw-overflow-compaction] manual compaction succeeded for ${provider}/${modelId}; retrying prompt`, + ); continue; } log.warn( - `auto-compaction failed for ${provider}/${modelId}: ${compactResult.reason ?? "nothing to compact"}`, + `[openclaw-overflow-compaction] manual compaction failed for ${provider}/${modelId}: ${compactResult.reason ?? "nothing to compact"}`, ); } const kind = isCompactionFailure ? "compaction_failure" : "context_overflow"; diff --git a/src/agents/pi-embedded-runner/run/attempt.cache-ttl.test.ts b/src/agents/pi-embedded-runner/run/attempt.cache-ttl.test.ts new file mode 100644 index 0000000000..43afe84a4b --- /dev/null +++ b/src/agents/pi-embedded-runner/run/attempt.cache-ttl.test.ts @@ -0,0 +1,366 @@ +import type { Api, Model } from "@mariozechner/pi-ai"; +import { beforeEach, describe, expect, it, vi } from "vitest"; + +const appendCacheTtlTimestamp = vi.fn(); +const isCacheTtlEligibleProvider = vi.fn(() => true); + +const waitOrder: string[] = []; +const didAutoCompaction = vi.fn(); +const waitForCompactionRetry = vi.fn(async () => { + waitOrder.push("wait"); +}); + +vi.mock("../cache-ttl.js", () => ({ + appendCacheTtlTimestamp: (...args: unknown[]) => appendCacheTtlTimestamp(...args), + isCacheTtlEligibleProvider: (...args: unknown[]) => isCacheTtlEligibleProvider(...args), +})); + +vi.mock("../../pi-embedded-subscribe.js", () => ({ + subscribeEmbeddedPiSession: () => ({ + assistantTexts: [], + toolMetas: [], + unsubscribe: vi.fn(), + waitForCompactionRetry, + didAutoCompaction: () => didAutoCompaction(), + isCompacting: () => false, + getMessagingToolSentTexts: () => [], + getMessagingToolSentTargets: () => [], + didSendViaMessagingTool: () => false, + getLastToolError: () => undefined, + }), +})); + +vi.mock("@mariozechner/pi-ai", () => ({ + streamSimple: vi.fn(), +})); + +vi.mock("@mariozechner/pi-coding-agent", () => { + const sessionManager = { + getLeafEntry: () => null, + branch: vi.fn(), + resetLeaf: vi.fn(), + buildSessionContext: () => ({ messages: [] }), + appendCustomEntry: vi.fn(), + flushPendingToolResults: vi.fn(), + }; + const session = { + sessionId: "session:test", + agent: { replaceMessages: vi.fn(), streamFn: vi.fn() }, + messages: [], + isStreaming: false, + prompt: vi.fn(async () => {}), + steer: vi.fn(async () => {}), + dispose: vi.fn(), + }; + return { + SessionManager: { open: vi.fn(() => sessionManager) }, + SettingsManager: { create: vi.fn(() => ({})) }, + createAgentSession: vi.fn(async () => ({ session })), + }; +}); + +vi.mock("../../../auto-reply/heartbeat.js", () => ({ + resolveHeartbeatPrompt: vi.fn(() => undefined), +})); + +vi.mock("../../../config/channel-capabilities.js", () => ({ + resolveChannelCapabilities: vi.fn(() => ({ supportsImages: false })), +})); + +vi.mock("../../../infra/machine-name.js", () => ({ + getMachineDisplayName: vi.fn(() => "test-host"), +})); + +vi.mock("../../../media/constants.js", () => ({ + MAX_IMAGE_BYTES: 5_000_000, +})); + +vi.mock("../../../plugins/hook-runner-global.js", () => ({ + getGlobalHookRunner: vi.fn(() => ({ hasHooks: () => false })), +})); + +vi.mock("../../../routing/session-key.js", () => ({ + isSubagentSessionKey: vi.fn(() => false), +})); + +vi.mock("../../../signal/reaction-level.js", () => ({ + resolveSignalReactionLevel: vi.fn(() => "off"), +})); + +vi.mock("../../../telegram/inline-buttons.js", () => ({ + resolveTelegramInlineButtonsScope: vi.fn(() => "off"), +})); + +vi.mock("../../../telegram/reaction-level.js", () => ({ + resolveTelegramReactionLevel: vi.fn(() => "off"), +})); + +vi.mock("../../../tts/tts.js", () => ({ + buildTtsSystemPromptHint: vi.fn(() => undefined), +})); + +vi.mock("../../../utils.js", () => ({ + resolveUserPath: vi.fn((p: string) => p), +})); + +vi.mock("../../../utils/message-channel.js", () => ({ + normalizeMessageChannel: vi.fn((v?: string) => v), +})); + +vi.mock("../../../utils/provider-utils.js", () => ({ + isReasoningTagProvider: vi.fn(() => false), +})); + +vi.mock("../../agent-paths.js", () => ({ + resolveOpenClawAgentDir: vi.fn(() => "/tmp/agent-dir"), +})); + +vi.mock("../../agent-scope.js", () => ({ + resolveSessionAgentIds: vi.fn(() => ({ sessionAgentId: "main", defaultAgentId: "main" })), +})); + +vi.mock("../../anthropic-payload-log.js", () => ({ + createAnthropicPayloadLogger: vi.fn(() => ({ + recordUsage: vi.fn(), + wrapStreamFn: vi.fn((fn: unknown) => fn), + })), +})); + +vi.mock("../../bootstrap-files.js", () => ({ + makeBootstrapWarn: vi.fn(() => ({ warn: vi.fn() })), + resolveBootstrapContextForRun: vi.fn(async () => ({ + bootstrapFiles: [], + contextFiles: [], + })), +})); + +vi.mock("../../cache-trace.js", () => ({ + createCacheTrace: vi.fn(() => ({ + recordStage: vi.fn(), + wrapStreamFn: vi.fn((fn: unknown) => fn), + })), +})); + +vi.mock("../../channel-tools.js", () => ({ + listChannelSupportedActions: vi.fn(() => []), + resolveChannelMessageToolHints: vi.fn(() => undefined), +})); + +vi.mock("../../docs-path.js", () => ({ + resolveOpenClawDocsPath: vi.fn(async () => undefined), +})); + +vi.mock("../../failover-error.js", () => ({ + isTimeoutError: vi.fn(() => false), +})); + +vi.mock("../../model-auth.js", () => ({ + resolveModelAuthMode: vi.fn(() => "api-key"), +})); + +vi.mock("../../model-selection.js", () => ({ + resolveDefaultModelForAgent: vi.fn(() => ({ provider: "anthropic", model: "claude" })), +})); + +vi.mock("../../pi-embedded-helpers.js", () => ({ + isCloudCodeAssistFormatError: vi.fn(() => false), + resolveBootstrapMaxChars: vi.fn(() => 0), + validateAnthropicTurns: vi.fn(() => {}), + validateGeminiTurns: vi.fn(() => {}), +})); + +vi.mock("../../pi-settings.js", () => ({ + ensurePiCompactionReserveTokens: vi.fn(() => {}), + resolveCompactionReserveTokensFloor: vi.fn(() => 0), +})); + +vi.mock("../../pi-tool-definition-adapter.js", () => ({ + toClientToolDefinitions: vi.fn(() => []), +})); + +vi.mock("../../pi-tools.js", () => ({ + createOpenClawCodingTools: vi.fn(() => []), +})); + +vi.mock("../../sandbox.js", () => ({ + resolveSandboxContext: vi.fn(async () => undefined), +})); + +vi.mock("../../sandbox/runtime-status.js", () => ({ + resolveSandboxRuntimeStatus: vi.fn(() => ({ mode: "off", sandboxed: false })), +})); + +vi.mock("../../session-file-repair.js", () => ({ + repairSessionFileIfNeeded: vi.fn(async () => false), +})); + +vi.mock("../../session-tool-result-guard-wrapper.js", () => ({ + guardSessionManager: vi.fn((sm) => sm), +})); + +vi.mock("../../session-write-lock.js", () => ({ + acquireSessionWriteLock: vi.fn(async () => ({ release: vi.fn(async () => {}) })), +})); + +vi.mock("../../skills.js", () => ({ + applySkillEnvOverrides: vi.fn(() => () => {}), + applySkillEnvOverridesFromSnapshot: vi.fn(() => () => {}), + loadWorkspaceSkillEntries: vi.fn(() => []), + resolveSkillsPromptForRun: vi.fn(() => ""), +})); + +vi.mock("../../system-prompt-params.js", () => ({ + buildSystemPromptParams: vi.fn(() => ({ + runtimeInfo: {}, + userTimezone: "UTC", + userTime: "00:00", + userTimeFormat: "24h", + })), +})); + +vi.mock("../../system-prompt-report.js", () => ({ + buildSystemPromptReport: vi.fn(() => ({ systemPrompt: "" })), +})); + +vi.mock("../../transcript-policy.js", () => ({ + resolveTranscriptPolicy: vi.fn(() => ({ allowSyntheticToolResults: false })), +})); + +vi.mock("../../workspace.js", () => ({ + DEFAULT_BOOTSTRAP_FILENAME: "OPENCLAW.md", +})); + +vi.mock("../abort.js", () => ({ + isAbortError: vi.fn(() => false), +})); + +vi.mock("../extensions.js", () => ({ + buildEmbeddedExtensionPaths: vi.fn(() => []), +})); + +vi.mock("../extra-params.js", () => ({ + applyExtraParamsToAgent: vi.fn(() => {}), +})); + +vi.mock("../google.js", () => ({ + logToolSchemasForGoogle: vi.fn(() => {}), + sanitizeSessionHistory: vi.fn((messages) => messages), + sanitizeToolsForGoogle: vi.fn((tools) => tools), +})); + +vi.mock("../history.js", () => ({ + getDmHistoryLimitFromSessionKey: vi.fn(() => undefined), + limitHistoryTurns: vi.fn(() => []), +})); + +vi.mock("../logger.js", () => ({ + log: { debug: vi.fn(), warn: vi.fn(), error: vi.fn() }, +})); + +vi.mock("../model.js", () => ({ + buildModelAliasLines: vi.fn(() => []), +})); + +vi.mock("../runs.js", () => ({ + clearActiveEmbeddedRun: vi.fn(() => {}), + setActiveEmbeddedRun: vi.fn(() => {}), +})); + +vi.mock("../sandbox-info.js", () => ({ + buildEmbeddedSandboxInfo: vi.fn(() => undefined), +})); + +vi.mock("../session-manager-cache.js", () => ({ + prewarmSessionFile: vi.fn(async () => {}), + trackSessionManagerAccess: vi.fn(() => {}), +})); + +vi.mock("../session-manager-init.js", () => ({ + prepareSessionManagerForRun: vi.fn(async () => {}), +})); + +vi.mock("../system-prompt.js", () => ({ + applySystemPromptOverrideToSession: vi.fn(() => {}), + buildEmbeddedSystemPrompt: vi.fn(() => ""), + createSystemPromptOverride: vi.fn((prompt: string) => () => prompt), +})); + +vi.mock("../tool-split.js", () => ({ + splitSdkTools: vi.fn(() => ({ builtInTools: [], customTools: [] })), +})); + +vi.mock("../utils.js", () => ({ + describeUnknownError: vi.fn((err: unknown) => String(err)), + mapThinkingLevel: vi.fn(() => "off"), +})); + +vi.mock("./images.js", () => ({ + detectAndLoadPromptImages: vi.fn(async () => ({ + images: [], + historyImagesByIndex: new Map(), + })), +})); + +import { runEmbeddedAttempt } from "./attempt.js"; + +const model = { + id: "claude-3", + provider: "anthropic", + api: "messages", + input: ["text"], +} as unknown as Model; + +const baseParams = { + sessionId: "session:test", + sessionKey: "main", + sessionFile: "/tmp/session.jsonl", + workspaceDir: "/tmp/workspace", + prompt: "hi", + provider: "anthropic", + modelId: "claude-3", + model, + authStorage: {}, + modelRegistry: {}, + thinkLevel: "off", + timeoutMs: 1000, + runId: "run-1", + config: { + agents: { + defaults: { + contextPruning: { mode: "cache-ttl" }, + }, + }, + }, +}; + +describe("runEmbeddedAttempt cache-ttl timing", () => { + beforeEach(() => { + appendCacheTtlTimestamp.mockClear(); + isCacheTtlEligibleProvider.mockClear(); + didAutoCompaction.mockReset(); + waitForCompactionRetry.mockClear(); + waitOrder.length = 0; + }); + + it("skips cache-ttl append when auto-compaction ran", async () => { + didAutoCompaction.mockReturnValue(true); + + await runEmbeddedAttempt(baseParams); + + expect(waitForCompactionRetry).toHaveBeenCalledTimes(1); + expect(appendCacheTtlTimestamp).not.toHaveBeenCalled(); + }); + + it("appends cache-ttl after compaction retry wait when no auto-compaction ran", async () => { + didAutoCompaction.mockReturnValue(false); + appendCacheTtlTimestamp.mockImplementation(() => { + waitOrder.push("append"); + }); + + await runEmbeddedAttempt(baseParams); + + expect(waitForCompactionRetry).toHaveBeenCalledTimes(1); + expect(appendCacheTtlTimestamp).toHaveBeenCalledTimes(1); + expect(waitOrder).toEqual(["wait", "append"]); + }); +}); diff --git a/src/agents/pi-embedded-runner/run/attempt.ts b/src/agents/pi-embedded-runner/run/attempt.ts index 2e6c702929..638a346780 100644 --- a/src/agents/pi-embedded-runner/run/attempt.ts +++ b/src/agents/pi-embedded-runner/run/attempt.ts @@ -644,6 +644,7 @@ export async function runEmbeddedAttempt( toolMetas, unsubscribe, waitForCompactionRetry, + didAutoCompaction, getMessagingToolSentTexts, getMessagingToolSentTargets, didSendViaMessagingTool, @@ -799,17 +800,6 @@ export async function runEmbeddedAttempt( note: `images: prompt=${imageResult.images.length} history=${imageResult.historyImagesByIndex.size}`, }); - const shouldTrackCacheTtl = - params.config?.agents?.defaults?.contextPruning?.mode === "cache-ttl" && - isCacheTtlEligibleProvider(params.provider, params.modelId); - if (shouldTrackCacheTtl) { - appendCacheTtlTimestamp(sessionManager, { - timestamp: Date.now(), - provider: params.provider, - modelId: params.modelId, - }); - } - // Only pass images option if there are actually images to pass // This avoids potential issues with models that don't expect the images parameter if (imageResult.images.length > 0) { @@ -837,6 +827,17 @@ export async function runEmbeddedAttempt( } } + const shouldTrackCacheTtl = + params.config?.agents?.defaults?.contextPruning?.mode === "cache-ttl" && + isCacheTtlEligibleProvider(params.provider, params.modelId); + if (shouldTrackCacheTtl && !didAutoCompaction()) { + appendCacheTtlTimestamp(sessionManager, { + timestamp: Date.now(), + provider: params.provider, + modelId: params.modelId, + }); + } + messagesSnapshot = activeSession.messages.slice(); sessionIdUsed = activeSession.sessionId; cacheTrace?.recordStage("session:after", { @@ -906,6 +907,7 @@ export async function runEmbeddedAttempt( cloudCodeAssistFormatError: Boolean( lastAssistant?.errorMessage && isCloudCodeAssistFormatError(lastAssistant.errorMessage), ), + didAutoCompaction: didAutoCompaction(), // Client tool call detected (OpenResponses hosted tools) clientToolCall: clientToolCallDetected ?? undefined, }; diff --git a/src/agents/pi-embedded-runner/run/types.ts b/src/agents/pi-embedded-runner/run/types.ts index 181a42c9f9..8efa816539 100644 --- a/src/agents/pi-embedded-runner/run/types.ts +++ b/src/agents/pi-embedded-runner/run/types.ts @@ -106,6 +106,7 @@ export type EmbeddedRunAttemptResult = { messagingToolSentTexts: string[]; messagingToolSentTargets: MessagingToolSend[]; cloudCodeAssistFormatError: boolean; + didAutoCompaction: boolean; /** Client tool call detected (OpenResponses hosted tools). */ clientToolCall?: { name: string; params: Record }; }; diff --git a/src/agents/pi-embedded-subscribe.handlers.lifecycle.ts b/src/agents/pi-embedded-subscribe.handlers.lifecycle.ts index de8c8bd6ae..64042a1a2d 100644 --- a/src/agents/pi-embedded-subscribe.handlers.lifecycle.ts +++ b/src/agents/pi-embedded-subscribe.handlers.lifecycle.ts @@ -20,9 +20,12 @@ export function handleAgentStart(ctx: EmbeddedPiSubscribeContext) { } export function handleAutoCompactionStart(ctx: EmbeddedPiSubscribeContext) { + ctx.state.autoCompactionAttempts += 1; ctx.state.compactionInFlight = true; ctx.ensureCompactionPromise(); - ctx.log.debug(`embedded run compaction start: runId=${ctx.params.runId}`); + ctx.log.debug( + `[pi-auto-compaction] start: runId=${ctx.params.runId} attempt=${ctx.state.autoCompactionAttempts}`, + ); emitAgentEvent({ runId: ctx.params.runId, stream: "compaction", @@ -43,7 +46,7 @@ export function handleAutoCompactionEnd( if (willRetry) { ctx.noteCompactionRetry(); ctx.resetForCompactionRetry(); - ctx.log.debug(`embedded run compaction retry: runId=${ctx.params.runId}`); + ctx.log.debug(`[pi-auto-compaction] retry: runId=${ctx.params.runId}`); } else { ctx.maybeResolveCompactionWait(); } diff --git a/src/agents/pi-embedded-subscribe.handlers.types.ts b/src/agents/pi-embedded-subscribe.handlers.types.ts index db2f07b768..416507549e 100644 --- a/src/agents/pi-embedded-subscribe.handlers.types.ts +++ b/src/agents/pi-embedded-subscribe.handlers.types.ts @@ -54,6 +54,7 @@ export type EmbeddedPiSubscribeState = { pendingCompactionRetry: number; compactionRetryResolve?: () => void; compactionRetryPromise: Promise | null; + autoCompactionAttempts: number; messagingToolSentTexts: string[]; messagingToolSentTextsNormalized: string[]; diff --git a/src/agents/pi-embedded-subscribe.ts b/src/agents/pi-embedded-subscribe.ts index 0a4b9c0fa5..3ac7e84068 100644 --- a/src/agents/pi-embedded-subscribe.ts +++ b/src/agents/pi-embedded-subscribe.ts @@ -63,6 +63,7 @@ export function subscribeEmbeddedPiSession(params: SubscribeEmbeddedPiSessionPar pendingCompactionRetry: 0, compactionRetryResolve: undefined, compactionRetryPromise: null, + autoCompactionAttempts: 0, messagingToolSentTexts: [], messagingToolSentTextsNormalized: [], messagingToolSentTargets: [], @@ -539,6 +540,7 @@ export function subscribeEmbeddedPiSession(params: SubscribeEmbeddedPiSessionPar toolMetas, unsubscribe, isCompacting: () => state.compactionInFlight || state.pendingCompactionRetry > 0, + didAutoCompaction: () => state.autoCompactionAttempts > 0, getMessagingToolSentTexts: () => messagingToolSentTexts.slice(), getMessagingToolSentTargets: () => messagingToolSentTargets.slice(), // Returns true if any messaging tool successfully sent a message.