Files
orca/src/shared/agent-session-conversation-command.ts
T
Brennan Benson bb44f74d00 Keep native chat /clear in the same conversation (#26579)
* Keep clear in the same chat and reset its provider context

* Derive clear pauses only from journal membership and cover older rewind clients

* Point queued command rewind test at the structured hook

* Group queue consume errors and rewind replay assertions
2026-10-08 16:00:29 -07:00

76 lines
2.9 KiB
TypeScript

import type { AgentSessionFailureFact } from './agent-session-failure'
import type { AgentSessionFailureSentence } from './agent-session-failure-words'
import type { AgentSessionQueuedSendReceipt } from './agent-session-wire'
export type AgentSessionConversationCommand = 'clear' | 'compact'
/** How long a client waits for a conversation command: the host may first start an agent at rest. */
export const AGENT_SESSION_CONVERSATION_COMMAND_TIMEOUT_MS = 195_000
export type AgentSessionConversationCommandResult = {
command: AgentSessionConversationCommand
state: 'completed' | 'unknown'
replacementSessionId?: string
/** A /compact the host holds as a card behind work in flight, with `state: 'completed'`: the
* card is its one surface from here. Only clients that sent `delivery` receive it. */
queued?: AgentSessionQueuedSendReceipt
} &
/** `error`: a sentence for a person; released clients print it as it is. */
(
| { error?: string; failure?: undefined }
/** What failed, typed, beside its sentence, both from `agentSessionFailureWords`; `failure` is
* absent from older hosts. */
| { error: AgentSessionFailureSentence; failure: AgentSessionFailureFact }
)
export type AgentSessionConversationCommandRecord = AgentSessionConversationCommandResult & {
runtimeFence?: number
operationId: string
callerKey: string
phase: 'prepared' | 'committed'
}
/** A newer host may answer with a command this build doesn't know. */
export function isAgentSessionConversationCommand(
value: unknown
): value is AgentSessionConversationCommand {
return value === 'clear' || value === 'compact'
}
export function isAgentSessionConversationCommandResult(
value: unknown
): value is AgentSessionConversationCommandResult {
if (!value || typeof value !== 'object') {
return false
}
const row = value as AgentSessionConversationCommandResult
return (
isAgentSessionConversationCommand(row.command) &&
(row.state === 'completed' || row.state === 'unknown') &&
(row.replacementSessionId === undefined ||
(typeof row.replacementSessionId === 'string' &&
/^[A-Za-z0-9_-]{8,128}$/.test(row.replacementSessionId))) &&
(row.error === undefined || (typeof row.error === 'string' && row.error.length <= 4096))
)
}
export function isAgentSessionConversationCommandRecord(
value: unknown
): value is AgentSessionConversationCommandRecord {
if (!isAgentSessionConversationCommandResult(value)) {
return false
}
const row = value as AgentSessionConversationCommandRecord
return (
(row.phase === 'prepared' || row.phase === 'committed') &&
(row.runtimeFence === undefined ||
(Number.isSafeInteger(row.runtimeFence) && row.runtimeFence >= 0)) &&
typeof row.operationId === 'string' &&
row.operationId.length > 0 &&
row.operationId.length <= 512 &&
typeof row.callerKey === 'string' &&
row.callerKey.length > 0 &&
row.callerKey.length <= 512
)
}