refactor(messages): extract system factories (6 of 8) (#1903)

* refactor(messages): extract system factories

* test(messages): cover system factory extraction
This commit is contained in:
JATMN
2026-07-09 09:15:48 +08:00
committed by GitHub
parent a154f711da
commit 6602076b53
4 changed files with 587 additions and 322 deletions
+6
View File
@@ -45,6 +45,9 @@ const _realDiskOutputModule = await import(
const _realMessagesModule = await import(
`../../utils/messages.js?real=${Date.now()}-${Math.random()}`
)
const _realSystemFactoriesModule = await import(
`../../utils/messages/systemFactories.js?real=${Date.now()}-${Math.random()}`
)
const _realSlowOperationsModule = await import(
`../../utils/slowOperations.js?real=${Date.now()}-${Math.random()}`
)
@@ -663,6 +666,9 @@ afterAll(async () => {
MAX_TASK_OUTPUT_BYTES_DISPLAY: _realDiskOutputModule.MAX_TASK_OUTPUT_BYTES_DISPLAY,
}))
mock.module('../../utils/messages.js', () => ({ ..._realMessagesModule }))
mock.module('../../utils/messages/systemFactories.js', () => ({
..._realSystemFactoriesModule,
}))
mock.module('../../utils/slowOperations.js', () => ({
..._realSlowOperationsModule,
}))
+19 -322
View File
@@ -4246,328 +4246,25 @@ function createToolUseMessage(
})
}
export function createSystemMessage(
content: string,
level: SystemMessageLevel,
toolUseID?: string,
preventContinuation?: boolean,
): SystemInformationalMessage {
return {
type: 'system',
subtype: 'informational',
content,
isMeta: false,
timestamp: new Date().toISOString(),
uuid: randomUUID(),
toolUseID,
level,
...(preventContinuation && { preventContinuation }),
}
}
export function createPermissionRetryMessage(
commands: string[],
): SystemPermissionRetryMessage {
return {
type: 'system',
subtype: 'permission_retry',
content: `Allowed ${commands.join(', ')}`,
commands,
level: 'info',
isMeta: false,
timestamp: new Date().toISOString(),
uuid: randomUUID(),
}
}
export function createBridgeStatusMessage(
url: string,
upgradeNudge?: string,
): SystemBridgeStatusMessage {
return {
type: 'system',
subtype: 'bridge_status',
content: `/remote-control is active. Code in CLI or at ${url}`,
url,
upgradeNudge,
isMeta: false,
timestamp: new Date().toISOString(),
uuid: randomUUID(),
}
}
export function createScheduledTaskFireMessage(
content: string,
): SystemScheduledTaskFireMessage {
return {
type: 'system',
subtype: 'scheduled_task_fire',
content,
isMeta: false,
timestamp: new Date().toISOString(),
uuid: randomUUID(),
}
}
export function createStopHookSummaryMessage(
hookCount: number,
hookInfos: StopHookInfo[],
hookErrors: string[],
preventedContinuation: boolean,
stopReason: string | undefined,
hasOutput: boolean,
level: SystemMessageLevel,
toolUseID?: string,
hookLabel?: string,
totalDurationMs?: number,
): SystemStopHookSummaryMessage {
return {
type: 'system',
subtype: 'stop_hook_summary',
hookCount,
hookInfos,
hookErrors,
preventedContinuation,
stopReason,
hasOutput,
level,
timestamp: new Date().toISOString(),
uuid: randomUUID(),
toolUseID,
hookLabel,
totalDurationMs,
}
}
export function createTurnDurationMessage(
durationMs: number,
budget?: { tokens: number; limit: number; nudges: number },
messageCount?: number,
): SystemTurnDurationMessage {
return {
type: 'system',
subtype: 'turn_duration',
durationMs,
budgetTokens: budget?.tokens,
budgetLimit: budget?.limit,
budgetNudges: budget?.nudges,
messageCount,
timestamp: new Date().toISOString(),
uuid: randomUUID(),
isMeta: false,
}
}
export function createAwaySummaryMessage(
content: string,
): SystemAwaySummaryMessage {
return {
type: 'system',
subtype: 'away_summary',
content,
timestamp: new Date().toISOString(),
uuid: randomUUID(),
isMeta: false,
}
}
export function createMemorySavedMessage(
writtenPaths: string[],
): SystemMemorySavedMessage {
return {
type: 'system',
subtype: 'memory_saved',
writtenPaths,
timestamp: new Date().toISOString(),
uuid: randomUUID(),
isMeta: false,
}
}
export function createAgentsKilledMessage(): SystemAgentsKilledMessage {
return {
type: 'system',
subtype: 'agents_killed',
timestamp: new Date().toISOString(),
uuid: randomUUID(),
isMeta: false,
}
}
export function createApiMetricsMessage(metrics: {
ttftMs: number
otps: number
isP50?: boolean
hookDurationMs?: number
turnDurationMs?: number
toolDurationMs?: number
classifierDurationMs?: number
toolCount?: number
hookCount?: number
classifierCount?: number
configWriteCount?: number
}): SystemApiMetricsMessage {
return {
type: 'system',
subtype: 'api_metrics',
ttftMs: metrics.ttftMs,
otps: metrics.otps,
isP50: metrics.isP50,
hookDurationMs: metrics.hookDurationMs,
turnDurationMs: metrics.turnDurationMs,
toolDurationMs: metrics.toolDurationMs,
classifierDurationMs: metrics.classifierDurationMs,
toolCount: metrics.toolCount,
hookCount: metrics.hookCount,
classifierCount: metrics.classifierCount,
configWriteCount: metrics.configWriteCount,
timestamp: new Date().toISOString(),
uuid: randomUUID(),
isMeta: false,
}
}
export function createCommandInputMessage(
content: string,
): SystemLocalCommandMessage {
return {
type: 'system',
subtype: 'local_command',
content,
level: 'info',
timestamp: new Date().toISOString(),
uuid: randomUUID(),
isMeta: false,
}
}
export function createCompactBoundaryMessage(
trigger: 'manual' | 'auto',
preTokens: number,
lastPreCompactMessageUuid?: UUID,
userContext?: string,
messagesSummarized?: number,
): SystemCompactBoundaryMessage {
return {
type: 'system',
subtype: 'compact_boundary',
content: `Conversation compacted`,
isMeta: false,
timestamp: new Date().toISOString(),
uuid: randomUUID(),
level: 'info',
compactMetadata: {
trigger,
preTokens,
userContext,
messagesSummarized,
},
...(lastPreCompactMessageUuid && {
logicalParentUuid: lastPreCompactMessageUuid,
}),
}
}
export function createMicrocompactBoundaryMessage(
trigger: 'auto',
preTokens: number,
tokensSaved: number,
compactedToolIds: string[],
clearedAttachmentUUIDs: string[],
): SystemMicrocompactBoundaryMessage {
logForDebugging(
`[microcompact] saved ~${formatTokens(tokensSaved)} tokens (cleared ${compactedToolIds.length} tool results)`,
)
return {
type: 'system',
subtype: 'microcompact_boundary',
content: 'Context microcompacted',
isMeta: false,
timestamp: new Date().toISOString(),
uuid: randomUUID(),
level: 'info',
microcompactMetadata: {
trigger,
preTokens,
tokensSaved,
compactedToolIds,
clearedAttachmentUUIDs,
},
}
}
export function createSystemAPIErrorMessage(
error: APIError,
retryInMs: number,
retryAttempt: number,
maxRetries: number,
): SystemAPIErrorMessage {
return {
type: 'system',
subtype: 'api_error',
level: 'error',
cause: error.cause instanceof Error ? error.cause : undefined,
error,
retryInMs,
retryAttempt,
maxRetries,
timestamp: new Date().toISOString(),
uuid: randomUUID(),
}
}
/**
* Checks if a message is a compact boundary marker
*/
export function isCompactBoundaryMessage(
message: Message | NormalizedMessage,
): message is SystemCompactBoundaryMessage {
return message?.type === 'system' && message.subtype === 'compact_boundary'
}
/**
* Finds the index of the last compact boundary marker in the messages array
* @returns The index of the last compact boundary, or -1 if none found
*/
export function findLastCompactBoundaryIndex<
T extends Message | NormalizedMessage,
>(messages: T[]): number {
// Scan backwards to find the most recent compact boundary
for (let i = messages.length - 1; i >= 0; i--) {
const message = messages[i]
if (message && isCompactBoundaryMessage(message)) {
return i
}
}
return -1 // No boundary found
}
/**
* Returns messages from the last compact boundary onward (including the boundary).
* If no boundary exists, returns all messages.
*
* Also filters snipped messages by default (when HISTORY_SNIP is enabled) —
* the REPL keeps full history for UI scrollback, so model-facing paths need
* both compact-slice AND snip-filter applied. Pass `{ includeSnipped: true }`
* to opt out (e.g., REPL.tsx fullscreen compact handler which preserves
* snipped messages in scrollback).
*
* Note: The boundary itself is a system message and will be filtered by normalizeMessagesForAPI.
*/
export function getMessagesAfterCompactBoundary<
T extends Message | NormalizedMessage,
>(messages: T[], options?: { includeSnipped?: boolean }): T[] {
const boundaryIndex = findLastCompactBoundaryIndex(messages)
const sliced = boundaryIndex === -1 ? messages : messages.slice(boundaryIndex)
if (!options?.includeSnipped && feature('HISTORY_SNIP')) {
/* eslint-disable @typescript-eslint/no-require-imports */
const { projectSnippedView } =
require('../services/compact/snipProjection.js') as typeof import('../services/compact/snipProjection.js')
/* eslint-enable @typescript-eslint/no-require-imports */
return projectSnippedView(sliced as Message[]) as T[]
}
return sliced
}
export {
createAgentsKilledMessage,
createApiMetricsMessage,
createAwaySummaryMessage,
createBridgeStatusMessage,
createCommandInputMessage,
createCompactBoundaryMessage,
createMemorySavedMessage,
createMicrocompactBoundaryMessage,
createPermissionRetryMessage,
createScheduledTaskFireMessage,
createStopHookSummaryMessage,
createSystemAPIErrorMessage,
createSystemMessage,
createTurnDurationMessage,
findLastCompactBoundaryIndex,
getMessagesAfterCompactBoundary,
isCompactBoundaryMessage,
} from "./messages/systemFactories.js"
export function shouldShowUserMessage(
message: NormalizedMessage,
+233
View File
@@ -0,0 +1,233 @@
import { feature } from 'bun:bundle'
import { expect, test } from 'bun:test'
import {
createAgentsKilledMessage,
createApiMetricsMessage,
createAwaySummaryMessage,
createBridgeStatusMessage,
createCommandInputMessage,
createCompactBoundaryMessage,
createMemorySavedMessage,
createMicrocompactBoundaryMessage,
createPermissionRetryMessage,
createScheduledTaskFireMessage,
createStopHookSummaryMessage,
createSystemAPIErrorMessage,
createSystemMessage,
createTurnDurationMessage,
findLastCompactBoundaryIndex,
getMessagesAfterCompactBoundary,
isCompactBoundaryMessage,
} from './systemFactories.js'
import type { Message } from '../../types/message.js'
test('createSystemMessage builds an informational system message', () => {
const message = createSystemMessage('ready', 'info', 'toolu_1', true)
expect(message).toMatchObject({
type: 'system',
subtype: 'informational',
content: 'ready',
level: 'info',
toolUseID: 'toolu_1',
preventContinuation: true,
})
})
test('system factory helpers build their expected message shapes', () => {
expect(createPermissionRetryMessage(['Bash(ls)', 'Read(*)'])).toMatchObject({
type: 'system',
subtype: 'permission_retry',
content: 'Allowed Bash(ls), Read(*)',
commands: ['Bash(ls)', 'Read(*)'],
level: 'info',
isMeta: false,
})
expect(createBridgeStatusMessage('https://remote.test', 'upgrade')).toMatchObject({
type: 'system',
subtype: 'bridge_status',
content: '/remote-control is active. Code in CLI or at https://remote.test',
url: 'https://remote.test',
upgradeNudge: 'upgrade',
})
expect(createScheduledTaskFireMessage('run task')).toMatchObject({
type: 'system',
subtype: 'scheduled_task_fire',
content: 'run task',
isMeta: false,
})
expect(
createStopHookSummaryMessage(
2,
[{ hookName: 'stop', command: 'echo ok', matcher: undefined }],
['failed'],
true,
'stop_sequence',
true,
'warning',
'toolu_stop',
'Stop',
123,
),
).toMatchObject({
type: 'system',
subtype: 'stop_hook_summary',
hookCount: 2,
hookErrors: ['failed'],
preventedContinuation: true,
stopReason: 'stop_sequence',
hasOutput: true,
level: 'warning',
toolUseID: 'toolu_stop',
hookLabel: 'Stop',
totalDurationMs: 123,
})
expect(
createTurnDurationMessage(
250,
{ tokens: 10, limit: 100, nudges: 1 },
4,
),
).toMatchObject({
type: 'system',
subtype: 'turn_duration',
durationMs: 250,
budgetTokens: 10,
budgetLimit: 100,
budgetNudges: 1,
messageCount: 4,
isMeta: false,
})
expect(createAwaySummaryMessage('away')).toMatchObject({
type: 'system',
subtype: 'away_summary',
content: 'away',
isMeta: false,
})
expect(createMemorySavedMessage(['/tmp/memory.md'])).toMatchObject({
type: 'system',
subtype: 'memory_saved',
writtenPaths: ['/tmp/memory.md'],
isMeta: false,
})
expect(createAgentsKilledMessage()).toMatchObject({
type: 'system',
subtype: 'agents_killed',
isMeta: false,
})
expect(
createApiMetricsMessage({
ttftMs: 12,
otps: 34,
isP50: true,
hookDurationMs: 5,
turnDurationMs: 6,
toolDurationMs: 7,
classifierDurationMs: 8,
toolCount: 2,
hookCount: 3,
classifierCount: 1,
configWriteCount: 4,
}),
).toMatchObject({
type: 'system',
subtype: 'api_metrics',
ttftMs: 12,
otps: 34,
isP50: true,
hookDurationMs: 5,
turnDurationMs: 6,
toolDurationMs: 7,
classifierDurationMs: 8,
toolCount: 2,
hookCount: 3,
classifierCount: 1,
configWriteCount: 4,
isMeta: false,
})
expect(createCommandInputMessage('<command-name>test</command-name>')).toMatchObject({
type: 'system',
subtype: 'local_command',
content: '<command-name>test</command-name>',
level: 'info',
isMeta: false,
})
})
test('microcompact and API error factories preserve branch-specific fields', () => {
const microcompact = createMicrocompactBoundaryMessage(
'auto',
1000,
250,
['toolu_1'],
['attachment_1'],
)
expect(microcompact).toMatchObject({
type: 'system',
subtype: 'microcompact_boundary',
content: 'Context microcompacted',
level: 'info',
isMeta: false,
microcompactMetadata: {
trigger: 'auto',
preTokens: 1000,
tokensSaved: 250,
compactedToolIds: ['toolu_1'],
clearedAttachmentUUIDs: ['attachment_1'],
},
})
const cause = new Error('network down')
const errorWithCause = { message: 'api failed', cause } as any
expect(createSystemAPIErrorMessage(errorWithCause, 100, 2, 5)).toMatchObject({
type: 'system',
subtype: 'api_error',
level: 'error',
error: errorWithCause,
cause,
retryInMs: 100,
retryAttempt: 2,
maxRetries: 5,
})
const errorWithoutErrorCause = { message: 'api failed', cause: 'string cause' } as any
expect(
createSystemAPIErrorMessage(errorWithoutErrorCause, 200, 1, 3).cause,
).toBeUndefined()
})
test('compact boundary helpers find and slice from the latest boundary', () => {
const first = createSystemMessage('old', 'info')
const boundary = createCompactBoundaryMessage('manual', 100)
const last = createSystemMessage('new', 'info')
expect(isCompactBoundaryMessage(boundary)).toBe(true)
expect(findLastCompactBoundaryIndex([first, boundary, last])).toBe(1)
expect(getMessagesAfterCompactBoundary([first, boundary, last])).toEqual([
boundary,
last,
])
})
test('compact boundary slicing also applies snip projection when enabled', () => {
const removed = createSystemMessage('removed', 'info')
const kept = createSystemMessage('kept', 'info')
const snipBoundary = {
...createSystemMessage('snipped', 'info'),
subtype: 'snip_boundary',
snipMetadata: { removedUuids: [removed.uuid] },
} as Message
const messages = [removed, snipBoundary, kept]
const result = getMessagesAfterCompactBoundary(messages)
if (feature('HISTORY_SNIP')) {
expect(result).toEqual([snipBoundary, kept])
} else {
expect(result).toEqual(messages)
}
expect(getMessagesAfterCompactBoundary(messages, { includeSnipped: true })).toEqual(
messages,
)
})
+329
View File
@@ -0,0 +1,329 @@
import { feature } from "bun:bundle"
import type { APIError } from "@anthropic-ai/sdk"
import { randomUUID, type UUID } from "crypto"
import type { Message, NormalizedMessage, StopHookInfo, SystemAgentsKilledMessage, SystemAPIErrorMessage, SystemApiMetricsMessage, SystemAwaySummaryMessage, SystemBridgeStatusMessage, SystemCompactBoundaryMessage, SystemInformationalMessage, SystemLocalCommandMessage, SystemMemorySavedMessage, SystemMicrocompactBoundaryMessage, SystemPermissionRetryMessage, SystemScheduledTaskFireMessage, SystemStopHookSummaryMessage, SystemTurnDurationMessage, SystemMessageLevel } from "../../types/message.js"
import { formatTokens } from "../format.js"
import { logForDebugging } from "../debug.js"
export function createSystemMessage(
content: string,
level: SystemMessageLevel,
toolUseID?: string,
preventContinuation?: boolean,
): SystemInformationalMessage {
return {
type: 'system',
subtype: 'informational',
content,
isMeta: false,
timestamp: new Date().toISOString(),
uuid: randomUUID(),
toolUseID,
level,
...(preventContinuation && { preventContinuation }),
}
}
export function createPermissionRetryMessage(
commands: string[],
): SystemPermissionRetryMessage {
return {
type: 'system',
subtype: 'permission_retry',
content: `Allowed ${commands.join(', ')}`,
commands,
level: 'info',
isMeta: false,
timestamp: new Date().toISOString(),
uuid: randomUUID(),
}
}
export function createBridgeStatusMessage(
url: string,
upgradeNudge?: string,
): SystemBridgeStatusMessage {
return {
type: 'system',
subtype: 'bridge_status',
content: `/remote-control is active. Code in CLI or at ${url}`,
url,
upgradeNudge,
isMeta: false,
timestamp: new Date().toISOString(),
uuid: randomUUID(),
}
}
export function createScheduledTaskFireMessage(
content: string,
): SystemScheduledTaskFireMessage {
return {
type: 'system',
subtype: 'scheduled_task_fire',
content,
isMeta: false,
timestamp: new Date().toISOString(),
uuid: randomUUID(),
}
}
export function createStopHookSummaryMessage(
hookCount: number,
hookInfos: StopHookInfo[],
hookErrors: string[],
preventedContinuation: boolean,
stopReason: string | undefined,
hasOutput: boolean,
level: SystemMessageLevel,
toolUseID?: string,
hookLabel?: string,
totalDurationMs?: number,
): SystemStopHookSummaryMessage {
return {
type: 'system',
subtype: 'stop_hook_summary',
hookCount,
hookInfos,
hookErrors,
preventedContinuation,
stopReason,
hasOutput,
level,
timestamp: new Date().toISOString(),
uuid: randomUUID(),
toolUseID,
hookLabel,
totalDurationMs,
}
}
export function createTurnDurationMessage(
durationMs: number,
budget?: { tokens: number; limit: number; nudges: number },
messageCount?: number,
): SystemTurnDurationMessage {
return {
type: 'system',
subtype: 'turn_duration',
durationMs,
budgetTokens: budget?.tokens,
budgetLimit: budget?.limit,
budgetNudges: budget?.nudges,
messageCount,
timestamp: new Date().toISOString(),
uuid: randomUUID(),
isMeta: false,
}
}
export function createAwaySummaryMessage(
content: string,
): SystemAwaySummaryMessage {
return {
type: 'system',
subtype: 'away_summary',
content,
timestamp: new Date().toISOString(),
uuid: randomUUID(),
isMeta: false,
}
}
export function createMemorySavedMessage(
writtenPaths: string[],
): SystemMemorySavedMessage {
return {
type: 'system',
subtype: 'memory_saved',
writtenPaths,
timestamp: new Date().toISOString(),
uuid: randomUUID(),
isMeta: false,
}
}
export function createAgentsKilledMessage(): SystemAgentsKilledMessage {
return {
type: 'system',
subtype: 'agents_killed',
timestamp: new Date().toISOString(),
uuid: randomUUID(),
isMeta: false,
}
}
export function createApiMetricsMessage(metrics: {
ttftMs: number
otps: number
isP50?: boolean
hookDurationMs?: number
turnDurationMs?: number
toolDurationMs?: number
classifierDurationMs?: number
toolCount?: number
hookCount?: number
classifierCount?: number
configWriteCount?: number
}): SystemApiMetricsMessage {
return {
type: 'system',
subtype: 'api_metrics',
ttftMs: metrics.ttftMs,
otps: metrics.otps,
isP50: metrics.isP50,
hookDurationMs: metrics.hookDurationMs,
turnDurationMs: metrics.turnDurationMs,
toolDurationMs: metrics.toolDurationMs,
classifierDurationMs: metrics.classifierDurationMs,
toolCount: metrics.toolCount,
hookCount: metrics.hookCount,
classifierCount: metrics.classifierCount,
configWriteCount: metrics.configWriteCount,
timestamp: new Date().toISOString(),
uuid: randomUUID(),
isMeta: false,
}
}
export function createCommandInputMessage(
content: string,
): SystemLocalCommandMessage {
return {
type: 'system',
subtype: 'local_command',
content,
level: 'info',
timestamp: new Date().toISOString(),
uuid: randomUUID(),
isMeta: false,
}
}
export function createCompactBoundaryMessage(
trigger: 'manual' | 'auto',
preTokens: number,
lastPreCompactMessageUuid?: UUID,
userContext?: string,
messagesSummarized?: number,
): SystemCompactBoundaryMessage {
return {
type: 'system',
subtype: 'compact_boundary',
content: `Conversation compacted`,
isMeta: false,
timestamp: new Date().toISOString(),
uuid: randomUUID(),
level: 'info',
compactMetadata: {
trigger,
preTokens,
userContext,
messagesSummarized,
},
...(lastPreCompactMessageUuid && {
logicalParentUuid: lastPreCompactMessageUuid,
}),
}
}
export function createMicrocompactBoundaryMessage(
trigger: 'auto',
preTokens: number,
tokensSaved: number,
compactedToolIds: string[],
clearedAttachmentUUIDs: string[],
): SystemMicrocompactBoundaryMessage {
logForDebugging(
`[microcompact] saved ~${formatTokens(tokensSaved)} tokens (cleared ${compactedToolIds.length} tool results)`,
)
return {
type: 'system',
subtype: 'microcompact_boundary',
content: 'Context microcompacted',
isMeta: false,
timestamp: new Date().toISOString(),
uuid: randomUUID(),
level: 'info',
microcompactMetadata: {
trigger,
preTokens,
tokensSaved,
compactedToolIds,
clearedAttachmentUUIDs,
},
}
}
export function createSystemAPIErrorMessage(
error: APIError,
retryInMs: number,
retryAttempt: number,
maxRetries: number,
): SystemAPIErrorMessage {
return {
type: 'system',
subtype: 'api_error',
level: 'error',
cause: error.cause instanceof Error ? error.cause : undefined,
error,
retryInMs,
retryAttempt,
maxRetries,
timestamp: new Date().toISOString(),
uuid: randomUUID(),
}
}
/**
* Checks if a message is a compact boundary marker
*/
export function isCompactBoundaryMessage(
message: Message | NormalizedMessage,
): message is SystemCompactBoundaryMessage {
return message?.type === 'system' && message.subtype === 'compact_boundary'
}
/**
* Finds the index of the last compact boundary marker in the messages array
* @returns The index of the last compact boundary, or -1 if none found
*/
export function findLastCompactBoundaryIndex<
T extends Message | NormalizedMessage,
>(messages: T[]): number {
// Scan backwards to find the most recent compact boundary
for (let i = messages.length - 1; i >= 0; i--) {
const message = messages[i]
if (message && isCompactBoundaryMessage(message)) {
return i
}
}
return -1 // No boundary found
}
/**
* Returns messages from the last compact boundary onward (including the boundary).
* If no boundary exists, returns all messages.
*
* Also filters snipped messages by default (when HISTORY_SNIP is enabled)
* the REPL keeps full history for UI scrollback, so model-facing paths need
* both compact-slice AND snip-filter applied. Pass `{ includeSnipped: true }`
* to opt out (e.g., REPL.tsx fullscreen compact handler which preserves
* snipped messages in scrollback).
*
* Note: The boundary itself is a system message and will be filtered by normalizeMessagesForAPI.
*/
export function getMessagesAfterCompactBoundary<
T extends Message | NormalizedMessage,
>(messages: T[], options?: { includeSnipped?: boolean }): T[] {
const boundaryIndex = findLastCompactBoundaryIndex(messages)
const sliced = boundaryIndex === -1 ? messages : messages.slice(boundaryIndex)
if (!options?.includeSnipped && feature('HISTORY_SNIP')) {
/* eslint-disable @typescript-eslint/no-require-imports */
const { projectSnippedView } =
require('../../services/compact/snipProjection.js') as typeof import('../../services/compact/snipProjection.js')
/* eslint-enable @typescript-eslint/no-require-imports */
return projectSnippedView(sliced as Message[]) as T[]
}
return sliced
}