mirror of
https://github.com/stablyai/orca.git
synced 2026-10-31 00:57:45 +00:00
* [comment audit] Align legacy failure-read counts with cached display * [comment audit] Condense shared provider lifecycle and browser recovery comments [skip ci] * [comment audit] Condense shared generation, path and preview comments [skip ci] * [comment audit] Condense host, file and Git contract rationale [skip ci] * [comment audit] Tighten Git lifecycle and provider contract comments [skip ci] * [comment audit] Clarify shared host authority and launch isolation [skip ci] * [comment audit] simplify local access mobile and native chat contracts [skip ci] * [comment audit] simplify native chat presentation and state contracts [skip ci] * [comment audit] simplify native runtime and provider contracts [skip ci] * [comment audit] simplify migration and orchestration contracts [skip ci] * [comment audit] simplify plugin trust and process capture rationale [skip ci] * [comment audit] simplify terminal delivery and remote compatibility rationale [skip ci] * [comment audit] clarify remote recovery and launch wire rationale [skip ci] * [comment audit] shorten shared RPC compatibility and runtime evidence comments [skip ci] * [comment audit] simplify secure storage shell startup and sidebar rationale [skip ci] * [comment audit] clarify sidebar, skill and SSH invariants [skip ci] * [comment audit] condense structured chat delivery and timing rationale [skip ci] * [comment audit] condense terminal replay and telemetry invariants [skip ci] * [comment audit] clarify launch permission and terminal delivery rationale [skip ci] * [comment audit] clarify workspace ownership and layout contracts [skip ci] * [comment audit] wait for fitted and painted SSH terminal links * [comment audit] clarify layout persistence and session ownership [skip ci] * [comment audit] clarify host routing and workspace lifecycle contracts [skip ci] * [comment audit] clarify startup persistence and app lifetime comments [skip ci] * [comment audit] clarify recovery and activity boundaries [skip ci] * [comment audit] condense activity and artifact invariants [skip ci] * [comment audit] condense automation authority contracts [skip ci] * [comment audit] simplify automation scheduling and history contracts [skip ci] * [comment audit] clarify browser chrome and annotation lifecycles [skip ci] * [comment audit] clarify browser routing and retained guest ownership [skip ci] * [comment audit] clarify browser recovery and document ownership comments [skip ci] * [comment audit] restore hosted runners required by native IME fixtures * [comment audit] shorten preview, palette and dashboard invariants [skip ci] * [comment audit] clarify terminal preview, dashboard and retention comments [skip ci] * [comment audit] clarify diff-note recovery and editor lifecycle comments [skip ci] * [comment audit] shorten rich-text and combined-diff ownership comments [skip ci] * [comment audit] shorten autosave, draft recovery and model ownership comments [skip ci] * [comment audit] shorten save-queue, notebook and Markdown constraints [skip ci] * [comment audit] shorten Monaco, PDF and rich Markdown safeguards [skip ci] * [comment audit] clarify rich Markdown source and selection safeguards [skip ci] * [comment audit] clarify editor loading and emulator stream lifetimes [skip ci] * [comment audit] trim emulator and feature tour narration [skip ci] * [comment audit] clarify floating workspace and GitHub cache ownership [skip ci] * [comment audit] trim provider controls and Linear filter explanations [skip ci] * [comment audit] clarify pairing and chat resume lifetimes [skip ci] * [comment audit] clarify chat delivery and transcript lifetimes [skip ci] * [comment audit] clarify session options and host-owned status [skip ci] * [comment audit] clarify draft durability and live-status precedence [skip ci] * [comment audit] Trim chat send and model ownership explanations [skip ci] * [comment audit] Shorten transcript ownership and send recovery comments [skip ci] * [comment audit] Simplify native chat composer and transcript hook comments [skip ci] * [comment audit] Shorten native chat scroll and structured session explanations [skip ci] * [comment audit] Trim workspace picker and onboarding commentary [skip ci] * [comment audit] Simplify pet rendering, review cache and quick open comments [skip ci] * [comment audit] Trim sidebar and session history explanations [skip ci] * [comment audit] Condense session resume and review checks comments [skip ci] * [comment audit] Shorten checks state and file explorer refresh comments [skip ci] * [comment audit] Trim plugin bridge and source-control action rationale [skip ci] * [comment audit] Trim source-control listing and review rationale [skip ci] * [comment audit] Shorten sidebar lifecycle and explorer safeguards [skip ci] * [comment audit] Condense Settings account and appearance rationale [skip ci] * [comment audit] Trim pairing and repository Settings explanations [skip ci] * [comment audit] Condense Settings search SSH and terminal safeguards [skip ci] * [comment audit] Shorten Settings voice and provider ownership comments [skip ci] * [comment audit] Condense Settings draft persistence and host-state rationale [skip ci] * [comment audit] Trim Settings hooks and sidebar setup rationale [skip ci] * [comment audit] Condense sidebar deletion and board-filter safeguards [skip ci] * [comment audit] Condense worktree cards, host ownership and Markdown contracts [skip ci] * [comment audit] Condense sidebar drag, folder and filtering contracts [skip ci] * [comment audit] Condense sidebar parity, sleep and host ownership contracts [skip ci] * [comment audit] Prune focus-read baseline after comment cleanup * [comment audit] Clarify sidebar visibility, board drops and agent evidence [skip ci] * [comment audit] Tighten sidebar listing, drag and reveal contracts [skip ci] * [comment audit] Clarify viewport geometry and skill update safeguards [skip ci] * [comment audit] Tighten skill inventory, usage and host status comments [skip ci] * [comment audit] Clarify usage ownership and resource kill safeguards [skip ci] * [comment audit] Tighten tab selection, rename and search contracts [skip ci] * [comment audit] Tighten tab-strip geometry and drag ownership comments [skip ci] * [comment audit] Tighten Tasks recovery, pagination and mutation comments [skip ci] * [comment audit] Tighten provider loading and terminal recovery comments [skip ci] * [comment audit] Tighten completion, cadence and PTY ownership comments [skip ci] * [comment audit] Clarify terminal restore and parked-pane boundaries [skip ci] * [comment audit] Tighten PTY startup and replay explanations [skip ci] * [comment audit] Clarify PTY identity and delivery ownership [skip ci] * [comment audit] Shorten remote reconnect and input replay rationale [skip ci] * [comment audit] Condense terminal parking clipboard and file link comments [skip ci] * [comment audit] Clarify IME ownership and trim trace history [skip ci] * [comment audit] Shorten pane recovery and parked watcher explanations [skip ci] * [comment audit] Condense snapshot replay and visibility recovery rationale [skip ci] * [comment audit] Shorten terminal parking and keyboard ownership rationale [skip ci] * [comment audit] Trim renderer lifecycle and menu rationale [skip ci] * [comment audit] Condense workspace cleanup ownership and settlement notes [skip ci] * [comment audit] Clarify renderer hook evidence and source ownership [skip ci] * [comment audit] Tighten IPC ownership and reconnect explanations [skip ci] * [comment audit] Trim renderer focus, paste and attention comments [skip ci] * [comment audit] Shorten scroll, locale and agent lifecycle comments [skip ci] * [comment audit] Trim agent launch, resume and ownership commentary [skip ci] * [comment audit] Shorten account, browser and editor comments [skip ci] * [comment audit] Clarify host-mirror waits, previews and input ownership [skip ci] * [comment audit] Condense launch recovery and editor tokenization comments [skip ci] * [comment audit] Condense workspace selection and terminal pane rationale [skip ci] * [comment audit] Condense terminal fitting and rendering recovery comments [skip ci] * [comment audit] Condense terminal input and host routing explanations [skip ci] * [comment audit] Condense ownership and sleeping-session recovery rationale [skip ci] * [comment audit] Condense structured launch and terminal link explanations [skip ci] * [comment audit] Condense terminal ownership and workspace file routing rationale [skip ci] * [comment audit] Condense session persistence and workspace activation rationale [skip ci] * [comment audit] Condense workspace routing and browser ownership rationale [skip ci] * [comment audit] Condense mirror recovery and layout authority rationale [skip ci] * [comment audit] Condense runtime transport and file safety rationale [skip ci] * [comment audit] Condense runtime graph and structured session rationale [skip ci] * [comment audit] Condense browser creation and remote tab recovery rationale [skip ci] * [comment audit] Condense snapshot authority and publisher recovery rationale [skip ci] * [comment audit] Condense frame, startup and provider cache rationale [skip ci] * [comment audit] Condense host twin and agent lifecycle rationale [skip ci] * [comment audit] Condense browser authority and editor cleanup rationale [skip ci] * [comment audit] Condense editor buffer identity and review cache rationale [skip ci] * [comment audit] Condense provider lifetime and repository ownership rationale [skip ci] * [comment audit] Condense reconnect, hydration and terminal retirement rationale [skip ci] * [comment audit] Condense recovery, reconciliation and persistence rationale [skip ci] * [comment audit] Condense cleanup ownership and listing admission rationale [skip ci] * [comment audit] Condense teardown ordering and terminal ownership rationale [skip ci] * [comment audit] Condense recovery ledger and web host-boundary rationale [skip ci] * [comment audit] Condense web reconnect and credential lifecycle rationale [skip ci] * [comment audit] Condense cloud rollout and assignment safety rationale [skip ci] * [comment audit] Condense relay session and database recovery rationale [skip ci] * [comment audit] Condense relay placement and protocol compatibility rationale [skip ci] * [comment audit] Condense build and packaging constraints [skip ci] * [comment audit] Condense CI gates and validation isolation rationale [skip ci] * [comment audit] Condense catalog identity and browser validation rationale [skip ci] * [comment audit] Condense mobile browser and frame-budget rationale [skip ci] * [comment audit] Shorten mobile route and browser test explanations [skip ci] * [comment audit] Condense editor and session seam test rationale [skip ci] * [comment audit] Trim session and terminal closure history [skip ci] * [comment audit] Shorten terminal browser and census explanations [skip ci] * [comment audit] Preserve root-only E2E diagnostic baseline * [comment audit] Condense mobile seam and HTML preview test rationale [skip ci] * [comment audit] Condense preview instrumentation and render harness rationale [skip ci] * [comment audit] Condense bundle contracts and native-build constraints [skip ci] * [comment audit] Shorten native packaging and CI routing rationale [skip ci] * [comment audit] Condense release tooling and benchmark constraints [skip ci] * [comment audit] Condense packaging and platform gate rationale [skip ci] * [comment audit] Shorten mobile route and history rationale [skip ci] * [comment audit] Condense mobile browser and component rationale [skip ci] * [comment audit] Shorten drawer and workspace-form rationale [skip ci] * [comment audit] Trim mobile review and editor-document explanations [skip ci] * [comment audit] Condense editor lifecycle and Markdown explanations [skip ci] * [comment audit] Trim mobile file, diagnostics and dictation commentary [skip ci] * [comment audit] Simplify mobile host-list and workspace-view explanations [skip ci] * [comment audit] Clarify mobile shell lifetime and bridge delivery constraints [skip ci] * [comment audit] Condense bridge stream, native request and audio rationale [skip ci] * [comment audit] Reduce bridge schema and page-client commentary [skip ci] * [comment audit] Tighten mobile page bridge contracts [skip ci] * [comment audit] Clarify mobile cache and session constraints [skip ci] * [comment audit] Condense mobile route and lifecycle rationale [skip ci] * [comment audit] Tighten mobile shell hook timing rationale [skip ci] * [comment audit] Clarify route handoff and notification compatibility [skip ci] * [comment audit] Clarify push registration and dictation cleanup constraints [skip ci] * [comment audit] Condense keyboard geometry and native media ownership rationale [skip ci] * [comment audit] Tighten mobile chat scroll and composer concurrency rationale [skip ci] * [comment audit] Condense mobile reply compatibility and upload ownership rationale [skip ci] * [comment audit] Condense optimistic echo and scroll ownership rationale [skip ci] * [comment audit] Tighten stream replay and queued session ownership rationale [skip ci] * [comment audit] Condense mobile composer and reply-schema constraints [skip ci] * [comment audit] Clarify mobile transcript recovery and session ownership [skip ci] * [comment audit] Clarify mobile queue and source-control compatibility [skip ci] * [comment audit] Condense storage and task launch constraints [skip ci] * [comment audit] Clarify task schemas and terminal document ownership [skip ci] * [comment audit] Tighten terminal input and viewport rationale [skip ci] * [comment audit] Shorten terminal and RPC recorder contracts [skip ci] * [comment audit] Condense recorder engine and connection contracts [skip ci] * [comment audit] Shorten host credential and relay recovery comments [skip ci] * [comment audit] Tighten pairing recovery and bundle transport rationale [skip ci] * [comment audit] Clarify RPC delivery and workspace catalog boundaries [skip ci] * [comment audit] Condense catalog and relay entry contracts [skip ci] * [comment audit] Shorten shutdown and SSH relay delivery contracts [skip ci] * [comment audit] Condense WSL relay and E2E regression rationale [skip ci] * [comment audit] Shorten cross-version harness and renderer fixture explanations [skip ci] * [comment audit] Condense paired-host and E2E lifecycle rationale [skip ci] * [comment audit] Shorten layout oracle and regression explanations [skip ci] * [comment audit] Condense paired recovery and ownership regression contracts [skip ci] * [comment audit] Shorten restart, sidebar and SSH lifecycle contracts [skip ci] * [comment audit] Condense terminal rendering and IME regression rationale [skip ci] * [comment audit] Shorten terminal recovery and benchmark contracts [skip ci] * [comment audit] Condense tooling and Windows lifecycle proof rationale [skip ci] * [comment audit] Condense workflow routing and release rationale [skip ci] * [comment audit] Condense mobile security and installer contracts [skip ci] * [comment audit] Shorten native rationale and stylesheet workarounds [skip ci] * [comment audit] Shorten Windows visibility probe rationale [skip ci] * [comment audit] Condense embedded release and signing rationale [skip ci] * [comment audit] Retain current main test behavior in final reconciliation [skip ci] * [comment audit] Complete final comment review across current source inventory * [comment audit] Handle consolidated audit reports before final CI * [comment audit] Preserve credential storage census detail [skip ci] * [comment audit] Scale broad changed E2E selections with verified timing shards * [comment audit] Align E2E assertions with browser close and settled session metadata * [comment audit] Match existing E2Es to current resource and windowing contracts * [comment audit] Observe the shared server outage notice in reconnect coverage * [comment audit] Make existing renderer probes measure settled tab and PTY work * [comment audit] Align palette E2E selector with task search * [comment audit] Refresh remaining palette search selectors * [comment audit] Keep selector correction within selected audit specs * [comment audit] Await emoji fixture settings inside renderer evaluation * [comment audit] Cancel fixture fit retries between terminal tests --------- Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
263 lines
10 KiB
TypeScript
263 lines
10 KiB
TypeScript
import { agentJournalSubmissionKey } from './agent-session-journal-item-key'
|
||
import {
|
||
agentJournalItemPosition,
|
||
compareAgentJournalItems,
|
||
compareAgentJournalPositions
|
||
} from './agent-session-journal-position'
|
||
import type {
|
||
AgentJournalPosition,
|
||
AgentJournalRenderItem,
|
||
AgentJournalSubmission
|
||
} from './agent-session-journal-types'
|
||
import { inSendOrder } from './native-chat/native-chat-send-order'
|
||
import { stoppedTurnOpeners, structuredAgentTurnAnchors } from './native-chat-turn-membership'
|
||
import type { NativeChatBlock, NativeChatMessage } from './native-chat-types'
|
||
|
||
/** Presentation key for the notice after a send stopped before execution. */
|
||
export const NATIVE_CHAT_STOPPED_BEFORE_START_PRESENTATION = 'stopped-before-start'
|
||
/** Its words where a client has no catalog of its own (the phone). */
|
||
export const NATIVE_CHAT_STOPPED_BEFORE_START_TEXT = 'Stopped manually'
|
||
|
||
/** That row: it outlives any turn it sits in, as the send it follows does. */
|
||
export function isStoppedBeforeStartBlock(block: NativeChatBlock): boolean {
|
||
return (
|
||
block.type === 'text' && block.presentation === NATIVE_CHAT_STOPPED_BEFORE_START_PRESENTATION
|
||
)
|
||
}
|
||
|
||
/** opensTurn identifies its interrupted turn; position overrides where its own row draws. */
|
||
export type StoppedSendPlace = { opensTurn: boolean; position?: AgentJournalPosition }
|
||
|
||
/** Place stopped sends after turns whose openers precede them, using journal order. A send that opened a turn stays its opener,
|
||
* or precedes that turn’s first loaded row when the record is unloaded.
|
||
* Temporary until the remote version floor: hosts without submittedSequence may leave sends at their original place;
|
||
* latestRowsSentBefore also puts them below earlier sends, harmless when newer hosts already moved them. */
|
||
export function placeStoppedSends(
|
||
items: readonly AgentJournalRenderItem[],
|
||
submissions: readonly AgentJournalSubmission[],
|
||
stopped: ReadonlyMap<string, AgentJournalSubmission>
|
||
): (itemId: string) => StoppedSendPlace {
|
||
const anchors = structuredAgentTurnAnchors(items, submissions)
|
||
const turnOpenedBy = new Map([...anchors].map(([turnItemId, anchorId]) => [anchorId, turnItemId]))
|
||
const itemsById = new Map(items.map((item) => [item.itemId, item]))
|
||
const pastTurnsOpenedBefore = turnEndsByOpener(items, anchors, itemsById)
|
||
const unloadedTurnOpened = unloadedTurnsOpened(items, itemsById, stopped)
|
||
const sentBefore = [...stopped.values()].some(
|
||
(submission) => submission.submittedSequence === undefined
|
||
)
|
||
? latestRowsSentBefore(submissions, itemsById)
|
||
: undefined
|
||
return (itemId) => {
|
||
const item = itemsById.get(itemId)
|
||
if (!item) {
|
||
return { opensTurn: false }
|
||
}
|
||
const own = agentJournalItemPosition(item)
|
||
const record = itemsById.get(turnOpenedBy.get(itemId) ?? '')
|
||
const opened = record ? openerPosition(item, record) : unloadedTurnOpened.get(itemId)
|
||
if (opened) {
|
||
return compareAgentJournalPositions(own, opened) !== 0
|
||
? { opensTurn: true, position: opened }
|
||
: { opensTurn: true }
|
||
}
|
||
const floorRow = sentBefore?.get(itemId)
|
||
const floor = floorRow ? agentJournalItemPosition(floorRow) : undefined
|
||
// Just after the floor row, so a turn that row opened counts as waited on.
|
||
const from =
|
||
floor && compareAgentJournalPositions(floor, own) > 0
|
||
? { sequence: floor.sequence, index: floor.index + 0.5 }
|
||
: own
|
||
const position = pastTurnsOpenedBefore(from)
|
||
return compareAgentJournalPositions(position, own) !== 0
|
||
? { opensTurn: false, position }
|
||
: { opensTurn: false }
|
||
}
|
||
}
|
||
|
||
/** Place an opener before its turn record if its own row moved after it during take-back. */
|
||
function openerPosition(
|
||
opener: AgentJournalRenderItem,
|
||
record: AgentJournalRenderItem
|
||
): AgentJournalPosition {
|
||
const own = agentJournalItemPosition(opener)
|
||
const opened = agentJournalItemPosition(record)
|
||
return compareAgentJournalPositions(own, opened) > 0
|
||
? { sequence: opened.sequence, index: opened.index - 0.5 }
|
||
: own
|
||
}
|
||
|
||
/** When a stopped opener’s turn record is unloaded, place it before the first loaded turn row unless that row is already user-owned. */
|
||
function unloadedTurnsOpened(
|
||
items: readonly AgentJournalRenderItem[],
|
||
itemsById: ReadonlyMap<string, AgentJournalRenderItem>,
|
||
stopped: ReadonlyMap<string, AgentJournalSubmission>
|
||
): ReadonlyMap<string, AgentJournalPosition> {
|
||
const openers = new Map(
|
||
[...stoppedTurnOpeners([...stopped.values()])].filter(
|
||
([turnItemId]) => !itemsById.has(turnItemId)
|
||
)
|
||
)
|
||
const first = new Map<string, AgentJournalPosition | null>()
|
||
for (const item of items) {
|
||
const turnItemId = item.turnScope?.kind === 'turn' ? item.turnScope.turnItemId : undefined
|
||
if (turnItemId === undefined || !openers.has(turnItemId) || first.has(turnItemId)) {
|
||
continue
|
||
}
|
||
const userRow = item.body.kind === 'message' && item.body.role === 'user'
|
||
const position = agentJournalItemPosition(item)
|
||
first.set(turnItemId, userRow ? null : { ...position, index: position.index - 0.5 })
|
||
}
|
||
return new Map(
|
||
[...openers].flatMap(([turnItemId, itemId]) => {
|
||
const position = first.get(turnItemId)
|
||
return position ? [[itemId, position] as const] : []
|
||
})
|
||
)
|
||
}
|
||
|
||
/** For each point, advance beyond all turns opened before it. One pass plus a binary search per point; these sends opened no turn themselves. */
|
||
function turnEndsByOpener(
|
||
items: readonly AgentJournalRenderItem[],
|
||
anchors: ReadonlyMap<string, string>,
|
||
itemsById: ReadonlyMap<string, AgentJournalRenderItem>
|
||
): (from: AgentJournalPosition) => AgentJournalPosition {
|
||
const lastOfTurn = new Map<string, AgentJournalPosition>()
|
||
const reach = (turnItemId: string, position: AgentJournalPosition): void => {
|
||
const last = lastOfTurn.get(turnItemId)
|
||
if (!last || compareAgentJournalPositions(position, last) > 0) {
|
||
lastOfTurn.set(turnItemId, position)
|
||
}
|
||
}
|
||
for (const item of items) {
|
||
const position = agentJournalItemPosition(item)
|
||
if (anchors.has(item.itemId)) {
|
||
reach(item.itemId, position)
|
||
}
|
||
if (item.turnScope?.kind === 'turn') {
|
||
reach(item.turnScope.turnItemId, position)
|
||
}
|
||
}
|
||
const turns = [...anchors].flatMap(([turnItemId, anchorId]) => {
|
||
const opener = itemsById.get(anchorId)
|
||
const record = itemsById.get(turnItemId)
|
||
const last = lastOfTurn.get(turnItemId)
|
||
return opener && record && last ? [{ opener: openerPosition(opener, record), last }] : []
|
||
})
|
||
turns.sort((left, right) => compareAgentJournalPositions(left.opener, right.opener))
|
||
// The furthest row of the turns opened up to each one.
|
||
const furthest: AgentJournalPosition[] = []
|
||
for (const turn of turns) {
|
||
const previous = furthest.at(-1)
|
||
furthest.push(
|
||
previous && compareAgentJournalPositions(previous, turn.last) > 0 ? previous : turn.last
|
||
)
|
||
}
|
||
return (from) => {
|
||
let low = 0
|
||
let high = turns.length
|
||
while (low < high) {
|
||
const middle = (low + high) >> 1
|
||
if (compareAgentJournalPositions(turns[middle]!.opener, from) < 0) {
|
||
low = middle + 1
|
||
} else {
|
||
high = middle
|
||
}
|
||
}
|
||
const last = low > 0 ? furthest[low - 1]! : undefined
|
||
return last && compareAgentJournalPositions(last, from) > 0
|
||
? { sequence: last.sequence, index: last.index + 0.5 }
|
||
: from
|
||
}
|
||
}
|
||
|
||
/** Legacy-only latest loaded send before each submission, by submittedAt with stable ties; removable after the submittedSequence host floor. */
|
||
function latestRowsSentBefore(
|
||
submissions: readonly AgentJournalSubmission[],
|
||
itemsById: ReadonlyMap<string, AgentJournalRenderItem>
|
||
): ReadonlyMap<string, AgentJournalRenderItem> {
|
||
const byAcceptTime = submissions
|
||
.map((submission, order) => ({ submission, order }))
|
||
.sort(
|
||
(left, right) =>
|
||
left.submission.submittedAt - right.submission.submittedAt || left.order - right.order
|
||
)
|
||
const before = new Map<string, AgentJournalRenderItem>()
|
||
let latest: AgentJournalRenderItem | undefined
|
||
for (const { submission } of byAcceptTime) {
|
||
const key = agentJournalSubmissionKey(submission.clientMessageId)
|
||
if (latest) {
|
||
before.set(key, latest)
|
||
}
|
||
const row = itemsById.get(key)
|
||
if (row && (!latest || compareAgentJournalItems(row, latest) > 0)) {
|
||
latest = row
|
||
}
|
||
}
|
||
return before
|
||
}
|
||
|
||
/** Keep independently drawn stopped sends in published submittedSequence order. Return whether any moved.
|
||
* Temporary until a host floor: absent sequence falls back to submittedAt with stable list-order ties. */
|
||
export function keepStoppedSendsInSendOrder(
|
||
messages: NativeChatMessage[],
|
||
stopped: ReadonlyMap<string, AgentJournalSubmission>,
|
||
shown: ReadonlySet<string>
|
||
): boolean {
|
||
const indexById = new Map<string, number>()
|
||
messages.forEach((message, index) => {
|
||
if (shown.has(message.id)) {
|
||
indexById.set(message.id, index)
|
||
}
|
||
})
|
||
const taken = [...stopped].flatMap(([itemId, submission]) => {
|
||
const index = indexById.get(itemId)
|
||
return index === undefined ? [] : [{ index, submission }]
|
||
})
|
||
const sent = inSendOrder(taken, ({ submission }) => submission)
|
||
let floor: AgentJournalPosition | undefined
|
||
let moved = false
|
||
for (const { index } of sent) {
|
||
const message = messages[index]!
|
||
const position = message.journalPosition
|
||
if (!position) {
|
||
continue
|
||
}
|
||
if (floor && compareAgentJournalPositions(position, floor) <= 0) {
|
||
floor = { sequence: floor.sequence, index: floor.index + 1 / 1024 }
|
||
messages[index] = { ...message, journalPosition: floor }
|
||
moved = true
|
||
} else {
|
||
floor = position
|
||
}
|
||
}
|
||
return moved
|
||
}
|
||
|
||
/** One row after each run of sends a Stop took back (`stopped`, by id), placed with the last. */
|
||
export function withStopRowsAfterStoppedSends(
|
||
messages: readonly NativeChatMessage[],
|
||
stopped: ReadonlySet<string>
|
||
): NativeChatMessage[] {
|
||
return messages.flatMap((message, index) =>
|
||
stopped.has(message.id) && !stopped.has(messages[index + 1]?.id ?? '')
|
||
? [
|
||
message,
|
||
{
|
||
id: `stopped-before-start:${message.id}`,
|
||
role: 'system' as const,
|
||
source: 'transcript' as const,
|
||
timestamp: message.timestamp,
|
||
blocks: [
|
||
{
|
||
type: 'text' as const,
|
||
text: NATIVE_CHAT_STOPPED_BEFORE_START_TEXT,
|
||
presentation: NATIVE_CHAT_STOPPED_BEFORE_START_PRESENTATION
|
||
}
|
||
],
|
||
...(message.journalPosition ? { journalPosition: message.journalPosition } : {})
|
||
}
|
||
]
|
||
: [message]
|
||
)
|
||
}
|