fix(native-chat): plain wording for chat errors and status rows (#24594)

* fix(native-chat): plain wording for chat errors and status rows

Replaces Orca-internal words (host, journal, transcript, outbox, process,
unverifiable, "no contact", byte budgets) in native chat refusals, status
rows, the skills menu, subagent and background-task state labels and the
history-load error with plain language, and routes the two hard-coded
English composer errors through translate(). Copy only; no behaviour change.

* fix(native-chat): match chat copy to what happens and to the sidebar's words

- The held-message row says Orca keeps checking, which it does: the idle
  sweep retries the unproven stop and a landed retry sends what waited.
- An unsettled earlier message reads as unconfirmed, not undelivered.
- The skills-unavailable line names SSH chats, its only cause.
- Subagent and background-task rows say "no recent update", the sidebar's
  words for the same state, and "status unavailable" after a count; the
  row no longer repeats the state as a reason.

* test(native-chat): find the repair row by its own text, not the old wording

* fix(native-chat): word the history-repair and too-large rows in the reader's language

Both rows were finished English the host wrote into the chat, so nothing could
translate them. Each now names itself with a presentation, the way the
compaction row does, and the chat says it through translate(). The English text
stays on the row for clients that predate these presentations and for the phone.

* fix(native-chat): say composer send errors with the chat's notice sentences

The composer's two errors had their own wording file beside the sentence table
every other chat notice uses. The send outcome now carries notice parts, worded
by the same function as the rest. A refused redelivery says "Orca couldn't
confirm your message reached the agent. Check the chat, then send it again if
needed." (the same sentence the failed-send rework uses), and a message this
client couldn't store says "Couldn't save your message. Try again."

* fix(native-chat): drop the retry line, keep one name for a lost task, and say only true causes

- The history error pane no longer adds "Orca keeps trying to load this chat."
  under its title: the read still retries on its own, but the pane says only
  that the chat didn't load.
- A write refused as unsupported asks for an Orca update only when no reason
  came back, which means the host is older. A named reason (a location or agent
  that can't run there, no chat host, a client missing the capability) now reads
  "This isn't available in this chat.", since updating doesn't fix it.
- A task Orca lost track of is "no recent update" everywhere; after a count it
  reads "2 agents with no recent update" instead of a second name.
- The skills menu announces the same sentence it shows, including in SSH chats.
- The row for messages held behind a previous agent reads "{{agent}} from before
  may still be running. Your messages will send once it stops."

* fix(native-chat): a chat whose host can't run it says its history didn't load

A history read refused as unsupported with a named reason left the error pane
saying only "This isn't available in this chat.", which never said the chat
failed to load and named nothing the reader asked for. A read now says "This
chat's history couldn't be loaded."; other writes keep the shorter sentence.
This commit is contained in:
Brennan Benson
2026-10-02 19:56:00 -07:00
committed by GitHub
parent a5915b253f
commit ac46d9efda
51 changed files with 405 additions and 300 deletions
@@ -297,7 +297,8 @@ describe('what a structured refusal says on the phone', () => {
expect(result).toEqual({
status: 'failed',
message: "The Orca running this chat doesn't support this. Update Orca, then try again."
message:
'This needs a newer Orca on the computer running this chat. Update Orca there, then try again.'
})
})
})
@@ -146,10 +146,12 @@ describe('a malformed row', () => {
.snapshot()
.items.map((entry) => entry.body)
.find((entry) => entry.kind === 'status')
expect(disclosure).toMatchObject({ kind: 'status' })
expect(disclosure && 'text' in disclosure ? disclosure.text : '').toContain(
'1 journal line could not be read'
)
// Named for the client to word, with English for a client that can't.
expect(disclosure).toEqual({
kind: 'status',
text: "Part of this chat's history couldn't be loaded.",
presentation: 'history-repaired'
})
})
})
@@ -4,8 +4,12 @@
// nothing but its anchor and this row is a repair that has not been
// reconstructed yet, not a timeline.
import { agentSessionHostStatusBody } from '../../../shared/agent-session-host-status-rows'
import { agentJournalItemKey } from '../../../shared/agent-session-journal-item-key'
import type { AgentJournalItemIdentity } from '../../../shared/agent-session-journal-types'
import type {
AgentJournalItemIdentity,
AgentJournalPlainStatusItem
} from '../../../shared/agent-session-journal-types'
/** One stable identity, so a reopen upserts the same row instead of adding one. */
export const JOURNAL_REPAIR_DISCLOSURE_IDENTITY: AgentJournalItemIdentity = {
@@ -19,14 +23,13 @@ export const JOURNAL_REPAIR_DISCLOSURE_ITEM_ID = agentJournalItemKey(
export type JournalRepairDisclosure = {
identity: AgentJournalItemIdentity
body: { kind: 'status'; text: string }
body: AgentJournalPlainStatusItem
}
/** Disclosed when a repair skipped a row it could not read. */
export function journalRepairDisclosure(input: { malformedRows: number }): JournalRepairDisclosure {
const lines = `${input.malformedRows} journal line${input.malformedRows === 1 ? '' : 's'}`
export function journalRepairDisclosure(): JournalRepairDisclosure {
return {
identity: JOURNAL_REPAIR_DISCLOSURE_IDENTITY,
body: { kind: 'status', text: `${lines} could not be read` }
body: agentSessionHostStatusBody('history-repaired')
}
}
@@ -66,7 +66,7 @@ export async function openJournalStoreState(input: {
input.setMalformedRows(loaded.malformedRows)
}
if (input.malformedRows() > 0 && !input.readOnly()) {
const disclosure = journalRepairDisclosure({ malformedRows: input.malformedRows() })
const disclosure = journalRepairDisclosure()
await input.appendItem(disclosure.identity, disclosure.body, input.highestFence())
}
await settleStaleSubagentRosters(input, loaded)
@@ -18,6 +18,7 @@ import type {
} from '../../../shared/agent-session-journal-types'
import type Database from '../../sqlite/sync-database'
import { JOURNAL_DB_SCHEMA_VERSION } from './journal-database-schema'
import { journalRepairDisclosure } from './journal-repair-disclosure'
import { journalDatabasePath } from './journal-host-database'
import type { AgentSessionJournal } from './journal-store'
import {
@@ -109,6 +110,7 @@ afterEach(async () => {
await rm(root, { recursive: true, force: true })
})
const REPAIR_TEXT = journalRepairDisclosure().body.text
describe('axis 1: the database shape', () => {
// A newer build's database opens read-only: its chats read, and nothing here writes to it.
it('reads a database a newer build stamped, refuses every write, and writes nothing', async () => {
@@ -242,9 +244,7 @@ describe('axis 2: the row body shape', () => {
expect(items.some((entry) => entry.body.kind === 'message')).toBe(true)
// …and the skip is visible in the timeline instead of silently swallowed.
expect(
items.some(
(entry) => entry.body.kind === 'status' && entry.body.text.includes('could not be read')
)
items.some((entry) => entry.body.kind === 'status' && entry.body.text === REPAIR_TEXT)
).toBe(true)
})
@@ -264,9 +264,7 @@ describe('axis 2: the row body shape', () => {
expect(
reopened
.snapshot()
.items.filter(
(entry) => entry.body.kind === 'status' && entry.body.text.includes('could not be read')
)
.items.filter((entry) => entry.body.kind === 'status' && entry.body.text === REPAIR_TEXT)
).toHaveLength(1)
})
@@ -7,6 +7,7 @@ import type {
AgentJournalSubmission
} from '../../../shared/agent-session-journal-types'
import { REMOTE_RUNTIME_MAX_OUTBOUND_JSON_BYTES } from '../../../shared/remote-runtime-memory-limits'
import { agentSessionHostStatusBody } from '../../../shared/agent-session-host-status-rows'
export const AGENT_SESSION_HISTORY_MAX_PAGE_BYTES = REMOTE_RUNTIME_MAX_OUTBOUND_JSON_BYTES / 2
@@ -46,17 +47,11 @@ export function submissionBytesByItemId(
return bytes
}
export function oversizedHistoryItem(
item: AgentJournalRenderItem,
byteLength: number
): AgentJournalRenderItem {
export function oversizedHistoryItem(item: AgentJournalRenderItem): AgentJournalRenderItem {
return {
...item,
itemId: boundJournalKeyComponent(item.itemId),
body: {
kind: 'status',
text: `[Orca: item truncated — ${byteLength} bytes exceeds the history page budget]`
}
body: agentSessionHostStatusBody('history-item-too-large')
}
}
@@ -72,7 +67,7 @@ export function boundHistoryItemsByBytes(
for (const group of ordered) {
const bytes = group.reduce((sum, item) => sum + historyEntryBytes(item, submissionBytes), 0)
if (kept.length === 0 && bytes > maxBytes) {
kept.push(group.map((item) => oversizedHistoryItem(item, bytes)))
kept.push(group.map((item) => oversizedHistoryItem(item)))
break
}
if (total + bytes > maxBytes) {
@@ -50,7 +50,7 @@ function referenceBoundHistoryItemsByBytes(
for (const group of ordered) {
const bytes = group.reduce((sum, item) => sum + historyEntryBytes(item, submissionBytes), 0)
if (kept.length === 0 && bytes > maxBytes) {
kept.push(group.map((item) => oversizedHistoryItem(item, bytes)))
kept.push(group.map((item) => oversizedHistoryItem(item)))
break
}
if (total + bytes > maxBytes) {
@@ -153,7 +153,9 @@ it('matches eager byte bounding at every budget boundary in both directions', ()
).toEqual(expected)
if (
actual.items.some(
(entry) => entry.body.kind === 'status' && /truncated/.test(entry.body.text)
(entry) =>
entry.body.kind === 'status' &&
entry.body.presentation === 'history-item-too-large'
)
) {
truncatedCases += 1
@@ -319,9 +319,12 @@ describe('history page byte ceiling', () => {
readAgentSessionHistory(journal, { sessionId: 'session-1', direction: 'tail', limit: 40 })
)
expect(tail.items).toHaveLength(1)
const bodyOnPage = tail.items[0]?.body
expect(bodyOnPage?.kind).toBe('status')
expect(bodyOnPage?.kind === 'status' ? bodyOnPage.text : '').toContain('[Orca: item truncated')
// Named for the client to word, with English for a client that can't.
expect(tail.items[0]?.body).toEqual({
kind: 'status',
text: 'This part of the chat was too large to show.',
presentation: 'history-item-too-large'
})
})
})
@@ -248,7 +248,7 @@ function readForward(
if (contentBytes > HISTORY_PAGE_CONTENT_BUDGET_BYTES) {
items = items.map((item) => {
const bytes = historyEntryBytes(item, submissionBytes)
return bytes > HISTORY_PAGE_CONTENT_BUDGET_BYTES ? oversizedHistoryItem(item, bytes) : item
return bytes > HISTORY_PAGE_CONTENT_BUDGET_BYTES ? oversizedHistoryItem(item) : item
})
contentBytes = pageContentBytes(items, projected.batch.removedItemIds)
}
@@ -280,7 +280,7 @@ it('holds the message with its reason while the exit stays unverifiable, and sen
{
kind: 'status',
tone: 'warning',
text: "Orca couldn't confirm Claude's previous process ended. Messages wait to be sent until Orca confirms it has ended.",
text: 'Claude from before may still be running. Your messages will send once it stops.',
failure: { kind: 'previousExitUnverifiable' }
}
])
@@ -1,7 +1,10 @@
import type { AgentChildRowModel } from '../../../shared/agent-child-row-model'
import { formatAgentTypeLabel } from '../../../shared/agent-type-label'
import { agentStateLabel } from '@/components/AgentStateDot'
import { backgroundTaskStateReason } from '@/components/native-chat/background-task-roster'
import {
backgroundTaskStateReason,
backgroundTaskStateWord
} from '@/components/native-chat/background-task-roster'
import { translate } from '@/i18n/i18n'
import { agentNoUpdateLabel } from '@/lib/agent-row-decay-state'
import { formatAgentToolPreview } from '@/lib/agent-row-tool-preview'
@@ -41,7 +44,7 @@ export function agentChildRowDetailText(row: AgentChildRowModel, now: number): s
case 'role':
return formatAgentTypeLabel(detail.agentType)
case 'reason':
return backgroundTaskStateReason(detail.state) ?? ''
return backgroundTaskStateReason(detail.state) ?? backgroundTaskStateWord(detail.state)
}
}
@@ -106,12 +106,32 @@ describe('NativeChatPickerMenu', () => {
/>
)
expect(screen.getAllByText('Could not load skills from this host')).toHaveLength(2)
expect(screen.getAllByText("Couldn't load skills")).toHaveLength(2)
expect(screen.queryByText('Loading skills...')).toBeNull()
fireEvent.click(screen.getByRole('button', { name: 'Retry' }))
expect(onRetry).toHaveBeenCalledOnce()
})
it('announces the same unavailable sentence it shows, with no Retry', () => {
render(
<NativeChatPickerMenu
autocomplete={autocomplete({
items: [],
skillStatus: 'error',
skillErrorKind: 'unavailable'
})}
activeIndex={0}
listboxId="picker"
onChoose={vi.fn()}
onRetry={vi.fn()}
/>
)
expect(screen.getAllByText("Skills aren't available in SSH chats")).toHaveLength(2)
expect(screen.queryByText("Couldn't load skills")).toBeNull()
expect(screen.queryByRole('button', { name: 'Retry' })).toBeNull()
})
it('uses command-only empty copy for a picker without skill support', () => {
render(
<NativeChatPickerMenu
@@ -39,6 +39,14 @@ export const NativeChatPickerMenu = memo(function NativeChatPickerMenu({
const emptyText = noMatches ? getPickerEmptyText(autocomplete) : null
const collision = commands.find((item) => item.skillCollision)
const duplicate = skills.find((item) => item.sources.length > 1)
// One sentence for the line and the screen reader, so the two never disagree.
const skillErrorText =
autocomplete.skillErrorKind === 'unavailable'
? translate(
'components.native-chat.composer.skillsUnavailableHost',
"Skills aren't available in SSH chats"
)
: translate('components.native-chat.composer.skillsLoadFailed', "Couldn't load skills")
let optionIndex = 0
return (
@@ -71,17 +79,7 @@ export const NativeChatPickerMenu = memo(function NativeChatPickerMenu({
) : null}
{autocomplete.skillStatus === 'error' ? (
<PickerStatus>
<span className="min-w-0 flex-1">
{autocomplete.skillErrorKind === 'unavailable'
? translate(
'components.native-chat.composer.skillsUnavailableHost',
'Skills are unavailable for this host'
)
: translate(
'components.native-chat.composer.skillsLoadFailed',
'Could not load skills from this host'
)}
</span>
<span className="min-w-0 flex-1">{skillErrorText}</span>
{autocomplete.skillErrorKind !== 'unavailable' ? (
<button
type="button"
@@ -114,10 +112,7 @@ export const NativeChatPickerMenu = memo(function NativeChatPickerMenu({
{autocomplete.skillStatus === 'loading'
? translate('components.native-chat.composer.loadingSkills', 'Loading skills...')
: autocomplete.skillStatus === 'error'
? translate(
'components.native-chat.composer.skillsLoadFailed',
'Could not load skills from this host'
)
? skillErrorText
: emptyText
? emptyText
: autocomplete.skillsEnabled
@@ -56,10 +56,10 @@ describe('NativeChatBackgroundTaskRun', () => {
expect(screen.getByText('Background workflow')).toBeInTheDocument()
})
it('reads a state this build has no word for as no contact, never as live', () => {
it('reads a state this build has no word for as no recent update, never as live', () => {
// oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: models a row a newer build wrote, which the wire admits as an open string.
render(<NativeChatBackgroundTaskRun block={task({ state: 'teleported' as 'done' })} />)
expect(screen.getByText(/unverifiable/)).toBeInTheDocument()
expect(screen.getByText(/no recent update/)).toBeInTheDocument()
})
})
@@ -38,7 +38,7 @@ export function NativeChatBackgroundTaskRun({
// not read as two different things on the two surfaces.
const label = resolveBackgroundTaskName({ id: block.taskId, kind, description: block.label })
// Every attention state states its reason on the row, the same word the strip
// uses; `unverifiable` ("no contact") must never be silently dropped.
// uses; `unverifiable` has none beyond its state word, which must never be dropped.
const reason = backgroundTaskStateReason(state)
// The sentence the provider itself wrote. It is the row's whole reason for
// existing when a task fails, and it is dropped from the prose above as the
@@ -281,7 +281,7 @@ describe('background-task row reasons', () => {
return screen.getAllByRole('listitem')
}
// `unverifiable` is the SSH verdict for "no contact"; a row that hides it reads
// `unverifiable` is the SSH verdict for lost contact; a row that hides it reads
// like a working child. `blocked` is the same class of loss.
it('names the reason on every attention state, not only on waiting', () => {
const rows = expandedRows([
@@ -291,7 +291,7 @@ describe('background-task row reasons', () => {
{ id: 'a4', kind: 'agent', description: 'busy child', state: 'working' }
])
expect(rows).toHaveLength(4)
expect(rows[0].textContent).toContain('ssh child · no contact')
expect(rows[0].textContent).toContain('ssh child · no recent update')
expect(rows[1].textContent).toContain('flaky child · failed')
expect(rows[2].textContent).toContain('approval child · needs approval')
// A running row has nothing to explain.
@@ -17,11 +17,11 @@ it('keeps pointing the terminal-backed chat back to its terminal when a read fai
).toBeInTheDocument()
})
it('tells the structured chat its read keeps retrying', () => {
render(<NativeChatEmptyState kind="error" retrying />)
expect(
screen.getByText('The transcript could not be read. Orca keeps trying to load it.')
).toBeInTheDocument()
// It retries on its own, which the pane does not report, and has no terminal to point back to.
it('says only that the structured chat did not load while its read retries', () => {
const { container } = render(<NativeChatEmptyState kind="error" retrying />)
expect(screen.getByText('Could not load conversation')).toBeInTheDocument()
expect(container.querySelectorAll('p')).toHaveLength(1)
})
it('shows the host message in place of the terminal-backed default', () => {
@@ -31,8 +31,8 @@ it('shows the host message in place of the terminal-backed default', () => {
})
// The structured chat passes words only from the notice table, never the host's.
it("says the structured chat's own words for the failure once, as the title, above its retrying line", () => {
render(
it("says the structured chat's own words for the failure once, as the title, and nothing more", () => {
const { container } = render(
<NativeChatEmptyState
kind="error"
retrying
@@ -43,9 +43,7 @@ it("says the structured chat's own words for the failure once, as the title, abo
'font-medium'
)
expect(screen.queryByText('Could not load conversation')).toBeNull()
expect(
screen.getByText('The transcript could not be read. Orca keeps trying to load it.')
).toBeInTheDocument()
expect(container.querySelectorAll('p')).toHaveLength(1)
})
it('says a failure no retry gets past in its one line, with nothing of trying again', () => {
@@ -1,17 +1,13 @@
import { MessageSquare, TriangleAlert } from 'lucide-react'
import { translate } from '@/i18n/i18n'
import { formatAgentTypeLabel } from '@/lib/agent-status'
import {
NATIVE_CHAT_EMPTY_STATE_COPY,
NATIVE_CHAT_KEEPS_TRYING_COPY
} from '../../../../shared/native-chat-empty-state'
import { NATIVE_CHAT_EMPTY_STATE_COPY } from '../../../../shared/native-chat-empty-state'
import type { NativeChatSession } from '../../../../shared/native-chat-types'
export function NativeChatEmptyState({
kind,
message,
headline,
headlineSaysUnread = false,
agent,
retrying = false
}: {
@@ -19,15 +15,12 @@ export function NativeChatEmptyState({
message?: string
/** The chat's own sentence for the failure, said once: it takes the generic title's place. */
headline?: string
/** The headline already says the history didn't load, so the retrying line says only that
* Orca keeps trying. */
headlineSaysUnread?: boolean
agent?: NativeChatSession['agent']
/** The read retries on its own (structured chat), so the error says so instead of pointing
* back to the terminal. */
/** The read retries on its own (structured chat), so the error says only that it didn't load,
* never pointing back to the terminal. */
retrying?: boolean
}): React.JSX.Element {
const copy = emptyStateCopy(kind, { message, headline, headlineSaysUnread }, agent, retrying)
const copy = emptyStateCopy(kind, { message, headline }, agent, retrying)
return (
<div className="flex h-full w-full flex-col items-center justify-center gap-3 p-6 text-center">
<div
@@ -53,7 +46,7 @@ export function NativeChatEmptyState({
function emptyStateCopy(
kind: 'loading' | 'empty' | 'error' | 'not-agent',
words: { message?: string; headline?: string; headlineSaysUnread?: boolean },
words: { message?: string; headline?: string },
agent?: NativeChatSession['agent'],
retrying = false
): { title: string; subtitle: string | null } {
@@ -70,32 +63,21 @@ function emptyStateCopy(
)
}
case 'error': {
const retryingLine = !retrying
? null
: words.headline && words.headlineSaysUnread
? translate(
'components.native-chat.state.error.keepsTrying',
NATIVE_CHAT_KEEPS_TRYING_COPY
)
: translate(
'components.native-chat.state.error.retryingSubtitle',
NATIVE_CHAT_EMPTY_STATE_COPY.retryingError.subtitle
)
if (words.headline) {
return { title: words.headline, subtitle: retryingLine }
return { title: words.headline, subtitle: null }
}
return {
title: translate(
'components.native-chat.state.error.title',
NATIVE_CHAT_EMPTY_STATE_COPY.error.title
),
subtitle:
retryingLine ??
words.message ??
translate(
'components.native-chat.state.error.subtitle',
NATIVE_CHAT_EMPTY_STATE_COPY.error.subtitle
)
subtitle: retrying
? null
: (words.message ??
translate(
'components.native-chat.state.error.subtitle',
NATIVE_CHAT_EMPTY_STATE_COPY.error.subtitle
))
}
}
case 'not-agent':
@@ -70,6 +70,15 @@ describe('notice rows', () => {
expect(disclosure?.querySelector('summary')).not.toHaveTextContent('Check the configuration')
expect(disclosure?.querySelector('pre')).toHaveTextContent('Check the configuration')
})
// The host's text is only for a client that can't word the row itself.
it.each([
['history-repaired', "Part of this chat's history couldn't be loaded."],
['history-item-too-large', 'This part of the chat was too large to show.']
])('words a %s row itself, as a muted status line', (presentation, words) => {
renderStatus({ kind: 'status', text: 'Words an older host wrote', presentation })
expect(screen.getByText(words)).toHaveClass('text-muted-foreground', 'text-sm')
expect(screen.queryByText('Words an older host wrote')).toBeNull()
})
it('renders future presentation and tone values as untinted text', () => {
renderStatus({
kind: 'status',
@@ -5,9 +5,27 @@ import CommentMarkdown, {
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'
import { translate } from '@/i18n/i18n'
import { cn } from '@/lib/utils'
import {
AGENT_SESSION_HOST_STATUS_COPY as HOST_STATUS_COPY,
isAgentSessionHostStatusPresentation,
type AgentSessionHostStatusPresentation
} from '../../../../shared/agent-session-host-status-rows'
import type { NativeChatTextBlock } from '../../../../shared/native-chat-types'
import { ProviderFrameRow } from './NativeChatTranscriptChrome'
const HOST_STATUS_WORDS: Record<AgentSessionHostStatusPresentation, () => string> = {
'history-repaired': () =>
translate(
'components.native-chat.notices.historyRepaired',
HOST_STATUS_COPY['history-repaired']
),
'history-item-too-large': () =>
translate(
'components.native-chat.notices.historyItemTooLarge',
HOST_STATUS_COPY['history-item-too-large']
)
}
export function NativeChatNoticeRow({
block,
onLinkClick,
@@ -31,6 +49,14 @@ export function NativeChatNoticeRow({
</div>
)
}
if (isAgentSessionHostStatusPresentation(block.presentation)) {
// The look of any other host status line; only the words are the reader's.
return (
<p className="min-w-0 max-w-full select-text text-sm text-muted-foreground [overflow-wrap:anywhere]">
{HOST_STATUS_WORDS[block.presentation]()}
</p>
)
}
if (block.presentation === 'plan-document') {
return (
<Card className="gap-3 py-3 shadow-xs">
@@ -45,17 +45,16 @@ function journalRefusal(
return { code: 'agent_session_journal_unreadable', details: { reason } } as const
}
// The host's message and code never reach the pane; it words the refusal, and says it once.
it('says a failed read with no refusal keeps retrying, and adds nothing of the host', () => {
// The host's message and code never reach the pane; it words the refusal, and says it once. The
// read retries on its own, which the pane does not report.
it('says only that a failed read with no refusal did not load, and adds nothing of the host', () => {
mocks.status = 'error'
mocks.messages = []
renderPane()
expect(screen.getByText('Could not load conversation')).toBeTruthy()
expect(
screen.getByText('The transcript could not be read. Orca keeps trying to load it.')
).toBeTruthy()
expect(screen.queryByText(/keeps trying/)).toBeNull()
expect(screen.queryByText(/history couldn't be loaded/)).toBeNull()
expect(screen.queryByText(/Toggle back to the terminal/)).toBeNull()
})
@@ -73,7 +72,7 @@ it('says a damaged history cannot load in one line, without claiming Orca keeps
expect(screen.queryByText(/agent_session_/)).toBeNull()
})
it("names a history that couldn't open right now once, and that the pane keeps trying", () => {
it("names a history that couldn't open right now once, in its one line", () => {
mocks.status = 'error'
mocks.readRefusal = journalRefusal('journalUnavailable')
mocks.messages = []
@@ -82,13 +81,11 @@ it("names a history that couldn't open right now once, and that the pane keeps t
expect(screen.getAllByText("Orca couldn't open this chat's history right now.")).toHaveLength(1)
expect(screen.queryByText('Could not load conversation')).toBeNull()
// The title already says the history didn't open, so the line under it says only the retrying.
expect(screen.getByText('Orca keeps trying to load it.')).toBeTruthy()
expect(screen.queryByText(/could not be read/)).toBeNull()
expect(screen.queryByText(/keeps trying/)).toBeNull()
expect(screen.queryByText(/Try again/)).toBeNull()
})
it("says only that it keeps trying under a code's own words that the history didn't load", () => {
it("says a code's own words that the history didn't load, and nothing under them", () => {
mocks.status = 'error'
mocks.readRefusal = {
code: 'agent_session_checkpoint_stale',
@@ -99,11 +96,25 @@ it("says only that it keeps trying under a code's own words that the history did
renderPane()
expect(screen.getAllByText("This chat's history couldn't be loaded.")).toHaveLength(1)
expect(screen.getByText('Orca keeps trying to load it.')).toBeTruthy()
expect(screen.queryByText(/could not be read/)).toBeNull()
expect(screen.queryByText(/keeps trying/)).toBeNull()
})
it('keeps the whole retrying line under a newer Orca\'s words, which name no one chat for "it"', () => {
// "This isn't available in this chat." would name nothing the reader asked for.
it('says only that the history did not load for a chat its host cannot run', () => {
mocks.status = 'error'
mocks.readRefusal = {
code: 'structured_agent_session_unsupported',
details: { reason: 'hostUnsupported' }
} as const
mocks.messages = []
renderPane()
expect(screen.getAllByText("This chat's history couldn't be loaded.")).toHaveLength(1)
expect(screen.queryByText(/isn't available|newer Orca/)).toBeNull()
})
it("says a newer Orca's words alone", () => {
mocks.status = 'error'
mocks.readRefusal = journalRefusal('journalWrittenByNewerOrca')
mocks.messages = []
@@ -111,9 +122,7 @@ it('keeps the whole retrying line under a newer Orca\'s words, which name no one
renderPane()
expect(screen.getByText(/^Chats were saved by a newer Orca\./)).toBeTruthy()
expect(
screen.getByText('The transcript could not be read. Orca keeps trying to load it.')
).toBeTruthy()
expect(screen.queryByText(/keeps trying/)).toBeNull()
})
it('says only that it is reconnecting, not as an error, when a failure names nothing', () => {
@@ -276,9 +276,7 @@ export function NativeChatStructuredSession(
<NativeChatEmptyState
kind="error"
retrying={!readFailure?.final}
{...(readFailure?.named
? { headline: readFailure.text, headlineSaysUnread: readFailure.saysUnread }
: {})}
{...(readFailure?.named ? { headline: readFailure.text } : {})}
/>
) : viewState.kind === 'empty' ? (
<NativeChatEmptyState kind="empty" agent={props.agent} />
@@ -168,7 +168,7 @@ describe('NativeChatSubagentRun', () => {
)
const row = screen.getByRole('button')
expect(row).toHaveTextContent('unverifiable')
expect(row).toHaveTextContent('no recent update')
// `unverifiable` with no terminal timestamp has no known run length, so the
// clock would measure to `now` and report the time since we lost sight of
// the child as how long it ran — on a row that is not even counting.
@@ -189,7 +189,7 @@ describe('NativeChatSubagentRun', () => {
)
const row = screen.getByRole('button')
expect(row).toHaveTextContent('unverifiable')
expect(row).toHaveTextContent('1 with no recent update')
expect(row.textContent).not.toContain('·')
})
@@ -48,7 +48,7 @@ function subagentStateLabel(
case 'stopped':
return translate('components.native-chat.subagents.state.stopped', 'stopped')
case 'unverifiable':
return translate('components.native-chat.subagents.state.unverifiable', 'unverifiable')
return translate('components.native-chat.subagents.state.unverifiable', 'no recent update')
}
}
switch (state) {
@@ -79,7 +79,7 @@ function subagentStateLabel(
case 'unverifiable':
return translate(
'components.native-chat.subagents.state.unverifiableCount',
'{{value0}} unverifiable',
'{{value0}} with no recent update',
{ value0: count }
)
}
@@ -232,7 +232,7 @@ describe('desktop words for a failure fact', () => {
it('keeps Japanese-only characters out of the Chinese words', () => {
const chat = zh.components['native-chat']
for (const words of [...Object.values(chat.failureWords), chat.state.error.keepsTrying]) {
for (const words of Object.values(chat.failureWords)) {
expect(words).not.toMatch(JAPANESE_ONLY)
}
})
@@ -22,6 +22,8 @@ const SENTENCES: Record<AgentSessionWriteNoticeSentence, () => string> = {
notDoneSend: () => translate('components.native-chat.writeNotice.notDoneSend', COPY.notDoneSend),
tryAgainComposerSend: () =>
translate('components.native-chat.writeNotice.tryAgainComposerSend', COPY.tryAgainComposerSend),
messageNotSaved: () =>
translate('components.native-chat.writeNotice.messageNotSaved', COPY.messageNotSaved),
notDoneStop: () => translate('components.native-chat.writeNotice.notDoneStop', COPY.notDoneStop),
notDoneStopTask: () =>
translate('components.native-chat.writeNotice.notDoneStopTask', COPY.notDoneStopTask),
@@ -39,6 +41,8 @@ const SENTENCES: Record<AgentSessionWriteNoticeSentence, () => string> = {
capacity: () => translate('components.native-chat.writeNotice.capacity', COPY.capacity),
outcomeUnknown: () =>
translate('components.native-chat.writeNotice.outcomeUnknown', COPY.outcomeUnknown),
sendOutcomeLost: () =>
translate('components.native-chat.writeNotice.sendOutcomeLost', COPY.sendOutcomeLost),
questionChanged: () =>
translate('components.native-chat.writeNotice.questionChanged', COPY.questionChanged),
historyUnreadable: () =>
@@ -55,6 +59,8 @@ const SENTENCES: Record<AgentSessionWriteNoticeSentence, () => string> = {
COPY.updateOrcaToKeepUsing
),
unsupported: () => translate('components.native-chat.writeNotice.unsupported', COPY.unsupported),
notAvailable: () =>
translate('components.native-chat.writeNotice.notAvailable', COPY.notAvailable),
unreachable: () => translate('components.native-chat.writeNotice.unreachable', COPY.unreachable),
recordFailed: () =>
translate('components.native-chat.writeNotice.recordFailed', COPY.recordFailed),
@@ -8,6 +8,7 @@ import type {
} from '../../../../shared/agent-session-wire'
import { translate } from '@/i18n/i18n'
import {
backgroundTaskCountedState,
backgroundTaskElapsedLabel,
backgroundTaskStateReason,
backgroundTaskStateWord,
@@ -148,7 +149,7 @@ export function backgroundTasksHeaderContent(
return {
segments: [
{
text: `${kindCountLabel(group.kind, count)} ${backgroundTaskStateWord(uniformState)}`,
text: backgroundTaskCountedState(kindCountLabel(group.kind, count), uniformState),
kind: group.kind
}
],
@@ -185,7 +186,7 @@ export function backgroundTasksHeaderContent(
detail:
stateCounts.length > 0
? stateCounts
.map((entry) => `${entry.count} ${backgroundTaskStateWord(entry.state)}`)
.map((entry) => backgroundTaskCountedState(entry.count, entry.state))
.join(', ')
: null
}
@@ -77,8 +77,8 @@ describe('backgroundTasksHeaderContent', () => {
expect(
header([agent('a', { state: 'unverifiable' }), agent('b', { state: 'unverifiable' })])
).toEqual({
segments: [{ text: '2 agents unverifiable', kind: 'agent' }],
detail: 'no contact'
segments: [{ text: '2 agents with no recent update', kind: 'agent' }],
detail: null
})
})
@@ -122,7 +122,10 @@ export function backgroundTaskStateWord(state: RunState): string {
case 'idle':
return translate('components.native-chat.backgroundTasks.stateIdle', 'stopped')
case 'unverifiable':
return translate('components.native-chat.backgroundTasks.stateUnverifiable', 'unverifiable')
return translate(
'components.native-chat.backgroundTasks.stateUnverifiable',
'no recent update'
)
}
}
@@ -131,18 +134,29 @@ export function backgroundTaskStateReason(state: RunState): string | null {
switch (state) {
case 'waiting':
return translate('components.native-chat.backgroundTasks.reasonWaiting', 'needs approval')
case 'unverifiable':
return translate('components.native-chat.backgroundTasks.reasonUnverifiable', 'no contact')
case 'blocked':
return translate('components.native-chat.backgroundTasks.reasonBlocked', 'failed')
case 'working':
case 'monitoring':
case 'done':
case 'idle':
case 'unverifiable':
// `unverifiable`'s state word already says it.
return null
}
}
/** A count and its state ("2 agents waiting"); after a count, "no recent update" needs a "with". */
export function backgroundTaskCountedState(counted: string | number, state: RunState): string {
return state === 'unverifiable'
? translate(
'components.native-chat.backgroundTasks.stateUnverifiableCount',
'{{value0}} with no recent update',
{ value0: counted }
)
: `${counted} ${backgroundTaskStateWord(state)}`
}
function tokenScaleText(value: number): string {
return Number.isInteger(value) ? value.toFixed(0) : value.toFixed(1)
}
@@ -10,6 +10,7 @@ import {
disposeStructuredAgentSessionSendFailure,
disposeStructuredAgentSessionSendRefusal,
disposeStructuredAgentSessionSendResult,
STRUCTURED_AGENT_SESSION_OUTBOX_NOT_SAVED,
type StructuredAgentSessionSendDisposition
} from '../../../../shared/structured-agent-session-send-disposition'
import type { RuntimeClientTarget } from '@/runtime/runtime-rpc-client'
@@ -24,6 +25,7 @@ import {
commitStructuredAgentSessionOutbox,
getStructuredAgentSessionOutbox
} from './structured-agent-session-outbox-storage'
import { agentSessionWriteNoticeText } from './agent-session-write-notice-text'
import {
getStructuredAgentLaunchPromptDispatch,
shareStructuredAgentLaunchPromptDispatch
@@ -111,7 +113,7 @@ export function dispatchStructuredAgentSessionOutboxEntry(args: {
(entry) => ({ ...entry, lastFailure: { kind: 'failed' } })
)
)
args.setError('Message could not be saved to the outbox')
args.setError(agentSessionWriteNoticeText(STRUCTURED_AGENT_SESSION_OUTBOX_NOT_SAVED))
return false
}
// No `finally` release below: `applyDisposition` frees single-flight as part of the state
@@ -10,6 +10,8 @@ import {
commitStructuredAgentSessionOutbox,
getStructuredAgentSessionOutbox
} from './structured-agent-session-outbox-storage'
import { STRUCTURED_AGENT_SESSION_OUTBOX_NOT_SAVED } from '../../../../shared/structured-agent-session-send-disposition'
import { agentSessionWriteNoticeText } from './agent-session-write-notice-text'
export function retryStructuredAgentSessionOutboxEntry(args: {
clientMessageId: string
@@ -47,7 +49,7 @@ export function retryStructuredAgentSessionOutboxEntry(args: {
: entry
)
if (!commitStructuredAgentSessionOutbox(sessionId, rotated, { onlyIfSaved: true })) {
setError('Message could not be saved to the outbox')
setError(agentSessionWriteNoticeText(STRUCTURED_AGENT_SESSION_OUTBOX_NOT_SAVED))
}
return
}
@@ -67,7 +69,7 @@ export function retryStructuredAgentSessionOutboxEntry(args: {
: entry
)
if (!commitStructuredAgentSessionOutbox(sessionId, next, { onlyIfSaved: true })) {
setError('Message could not be saved to the outbox')
setError(agentSessionWriteNoticeText(STRUCTURED_AGENT_SESSION_OUTBOX_NOT_SAVED))
}
}
@@ -5,7 +5,6 @@ import {
agentSessionReadHistoryRefusalParts,
agentSessionWriteNotDoneParts
} from '../../../../shared/agent-session-refusal-notice'
import { agentSessionNoticeSaysThisChatUnread } from '../../../../shared/agent-session-write-notice-copy'
import type { AgentSessionRefusalReference } from '../../../../shared/agent-session-wire-refusals'
import { isFinalAgentSessionReadRefusal } from '../../../../shared/structured-agent-session-read-refusal'
import { agentSessionWriteNoticeText } from './agent-session-write-notice-text'
@@ -14,10 +13,8 @@ export type StructuredAgentSessionReadFailureNotice = {
text: string
/** The host named the situation, so the words say more than that the history did not load. */
named: boolean
/** Nothing the read retries gets past it, so the pane no longer says it keeps trying. */
/** Nothing the read retries gets past it. */
final: boolean
/** The words already say this chat's history didn't load, so the pane need not say it again. */
saysUnread: boolean
}
export function structuredAgentSessionReadFailureNotice(
@@ -30,7 +27,6 @@ export function structuredAgentSessionReadFailureNotice(
return {
text: agentSessionWriteNoticeText(parts),
named: refusal?.details?.reason !== undefined,
final: isFinalAgentSessionReadRefusal(refusal),
saysUnread: agentSessionNoticeSaysThisChatUnread(parts)
final: isFinalAgentSessionReadRefusal(refusal)
}
}
@@ -390,9 +390,7 @@ describe('a message whose send could not be saved before it went out', () => {
})
const { result } = mount()
act(() => expect(result.current.send('first')).toBe(true))
await waitFor(() =>
expect(result.current.error).toBe('Message could not be saved to the outbox')
)
await waitFor(() => expect(result.current.error).toBe("Couldn't save your message. Try again."))
setItem.mockRestore()
const firstId = result.current.outbox[0]!.clientMessageId
@@ -453,7 +451,7 @@ describe('a message whose send could not be saved before it went out', () => {
const before = mount()
act(() => expect(before.result.current.send('first')).toBe(true))
await waitFor(() =>
expect(before.result.current.error).toBe('Message could not be saved to the outbox')
expect(before.result.current.error).toBe("Couldn't save your message. Try again.")
)
const firstId = before.result.current.outbox[0]!.clientMessageId
before.unmount()
@@ -414,10 +414,10 @@ describe('useStructuredAgentSessionOutbox', () => {
throw new Error('storage full')
})
act(() => expect(result.current.send('tail that cannot be saved')).toBe(false))
expect(result.current.error).toBe('Message could not be saved to the outbox')
expect(result.current.error).toBe("Couldn't save your message. Try again.")
rerender({ submissions: [{ ...pendingResultFor(id, 10).value.submission }] })
expect(result.current.error).toBe('Message could not be saved to the outbox')
expect(result.current.error).toBe("Couldn't save your message. Try again.")
setItem.mockRestore()
})
@@ -457,7 +457,7 @@ describe('useStructuredAgentSessionOutbox', () => {
it('drains a head the host refuses to redeliver so the queue behind it advances', async () => {
// The guard refuses a retry it cannot prove is a first delivery. That must
// not leave a Retry that does nothing in front of a wedged queue: the entry
// leaves the outbox, the user is told Orca will not send it again, and the
// leaves the outbox, the user is told to check the chat before sending it again, and the
// message queued behind it goes out.
// The second send never settles, so the refusal's error is still on screen
// when the queue behind it advances.
@@ -503,7 +503,7 @@ describe('useStructuredAgentSessionOutbox', () => {
)
expect(sent).toContain('second')
expect(result.current.error).toBe(
'Message delivery is unconfirmed and Orca will not send it again'
"Orca couldn't confirm your message reached the agent. Check the chat, then send it again if needed."
)
})
@@ -14,6 +14,7 @@ import {
} from '../../../../shared/structured-agent-session-outbox-admission'
import {
journalAnswersInFlightSend,
STRUCTURED_AGENT_SESSION_OUTBOX_NOT_SAVED,
type StructuredAgentSessionSendDisposition
} from '../../../../shared/structured-agent-session-send-disposition'
import type { RuntimeClientTarget } from '@/runtime/runtime-rpc-client'
@@ -46,6 +47,7 @@ import {
} from '../../../../shared/structured-agent-session-outbox-delivery'
import { retryStructuredAgentSessionOutboxEntry } from './structured-agent-session-outbox-retry'
import { useStructuredAgentSessionOutboxFailedHere } from './use-structured-agent-session-outbox-failed-here'
import { agentSessionWriteNoticeText } from './agent-session-write-notice-text'
const NO_QUEUE_DELIVERY: StructuredAgentSessionQueueDelivery = {
capability: 'unsupported',
@@ -180,7 +182,7 @@ export function useStructuredAgentSessionOutbox(args: {
// Released here rather than in a `.finally`: the state write below is what re-runs the
// drain, so a later microtask would leave the queue with no trigger to move on.
inFlightIdRef.current = null
setError(disposition.error)
setError(disposition.error ? agentSessionWriteNoticeText(disposition.error) : null)
recordFailures(getStructuredAgentSessionOutbox(sessionId), disposition.entries)
commitStructuredAgentSessionOutbox(sessionId, disposition.entries)
},
@@ -266,7 +268,7 @@ export function useStructuredAgentSessionOutbox(args: {
}
// Whether it asks to be queued is decided when it first goes out.
if (!appendStructuredAgentSessionOutboxMessage(sessionId, text, attachments)) {
setError('Message could not be saved to the outbox')
setError(agentSessionWriteNoticeText(STRUCTURED_AGENT_SESSION_OUTBOX_NOT_SAVED))
return false
}
setError(null)
File diff suppressed because one or more lines are too long
@@ -221,7 +221,7 @@ describe('chat strip rows from a legacy background-task roster', () => {
container.innerHTML = markup
expect([...container.querySelectorAll('li')].map((row) => row.textContent)).toEqual([
'Failed child · failed',
'Background agent · no contactStop',
'Background agent · no recent updateStop',
'Cancelled child',
'Approve edits · needs approvalStop',
'Summarize logs900',
+13 -8
View File
@@ -2763,7 +2763,7 @@
"notDelivered": "This message was not delivered.",
"notDeliveredSendAgain": "This message was not delivered. Send it again to continue.",
"notSignedIn": "{{agent}} is not signed in for the selected account.",
"previousExitUnverifiable": "Orca couldn't confirm {{agent}}'s previous process ended. Messages wait to be sent until Orca confirms it has ended.",
"previousExitUnverifiable": "{{agent}} from before may still be running. Your messages will send once it stops.",
"providerExitedRejection": "{{agent}} stopped before this message was sent.",
"providerExitedRow": "{{agent}} stopped while this response was in progress. You can continue in this conversation.",
"providerRateLimited": "{{agent}} is rate-limited and retrying.",
@@ -2783,6 +2783,10 @@
"theAgent": "The agent",
"writeFailed": "Orca couldn't hand this message to the agent, so it was not sent."
},
"notices": {
"historyItemTooLarge": "This part of the chat was too large to show.",
"historyRepaired": "Part of this chat's history couldn't be loaded."
},
"question": {
"other": "Other…"
},
@@ -2792,8 +2796,6 @@
"title": "Start a chat with {{value0}}"
},
"error": {
"keepsTrying": "Orca keeps trying to load it.",
"retryingSubtitle": "The transcript could not be read. Orca keeps trying to load it.",
"subtitle": "The transcript could not be read. Toggle back to the terminal to keep working.",
"title": "Could not load conversation"
},
@@ -2838,7 +2840,7 @@
"answerFirst": "Answer the question or approval first.",
"backgroundTasksRunning": "Background tasks are still running.",
"capacity": "Orca has received too many requests in the last day.",
"chatNotFound": "The Orca running this chat has no record of it.",
"chatNotFound": "Orca can't find this chat.",
"clearUnfinished": "The last /clear didn't finish.",
"commandRunning": "A /compact or /clear is still running.",
"conversationCleared": "This conversation has been cleared.",
@@ -2847,7 +2849,9 @@
"historyUnreadable": "Orca couldn't read this chat's saved history.",
"historyUnusable": "Unable to load this chat.",
"hostReconciling": "Orca is still checking on this chat after restarting.",
"messagesUnsettled": "A message you sent earlier hasn't been confirmed yet.",
"messageNotSaved": "Couldn't save your message.",
"messagesUnsettled": "A message you sent earlier isn't confirmed yet.",
"notAvailable": "This isn't available in this chat.",
"notDoneAnswer": "Your answer was not sent.",
"notDoneCommand": "The command didn't run.",
"notDoneGoal": "The goal wasn't changed.",
@@ -2860,11 +2864,12 @@
"openCurrentConversation": "Open the current conversation to continue.",
"optionRejected": "The agent didn't accept this setting.",
"outcomeUnknown": "Orca couldn't confirm what happened. Check the chat.",
"ownerUnproven": "Orca hasn't confirmed that this chat's previous agent stopped.",
"sendOutcomeLost": "Orca couldn't confirm your message reached the agent. Check the chat, then send it again if needed.",
"ownerUnproven": "The previous agent in this chat may still be running.",
"promptPending": "The agent is waiting for an answer to a question or approval.",
"questionChanged": "This question was already answered or has changed.",
"quitTerminalAgent": "Quit that agent to continue the chat here.",
"recordFailed": "Orca couldn't record it in this chat's history.",
"recordFailed": "Orca couldn't save this to the chat's history.",
"recordUnreadable": "Orca couldn't read this chat's saved state.",
"reopenChat": "Reopen the chat to check again.",
"restartFailed": "The agent couldn't restart.",
@@ -2876,7 +2881,7 @@
"tryAgainComposerSend": "Send it again.",
"turnActive": "The agent is still responding.",
"unreachable": "Orca couldn't reach the agent.",
"unsupported": "The Orca running this chat doesn't support this. Update Orca, then try again.",
"unsupported": "This needs a newer Orca on the computer running this chat. Update Orca there, then try again.",
"updateOrcaToKeepUsing": "Update Orca to keep using them.",
"waitForBackgroundTasks": "Wait for the background tasks to finish.",
"waitForCommand": "Wait for the /compact or /clear to finish.",
+19 -16
View File
@@ -17565,12 +17565,13 @@
"providerRateLimited": "{{agent}} is rate-limited and retrying.",
"providerRetrying": "{{agent}} hit a temporary problem and is retrying.",
"providerRetryingQuoted": "{{agent}} is retrying: {{detail}}.",
"previousExitUnverifiable": "Orca couldn't confirm {{agent}}'s previous process ended. Messages wait to be sent until Orca confirms it has ended."
"previousExitUnverifiable": "{{agent}} from before may still be running. Your messages will send once it stops."
},
"writeNotice": {
"notDoneReadHistory": "This chat's history couldn't be loaded.",
"notDoneSend": "Your message was not sent.",
"tryAgainComposerSend": "Send it again.",
"messageNotSaved": "Couldn't save your message.",
"notDoneStop": "The agent wasn't stopped.",
"notDoneStopTask": "The background task wasn't stopped.",
"notDoneStopTasks": "The background tasks weren't stopped.",
@@ -17581,15 +17582,17 @@
"restartFailed": "The agent couldn't restart.",
"capacity": "Orca has received too many requests in the last day.",
"outcomeUnknown": "Orca couldn't confirm what happened. Check the chat.",
"sendOutcomeLost": "Orca couldn't confirm your message reached the agent. Check the chat, then send it again if needed.",
"questionChanged": "This question was already answered or has changed.",
"historyUnreadable": "Orca couldn't read this chat's saved history.",
"historyUnusable": "Unable to load this chat.",
"historyUnavailable": "Orca couldn't open this chat's history right now.",
"savedByNewerOrca": "Chats were saved by a newer Orca.",
"updateOrcaToKeepUsing": "Update Orca to keep using them.",
"unsupported": "The Orca running this chat doesn't support this. Update Orca, then try again.",
"unsupported": "This needs a newer Orca on the computer running this chat. Update Orca there, then try again.",
"notAvailable": "This isn't available in this chat.",
"unreachable": "Orca couldn't reach the agent.",
"recordFailed": "Orca couldn't record it in this chat's history.",
"recordFailed": "Orca couldn't save this to the chat's history.",
"conversationCleared": "This conversation has been cleared.",
"openCurrentConversation": "Open the current conversation to continue.",
"clearUnfinished": "The last /clear didn't finish.",
@@ -17603,19 +17606,19 @@
"answerFirst": "Answer the question or approval first.",
"backgroundTasksRunning": "Background tasks are still running.",
"waitForBackgroundTasks": "Wait for the background tasks to finish.",
"messagesUnsettled": "A message you sent earlier hasn't been confirmed yet.",
"messagesUnsettled": "A message you sent earlier isn't confirmed yet.",
"settleEarlierMessage": "Wait for your earlier message to go through, or retry it.",
"optionRejected": "The agent didn't accept this setting.",
"goalsUnsupported": "This agent doesn't support goals.",
"agentRefused": "The agent turned this down.",
"ownerUnproven": "Orca hasn't confirmed that this chat's previous agent stopped.",
"ownerUnproven": "The previous agent in this chat may still be running.",
"reopenChat": "Reopen the chat to check again.",
"terminalAgentHoldsChat": "This chat is still open in a terminal agent.",
"quitTerminalAgent": "Quit that agent to continue the chat here.",
"hostReconciling": "Orca is still checking on this chat after restarting.",
"waitMoment": "Wait a moment.",
"recordUnreadable": "Orca couldn't read this chat's saved state.",
"chatNotFound": "The Orca running this chat has no record of it.",
"chatNotFound": "Orca can't find this chat.",
"startNewChat": "Start a new chat to continue.",
"tryAgain": "Try again."
},
@@ -17651,7 +17654,9 @@
"notices": {
"compaction": "Context compacted",
"details": "Details",
"plan": "Plan"
"plan": "Plan",
"historyRepaired": "Part of this chat's history couldn't be loaded.",
"historyItemTooLarge": "This part of the chat was too large to show."
},
"composer": {
"imageUnsupported": "Image paste is not supported for this agent.",
@@ -17670,8 +17675,8 @@
"skills": "Skills",
"loadingSkills": "Loading skills...",
"skillsLoaded": "Skills loaded",
"skillsUnavailableHost": "Skills are unavailable for this host",
"skillsLoadFailed": "Could not load skills from this host",
"skillsUnavailableHost": "Skills aren't available in SSH chats",
"skillsLoadFailed": "Couldn't load skills",
"retrySkills": "Retry",
"skillCommandCollision": "Also a skill name - agent decides",
"skillMultipleSources": "{{sourceCount}} sources - agent resolves",
@@ -17831,9 +17836,9 @@
"stateBlocked": "blocked",
"stateDone": "done",
"stateIdle": "stopped",
"stateUnverifiable": "unverifiable",
"stateUnverifiable": "no recent update",
"stateUnverifiableCount": "{{value0}} with no recent update",
"reasonWaiting": "needs approval",
"reasonUnverifiable": "no contact",
"reasonBlocked": "failed",
"groupAgents": "Agents",
"groupShell": "Shell",
@@ -17854,9 +17859,7 @@
},
"error": {
"title": "Could not load conversation",
"subtitle": "The transcript could not be read. Toggle back to the terminal to keep working.",
"retryingSubtitle": "The transcript could not be read. Orca keeps trying to load it.",
"keepsTrying": "Orca keeps trying to load it."
"subtitle": "The transcript could not be read. Toggle back to the terminal to keep working."
},
"reconnecting": "Reconnecting to this chat…",
"pairHost": "Pair a host to view agent chat history.",
@@ -17917,12 +17920,12 @@
"idle": "idle",
"failed": "failed",
"stopped": "stopped",
"unverifiable": "unverifiable",
"unverifiable": "no recent update",
"workingCount": "{{value0}} working",
"idleCount": "{{value0}} idle",
"failedCount": "{{value0}} failed",
"stoppedCount": "{{value0}} stopped",
"unverifiableCount": "{{value0}} unverifiable"
"unverifiableCount": "{{value0}} with no recent update"
},
"startedOne": "Kicked off 1 subagent",
"startedN": "Kicked off {{value0}} subagents",
+12 -11
View File
@@ -14993,12 +14993,13 @@
"providerRateLimited": "{{agent}} alcanzó un límite de solicitudes y está reintentando.",
"providerRetrying": "{{agent}} tuvo un problema temporal y está reintentando.",
"providerRetryingQuoted": "{{agent}} está reintentando: {{detail}}.",
"previousExitUnverifiable": "Orca no pudo confirmar que el proceso anterior de {{agent}} terminó. Los mensajes esperan para enviarse hasta que Orca confirme que terminó."
"previousExitUnverifiable": "Es posible que {{agent}} siga en ejecución desde antes. Sus mensajes se enviarán cuando se detenga."
},
"writeNotice": {
"notDoneReadHistory": "No se pudo cargar el historial de este chat.",
"notDoneSend": "Su mensaje no se envió.",
"tryAgainComposerSend": "Vuelva a enviarlo.",
"messageNotSaved": "No se pudo guardar su mensaje.",
"notDoneStop": "El agente no se detuvo.",
"notDoneStopTask": "La tarea en segundo plano no se detuvo.",
"notDoneStopTasks": "Las tareas en segundo plano no se detuvieron.",
@@ -15009,15 +15010,17 @@
"restartFailed": "El agente no pudo reiniciarse.",
"capacity": "Orca ha recibido demasiadas solicitudes en el último día.",
"outcomeUnknown": "Orca no pudo confirmar qué pasó. Revise el chat.",
"sendOutcomeLost": "Orca no pudo confirmar que su mensaje llegó al agente. Revise el chat y vuelva a enviarlo si es necesario.",
"questionChanged": "Esta pregunta ya se respondió o ha cambiado.",
"historyUnreadable": "Orca no pudo leer el historial guardado de este chat.",
"historyUnusable": "No se puede cargar este chat.",
"historyUnavailable": "Orca no pudo abrir el historial de este chat en este momento.",
"savedByNewerOrca": "Los chats se guardaron con una versión más reciente de Orca.",
"updateOrcaToKeepUsing": "Actualice Orca para seguir usándolos.",
"unsupported": "La versión de Orca que ejecuta este chat no lo admite. Actualice Orca y vuelva a intentarlo.",
"unsupported": "Esto requiere una versión más reciente de Orca en el equipo que ejecuta este chat. Actualice Orca allí y vuelva a intentarlo.",
"notAvailable": "Esto no está disponible en este chat.",
"unreachable": "Orca no pudo comunicarse con el agente.",
"recordFailed": "Orca no pudo registrarlo en el historial de este chat.",
"recordFailed": "Orca no pudo guardar esto en el historial del chat.",
"conversationCleared": "Esta conversación se borró.",
"openCurrentConversation": "Abra la conversación actual para continuar.",
"clearUnfinished": "El último /clear no terminó.",
@@ -15031,19 +15034,19 @@
"answerFirst": "Responda primero a la pregunta o aprobación.",
"backgroundTasksRunning": "Todavía hay tareas en segundo plano en ejecución.",
"waitForBackgroundTasks": "Espere a que terminen las tareas en segundo plano.",
"messagesUnsettled": "Un mensaje que envió antes todavía no se ha confirmado.",
"messagesUnsettled": "Un mensaje que envió antes todavía no está confirmado.",
"settleEarlierMessage": "Espere a que se entregue su mensaje anterior o vuelva a intentarlo.",
"optionRejected": "El agente no aceptó este ajuste.",
"goalsUnsupported": "Este agente no admite objetivos.",
"agentRefused": "El agente lo rechazó.",
"ownerUnproven": "Orca no ha confirmado que el agente anterior de este chat se haya detenido.",
"ownerUnproven": "Es posible que el agente anterior de este chat siga en ejecución.",
"reopenChat": "Vuelva a abrir el chat para comprobarlo de nuevo.",
"terminalAgentHoldsChat": "Este chat sigue abierto en un agente de terminal.",
"quitTerminalAgent": "Cierre ese agente para continuar el chat aquí.",
"hostReconciling": "Orca todavía está comprobando este chat después de reiniciarse.",
"waitMoment": "Espere un momento.",
"recordUnreadable": "Orca no pudo leer el estado guardado de este chat.",
"chatNotFound": "La versión de Orca que ejecuta este chat no tiene ningún registro de él.",
"chatNotFound": "Orca no encuentra este chat.",
"startNewChat": "Inicie un chat nuevo para continuar.",
"tryAgain": "Vuelva a intentarlo."
},
@@ -15092,8 +15095,8 @@
"commands": "Comandos",
"skills": "Habilidades",
"loadingSkills": "Cargando habilidades…",
"skillsUnavailableHost": "Las habilidades no están disponibles para este host",
"skillsLoadFailed": "No se pudieron cargar las habilidades de este host",
"skillsUnavailableHost": "Las habilidades no están disponibles en los chats SSH",
"skillsLoadFailed": "No se pudieron cargar las habilidades",
"retrySkills": "Reintentar",
"skillCommandCollision": "También es un nombre de habilidad - el agente decide",
"skillMultipleSources": "{{sourceCount}} fuentes - el agente resuelve",
@@ -15126,9 +15129,7 @@
},
"error": {
"title": "No se pudo cargar la conversación",
"subtitle": "No se pudo leer la transcripción. Vuelva a la terminal para seguir trabajando.",
"retryingSubtitle": "No se pudo leer la transcripción. Orca sigue intentando cargarla.",
"keepsTrying": "Orca sigue intentando cargarlo."
"subtitle": "No se pudo leer la transcripción. Vuelva a la terminal para seguir trabajando."
},
"reconnecting": "Reconectando con este chat…",
"pairHost": "Empareje un host para ver el historial de chat del agente.",
+19 -16
View File
@@ -17463,12 +17463,13 @@
"providerRateLimited": "{{agent}} est limité en débit et réessaie.",
"providerRetrying": "{{agent}} a rencontré un problème temporaire et réessaie.",
"providerRetryingQuoted": "{{agent}} réessaie : {{detail}}.",
"previousExitUnverifiable": "Orca n'a pas pu confirmer que le processus précédent de {{agent}} s'est arrêté. Les messages attendent d'être envoyés jusqu'à ce qu'Orca confirme son arrêt."
"previousExitUnverifiable": "L'exécution précédente de {{agent}} est peut-être toujours en cours. Vos messages seront envoyés dès qu'elle s'arrêtera."
},
"writeNotice": {
"notDoneReadHistory": "L'historique de ce chat n'a pas pu être chargé.",
"notDoneSend": "Votre message n'a pas été envoyé.",
"tryAgainComposerSend": "Renvoyez-le.",
"messageNotSaved": "Impossible d'enregistrer votre message.",
"notDoneStop": "L'agent n'a pas été arrêté.",
"notDoneStopTask": "La tâche en arrière-plan n'a pas été arrêtée.",
"notDoneStopTasks": "Les tâches en arrière-plan n'ont pas été arrêtées.",
@@ -17479,15 +17480,17 @@
"restartFailed": "L'agent n'a pas pu redémarrer.",
"capacity": "Orca a reçu trop de requêtes au cours de la dernière journée.",
"outcomeUnknown": "Orca n'a pas pu confirmer ce qui s'est passé. Vérifiez le chat.",
"sendOutcomeLost": "Orca n'a pas pu confirmer que votre message est parvenu à l'agent. Vérifiez le chat, puis renvoyez-le si nécessaire.",
"questionChanged": "Cette question a déjà reçu une réponse ou a changé.",
"historyUnreadable": "Orca n'a pas pu lire l'historique enregistré de ce chat.",
"historyUnusable": "Impossible de charger ce chat.",
"historyUnavailable": "Orca n'a pas pu ouvrir l'historique de ce chat pour le moment.",
"savedByNewerOrca": "Les chats ont été enregistrés par une version plus récente d'Orca.",
"updateOrcaToKeepUsing": "Mettez à jour Orca pour continuer à les utiliser.",
"unsupported": "L'instance d'Orca qui exécute ce chat ne le prend pas en charge. Mettez Orca à jour, puis réessayez.",
"unsupported": "Cela nécessite une version plus récente d'Orca sur l'ordinateur qui exécute ce chat. Mettez Orca à jour sur cet ordinateur, puis réessayez.",
"notAvailable": "Ceci n'est pas disponible dans ce chat.",
"unreachable": "Orca n'a pas pu joindre l'agent.",
"recordFailed": "Orca n'a pas pu l'enregistrer dans l'historique de ce chat.",
"recordFailed": "Orca n'a pas pu enregistrer ceci dans l'historique du chat.",
"conversationCleared": "Cette conversation a été effacée.",
"openCurrentConversation": "Ouvrez la conversation actuelle pour continuer.",
"clearUnfinished": "Le dernier /clear ne s'est pas terminé.",
@@ -17501,19 +17504,19 @@
"answerFirst": "Répondez d'abord à la question ou à la demande d'approbation.",
"backgroundTasksRunning": "Des tâches en arrière-plan sont encore en cours.",
"waitForBackgroundTasks": "Attendez la fin des tâches en arrière-plan.",
"messagesUnsettled": "Un message que vous avez envoyé plus tôt n'a pas encore été confirmé.",
"messagesUnsettled": "Un message que vous avez envoyé plus tôt n'est pas encore confirmé.",
"settleEarlierMessage": "Attendez que votre message précédent soit transmis, ou renvoyez-le.",
"optionRejected": "L'agent n'a pas accepté ce réglage.",
"goalsUnsupported": "Cet agent ne prend pas en charge les objectifs.",
"agentRefused": "L'agent l'a refusé.",
"ownerUnproven": "Orca n'a pas confirmé l'arrêt de l'agent précédent de ce chat.",
"ownerUnproven": "L'agent précédent de ce chat est peut-être encore en cours d'exécution.",
"reopenChat": "Rouvrez le chat pour vérifier à nouveau.",
"terminalAgentHoldsChat": "Ce chat est encore ouvert dans un agent de terminal.",
"quitTerminalAgent": "Quittez cet agent pour continuer le chat ici.",
"hostReconciling": "Orca vérifie encore ce chat après son redémarrage.",
"waitMoment": "Patientez un instant.",
"recordUnreadable": "Orca n'a pas pu lire l'état enregistré de ce chat.",
"chatNotFound": "L'instance d'Orca qui exécute ce chat n'en a aucune trace.",
"chatNotFound": "Orca ne trouve pas ce chat.",
"startNewChat": "Démarrez un nouveau chat pour continuer.",
"tryAgain": "Réessayez."
},
@@ -17534,8 +17537,8 @@
"skills": "Skills",
"loadingSkills": "Chargement des skills...",
"skillsLoaded": "Skills chargées",
"skillsUnavailableHost": "Les skills sont indisponibles pour cet hôte",
"skillsLoadFailed": "Impossible de charger les skills depuis cet hôte",
"skillsUnavailableHost": "Les skills ne sont pas disponibles dans les chats SSH",
"skillsLoadFailed": "Impossible de charger les skills",
"retrySkills": "Réessayer",
"skillCommandCollision": "Aussi un nom de skill - l'agent décide",
"skillMultipleSources": "{{sourceCount}} sources - l'agent décide",
@@ -17675,9 +17678,7 @@
},
"error": {
"title": "Impossible de charger la conversation",
"subtitle": "La transcription n'a pas pu être lue. Rebasculez sur le terminal pour continuer à travailler.",
"retryingSubtitle": "La transcription n'a pas pu être lue. Orca continue d'essayer de la charger.",
"keepsTrying": "Orca continue d'essayer de le charger."
"subtitle": "La transcription n'a pas pu être lue. Rebasculez sur le terminal pour continuer à travailler."
},
"reconnecting": "Reconnexion à ce chat…",
"pairHost": "Associez un hôte pour consulter l'historique de chat des agents.",
@@ -17754,7 +17755,9 @@
"notices": {
"compaction": "Contexte compacté",
"details": "Détails",
"plan": "Plan"
"plan": "Plan",
"historyRepaired": "Une partie de l'historique de ce chat n'a pas pu être chargée.",
"historyItemTooLarge": "Cette partie du chat était trop volumineuse pour être affichée."
},
"providerFrame": {
"byteLength": "{{value0}} octets"
@@ -17789,9 +17792,9 @@
"stateBlocked": "bloqué",
"stateDone": "fait",
"stateIdle": "arrêté",
"stateUnverifiable": "invérifiable",
"stateUnverifiable": "aucune mise à jour récente",
"stateUnverifiableCount": "{{value0}} sans mise à jour récente",
"reasonWaiting": "a besoin d'approbation",
"reasonUnverifiable": "aucun contact",
"reasonBlocked": "échoué",
"groupAgents": "Agents",
"groupShell": "Coquille",
@@ -17815,12 +17818,12 @@
"idle": "idle",
"failed": "échoué",
"stopped": "arrêté",
"unverifiable": "invérifiable",
"unverifiable": "aucune mise à jour récente",
"workingCount": "{{value0}} fonctionne",
"idleCount": "{{value0}} inactif",
"failedCount": "{{value0}} a échoué",
"stoppedCount": "{{value0}} arrêté",
"unverifiableCount": "{{value0}} invérifiable"
"unverifiableCount": "{{value0}} sans mise à jour récente"
},
"startedOne": "A lancé 1 sous-agent",
"startedN": "Lancement des sous-agents {{value0}}",
+19 -16
View File
@@ -17399,12 +17399,13 @@
"providerRateLimited": "{{agent}} はレート制限を受けているため、再試行しています。",
"providerRetrying": "{{agent}} で一時的な問題が発生したため、再試行しています。",
"providerRetryingQuoted": "{{agent}} は再試行しています: {{detail}}。",
"previousExitUnverifiable": "Orca は {{agent}} の前のプロセスが終了したことを確認できませんでした。Orca が終了を確認するまで、メッセージは送信を待機します。"
"previousExitUnverifiable": "以前の {{agent}} がまだ実行中の可能性があります。停止するとメッセージが送信されます。"
},
"writeNotice": {
"notDoneReadHistory": "このチャットの履歴を読み込めませんでした。",
"notDoneSend": "メッセージは送信されませんでした。",
"tryAgainComposerSend": "もう一度送信してください。",
"messageNotSaved": "メッセージを保存できませんでした。",
"notDoneStop": "Agent は停止されませんでした。",
"notDoneStopTask": "バックグラウンドタスクは停止されませんでした。",
"notDoneStopTasks": "バックグラウンドタスクは停止されませんでした。",
@@ -17415,15 +17416,17 @@
"restartFailed": "Agent を再起動できませんでした。",
"capacity": "Orca が過去 1 日に受け取ったリクエストが多すぎます。",
"outcomeUnknown": "Orca は何が起きたかを確認できませんでした。チャットを確認してください。",
"sendOutcomeLost": "Orca はメッセージが Agent に届いたことを確認できませんでした。チャットを確認し、必要であればもう一度送信してください。",
"questionChanged": "この質問はすでに回答済みか、変更されています。",
"historyUnreadable": "Orca はこのチャットの保存済み履歴を読み取れませんでした。",
"historyUnusable": "このチャットを読み込めません。",
"historyUnavailable": "Orca は現在このチャットの履歴を開けませんでした。",
"savedByNewerOrca": "チャットは新しいバージョンの Orca で保存されました。",
"updateOrcaToKeepUsing": "引き続き使用するには、Orca を更新してください。",
"unsupported": "このチャットを実行している Orca はこの操作に対応していません。Orca を更新してから、もう一度お試しください。",
"unsupported": "このチャットを実行しているコンピューターに新しいバージョンの Orca が必要です。そのコンピューターで Orca を更新してから、もう一度お試しください。",
"notAvailable": "このチャットでは利用できません。",
"unreachable": "Orca は Agent に接続できませんでした。",
"recordFailed": "Orca はこのチャットの履歴に記録できませんでした。",
"recordFailed": "Orca はこれをチャットの履歴に保存できませんでした。",
"conversationCleared": "この会話はクリアされました。",
"openCurrentConversation": "続けるには、現在の会話を開いてください。",
"clearUnfinished": "前回の /clear は完了しませんでした。",
@@ -17437,19 +17440,19 @@
"answerFirst": "先に質問または承認に回答してください。",
"backgroundTasksRunning": "バックグラウンドタスクがまだ実行中です。",
"waitForBackgroundTasks": "バックグラウンドタスクが終わるまでお待ちください。",
"messagesUnsettled": "以前に送信したメッセージがまだ確認されていません。",
"messagesUnsettled": "以前に送信したメッセージはまだ確認されていません。",
"settleEarlierMessage": "以前のメッセージが届くまで待つか、再試行してください。",
"optionRejected": "Agent はこの設定を受け入れませんでした。",
"goalsUnsupported": "この Agent は目標に対応していません。",
"agentRefused": "Agent がこれを拒否しました。",
"ownerUnproven": "Orca は、このチャットの以前の Agent が停止したことをまだ確認できていません。",
"ownerUnproven": "このチャットの以前の Agent がまだ実行中の可能性があります。",
"reopenChat": "もう一度確認するには、チャットを開き直してください。",
"terminalAgentHoldsChat": "このチャットはまだターミナルの Agent で開かれています。",
"quitTerminalAgent": "ここでチャットを続けるには、その Agent を終了してください。",
"hostReconciling": "Orca は再起動後、このチャットをまだ確認しています。",
"waitMoment": "しばらくお待ちください。",
"recordUnreadable": "Orca はこのチャットの保存済みの状態を読み取れませんでした。",
"chatNotFound": "このチャットを実行している Orca には、このチャットの記録がありません。",
"chatNotFound": "Orca はこのチャットを見つけられません。",
"startNewChat": "続けるには、新しいチャットを開始してください。",
"tryAgain": "もう一度お試しください。"
},
@@ -17498,8 +17501,8 @@
"commands": "コマンド",
"skills": "スキル",
"loadingSkills": "スキルを読み込み中…",
"skillsUnavailableHost": "このホストではスキルが利用できません",
"skillsLoadFailed": "このホストからスキルを読み込めませんでした",
"skillsUnavailableHost": "SSH チャットではスキルを利用できません",
"skillsLoadFailed": "スキルを読み込めませんでした",
"retrySkills": "再試行",
"skillCommandCollision": "スキル名でもあります - Agent が判断します",
"skillMultipleSources": "{{sourceCount}} 個のソース - Agent が解決します",
@@ -17611,9 +17614,7 @@
},
"error": {
"title": "会話を読み込めませんでした",
"subtitle": "トランスクリプトを読み取ることができませんでした。作業を続けるには、ターミナルに切り替えてください。",
"retryingSubtitle": "トランスクリプトを読み取ることができませんでした。Orca は読み込みを再試行し続けます。",
"keepsTrying": "Orca は読み込みを再試行し続けます。"
"subtitle": "トランスクリプトを読み取ることができませんでした。作業を続けるには、ターミナルに切り替えてください。"
},
"reconnecting": "このチャットに再接続しています…",
"pairHost": "ホストをペアリングして、Agent のチャット履歴を表示します。",
@@ -17690,7 +17691,9 @@
"notices": {
"compaction": "圧縮されたコンテキスト",
"details": "詳細",
"plan": "プラン"
"plan": "プラン",
"historyRepaired": "このチャットの履歴の一部を読み込めませんでした。",
"historyItemTooLarge": "チャットのこの部分は大きすぎるため表示できません。"
},
"providerFrame": {
"byteLength": "{{value0}}バイト"
@@ -17725,9 +17728,9 @@
"stateBlocked": "ブロックされました",
"stateDone": "終わり",
"stateIdle": "止まった",
"stateUnverifiable": "検証不能",
"stateUnverifiable": "最近の更新なし",
"stateUnverifiableCount": "{{value0}} は最近の更新なし",
"reasonWaiting": "承認が必要です",
"reasonUnverifiable": "連絡がありません",
"reasonBlocked": "失敗した",
"groupAgents": "Agent",
"groupShell": "シェル",
@@ -17751,12 +17754,12 @@
"idle": "idle",
"failed": "失敗した",
"stopped": "止まった",
"unverifiable": "検証不能",
"unverifiable": "最近の更新なし",
"workingCount": "{{value0}} は動作しています",
"idleCount": "{{value0}} アイドル状態",
"failedCount": "{{value0}} 件失敗",
"stoppedCount": "{{value0}} が停止しました",
"unverifiableCount": "{{value0}} 検証不能"
"unverifiableCount": "{{value0}} は最近の更新なし"
},
"startedOne": "1 人のサブ Agent を解雇しました",
"startedN": "{{value0}} サブ Agent をキックオフしました",
+18 -15
View File
@@ -17399,12 +17399,13 @@
"providerRateLimited": "{{agent}}이(가) 속도 제한에 걸려 다시 시도하고 있습니다.",
"providerRetrying": "{{agent}}에 일시적인 문제가 발생하여 다시 시도하고 있습니다.",
"providerRetryingQuoted": "{{agent}}이(가) 다시 시도하고 있습니다: {{detail}}.",
"previousExitUnverifiable": "Orca가 {{agent}}의 이전 프로세스가 종료되었는지 확인하지 못했습니다. Orca가 종료를 확인할 때까지 메시지는 전송을 기다립니다."
"previousExitUnverifiable": "이전에 실행된 {{agent}}이(가) 아직 실행 중일 수 있습니다. 중지되면 메시지가 전송됩니다."
},
"writeNotice": {
"notDoneReadHistory": "이 채팅의 기록을 불러오지 못했습니다.",
"notDoneSend": "메시지가 전송되지 않았습니다.",
"tryAgainComposerSend": "다시 보내세요.",
"messageNotSaved": "메시지를 저장할 수 없습니다.",
"notDoneStop": "에이전트가 중지되지 않았습니다.",
"notDoneStopTask": "백그라운드 작업이 중지되지 않았습니다.",
"notDoneStopTasks": "백그라운드 작업이 중지되지 않았습니다.",
@@ -17415,15 +17416,17 @@
"restartFailed": "에이전트를 다시 시작하지 못했습니다.",
"capacity": "Orca가 지난 하루 동안 너무 많은 요청을 받았습니다.",
"outcomeUnknown": "Orca가 무슨 일이 있었는지 확인하지 못했습니다. 채팅을 확인하세요.",
"sendOutcomeLost": "Orca가 메시지가 에이전트에 전달되었는지 확인하지 못했습니다. 채팅을 확인하고 필요하면 다시 보내세요.",
"questionChanged": "이 질문은 이미 답변되었거나 변경되었습니다.",
"historyUnreadable": "Orca가 이 채팅의 저장된 기록을 읽지 못했습니다.",
"historyUnusable": "이 채팅을 불러올 수 없습니다.",
"historyUnavailable": "Orca가 지금 이 채팅의 기록을 열지 못했습니다.",
"savedByNewerOrca": "채팅이 최신 버전의 Orca에서 저장되었습니다.",
"updateOrcaToKeepUsing": "계속 사용하려면 Orca를 업데이트하세요.",
"unsupported": "이 채팅을 실행 중인 Orca가 이 기능을 지원하지 않습니다. Orca를 업데이트한 후 다시 시도하세요.",
"unsupported": "이 채팅을 실행 중인 컴퓨터에 최신 버전의 Orca가 필요합니다. 해당 컴퓨터에서 Orca를 업데이트한 후 다시 시도하세요.",
"notAvailable": "이 채팅에서는 사용할 수 없습니다.",
"unreachable": "Orca가 에이전트에 연결하지 못했습니다.",
"recordFailed": "Orca가 이 채팅의 기록에 저장하지 못했습니다.",
"recordFailed": "Orca가 이 내용을 채팅 기록에 저장하지 못했습니다.",
"conversationCleared": "이 대화는 지워졌습니다.",
"openCurrentConversation": "계속하려면 현재 대화를 여세요.",
"clearUnfinished": "마지막 /clear가 완료되지 않았습니다.",
@@ -17442,14 +17445,14 @@
"optionRejected": "에이전트가 이 설정을 받아들이지 않았습니다.",
"goalsUnsupported": "이 에이전트는 목표를 지원하지 않습니다.",
"agentRefused": "에이전트가 이를 거부했습니다.",
"ownerUnproven": "Orca가 이 채팅의 이전 에이전트가 중지되었는지 아직 확인하지 못했습니다.",
"ownerUnproven": "이 채팅의 이전 에이전트가 아직 실행 중일 수 있습니다.",
"reopenChat": "다시 확인하려면 채팅을 다시 여세요.",
"terminalAgentHoldsChat": "이 채팅은 아직 터미널 에이전트에서 열려 있습니다.",
"quitTerminalAgent": "여기에서 채팅을 계속하려면 해당 에이전트를 종료하세요.",
"hostReconciling": "Orca가 다시 시작된 후 이 채팅을 아직 확인하고 있습니다.",
"waitMoment": "잠시 기다리세요.",
"recordUnreadable": "Orca가 이 채팅의 저장된 상태를 읽지 못했습니다.",
"chatNotFound": "이 채팅을 실행하는 Orca에 이 채팅의 기록이 없습니다.",
"chatNotFound": "Orca에서 이 채팅을 찾을 수 없습니다.",
"startNewChat": "계속하려면 새 채팅을 시작하세요.",
"tryAgain": "다시 시도하세요."
},
@@ -17498,8 +17501,8 @@
"commands": "명령어",
"skills": "스킬",
"loadingSkills": "스킬 로딩 중…",
"skillsUnavailableHost": "이 호스트에서는 스킬을 사용할 수 없습니다",
"skillsLoadFailed": "이 호스트에서 스킬을 불러올 수 없습니다",
"skillsUnavailableHost": "SSH 채팅에서는 스킬을 사용할 수 없습니다",
"skillsLoadFailed": "스킬을 불러올 수 없습니다",
"retrySkills": "재시도",
"skillCommandCollision": "스킬 이름이기도 합니다 - 에이전트가 결정합니다",
"skillMultipleSources": "{{sourceCount}}개 소스 - 에이전트가 해결합니다",
@@ -17611,9 +17614,7 @@
},
"error": {
"title": "대화를 로드할 수 없습니다.",
"subtitle": "스크립트를 읽을 수 없습니다. 계속 작업하려면 terminal로 다시 전환하세요.",
"retryingSubtitle": "스크립트를 읽을 수 없습니다. Orca가 계속 다시 불러오기를 시도합니다.",
"keepsTrying": "Orca가 계속 다시 불러오기를 시도합니다."
"subtitle": "스크립트를 읽을 수 없습니다. 계속 작업하려면 terminal로 다시 전환하세요."
},
"reconnecting": "이 채팅에 다시 연결하는 중…",
"pairHost": "호스트를 페어링하여 에이전트 채팅 기록을 확인하세요.",
@@ -17690,7 +17691,9 @@
"notices": {
"compaction": "컨텍스트가 압축됨",
"details": "세부",
"plan": "계획"
"plan": "계획",
"historyRepaired": "이 채팅 기록의 일부를 불러올 수 없습니다.",
"historyItemTooLarge": "채팅의 이 부분은 너무 커서 표시할 수 없습니다."
},
"providerFrame": {
"byteLength": "{{value0}}바이트"
@@ -17725,9 +17728,9 @@
"stateBlocked": "막힌",
"stateDone": "완료",
"stateIdle": "중지됨",
"stateUnverifiable": "검증할 수 없는",
"stateUnverifiable": "최근 업데이트 없음",
"stateUnverifiableCount": "{{value0}} 최근 업데이트 없음",
"reasonWaiting": "승인이 필요합니다",
"reasonUnverifiable": "연락 없음",
"reasonBlocked": "실패한",
"groupAgents": "에이전트",
"groupShell": "셸",
@@ -17751,12 +17754,12 @@
"idle": "idle",
"failed": "실패한",
"stopped": "중지됨",
"unverifiable": "검증할 수 없는",
"unverifiable": "최근 업데이트 없음",
"workingCount": "{{value0}} 작업 중",
"idleCount": "{{value0}} 유휴",
"failedCount": "{{value0}} 실패",
"stoppedCount": "{{value0}} 중지됨",
"unverifiableCount": "{{value0}} 확인할 수 없음"
"unverifiableCount": "{{value0}} 최근 업데이트 없음"
},
"startedOne": "서브에이전트 1명 추방",
"startedN": "{{value0}} 하위 에이전트가 시작되었습니다.",
+18 -15
View File
@@ -17364,12 +17364,13 @@
"providerRateLimited": "{{agent}} 已被限流,正在重试。",
"providerRetrying": "{{agent}} 遇到临时问题,正在重试。",
"providerRetryingQuoted": "{{agent}} 正在重试:{{detail}}。",
"previousExitUnverifiable": "Orca 无法确认 {{agent}} 的上一个进程已结束。在 Orca 确认其已结束之前,消息会等待发送。"
"previousExitUnverifiable": "之前的 {{agent}} 可能仍在运行。它停止后,您的消息就会发送。"
},
"writeNotice": {
"notDoneReadHistory": "无法加载此聊天的历史记录。",
"notDoneSend": "您的消息未发送。",
"tryAgainComposerSend": "请重新发送。",
"messageNotSaved": "无法保存您的消息。",
"notDoneStop": "智能体未停止。",
"notDoneStopTask": "后台任务未停止。",
"notDoneStopTasks": "后台任务未停止。",
@@ -17380,15 +17381,17 @@
"restartFailed": "智能体无法重新启动。",
"capacity": "Orca 在过去一天内收到的请求过多。",
"outcomeUnknown": "Orca 无法确认发生了什么。请查看聊天。",
"sendOutcomeLost": "Orca 无法确认你的消息已送达代理。请查看聊天,如有需要请重新发送。",
"questionChanged": "此问题已被回答或已更改。",
"historyUnreadable": "Orca 无法读取此聊天的已保存历史记录。",
"historyUnusable": "无法加载此聊天。",
"historyUnavailable": "Orca 目前无法打开此聊天的历史记录。",
"savedByNewerOrca": "这些聊天由更新版本的 Orca 保存。",
"updateOrcaToKeepUsing": "请更新 Orca 以继续使用这些聊天。",
"unsupported": "运行此聊天的 Orca 不支持此操作。请更新 Orca 后重试。",
"unsupported": "运行此聊天的计算机需要更新版本的 Orca。请在该计算机上更新 Orca 后重试。",
"notAvailable": "此聊天中无法使用此功能。",
"unreachable": "Orca 无法连接到智能体。",
"recordFailed": "Orca 无法将其记录到此聊天的历史记录中。",
"recordFailed": "Orca 无法将此内容保存到聊天历史记录中。",
"conversationCleared": "此对话已被清除。",
"openCurrentConversation": "请打开当前对话以继续。",
"clearUnfinished": "上一次 /clear 未完成。",
@@ -17407,14 +17410,14 @@
"optionRejected": "智能体未接受此设置。",
"goalsUnsupported": "此智能体不支持目标。",
"agentRefused": "智能体拒绝了此请求。",
"ownerUnproven": "Orca 尚未确认此聊天之前的智能体已停止。",
"ownerUnproven": "此聊天之前的智能体可能仍在运行。",
"reopenChat": "请重新打开聊天以再次检查。",
"terminalAgentHoldsChat": "此聊天仍在终端智能体中打开。",
"quitTerminalAgent": "请退出该智能体,以便在此处继续聊天。",
"hostReconciling": "Orca 重启后仍在检查此聊天。",
"waitMoment": "请稍候。",
"recordUnreadable": "Orca 无法读取此聊天的已保存状态。",
"chatNotFound": "运行此聊天的 Orca 没有此聊天的记录。",
"chatNotFound": "Orca 找不到此聊天。",
"startNewChat": "请开始新的聊天以继续。",
"tryAgain": "请重试。"
},
@@ -17463,8 +17466,8 @@
"commands": "命令",
"skills": "技能",
"loadingSkills": "正在加载技能…",
"skillsUnavailableHost": "此主机上无法使用技能",
"skillsLoadFailed": "无法从此主机加载技能",
"skillsUnavailableHost": "SSH 聊天中无法使用技能",
"skillsLoadFailed": "无法加载技能",
"retrySkills": "重试",
"skillCommandCollision": "也是技能名称 — 由智能体决定",
"skillMultipleSources": "{{sourceCount}} 个来源 — 由智能体处理",
@@ -17576,9 +17579,7 @@
},
"error": {
"title": "无法加载对话",
"subtitle": "无法读取记录。请切回终端继续工作。",
"retryingSubtitle": "无法读取记录。Orca 会继续尝试加载。",
"keepsTrying": "Orca 会继续尝试加载。"
"subtitle": "无法读取记录。请切回终端继续工作。"
},
"reconnecting": "正在重新连接此聊天…",
"pairHost": "配对主机以查看智能体聊天记录。",
@@ -17655,7 +17656,9 @@
"notices": {
"compaction": "上下文压缩",
"details": "细节",
"plan": "计划"
"plan": "计划",
"historyRepaired": "此聊天的部分历史记录无法加载。",
"historyItemTooLarge": "聊天的这一部分过大,无法显示。"
},
"providerFrame": {
"byteLength": "{{value0}} 字节"
@@ -17690,9 +17693,9 @@
"stateBlocked": "被阻止",
"stateDone": "完毕",
"stateIdle": "停止了",
"stateUnverifiable": "无法验证的",
"stateUnverifiable": "暂无近期更新",
"stateUnverifiableCount": "{{value0}} 暂无近期更新",
"reasonWaiting": "需要批准",
"reasonUnverifiable": "没有联系",
"reasonBlocked": "失败的",
"groupAgents": "代理",
"groupShell": "壳",
@@ -17716,12 +17719,12 @@
"idle": "idle",
"failed": "失败的",
"stopped": "停止了",
"unverifiable": "无法验证的",
"unverifiable": "暂无近期更新",
"workingCount": "{{value0}} 工作",
"idleCount": "{{value0}} 空闲",
"failedCount": "{{value0}} 失败",
"stoppedCount": "{{value0}} 已停止",
"unverifiableCount": "{{value0}} 无法验证"
"unverifiableCount": "{{value0}} 暂无近期更新"
},
"startedOne": "踢掉 1 个子代理",
"startedN": "启动 {{value0}} 子代理",
+1 -1
View File
@@ -82,7 +82,7 @@ export const AGENT_SESSION_FAILURE_COPY = {
providerRetrying: '{{agent}} hit a temporary problem and is retrying.',
providerRetryingQuoted: '{{agent}} is retrying: {{detail}}.',
previousExitUnverifiable:
"Orca couldn't confirm {{agent}}'s previous process ended. Messages wait to be sent until Orca confirms it has ended."
'{{agent}} from before may still be running. Your messages will send once it stops.'
} as const
export type AgentSessionFailureCopyId = keyof typeof AGENT_SESSION_FAILURE_COPY
@@ -0,0 +1,25 @@
// Status rows a host writes in its own words. Each names itself with a presentation so a client can
// say it in the reader's language; its `text` stays the words for a client that can't.
import type { AgentJournalPlainStatusItem } from './agent-session-journal-types'
export const AGENT_SESSION_HOST_STATUS_COPY = {
/** A repair skipped rows it could not read. */
'history-repaired': "Part of this chat's history couldn't be loaded.",
/** In place of an item too large for any history page. */
'history-item-too-large': 'This part of the chat was too large to show.'
} as const
export type AgentSessionHostStatusPresentation = keyof typeof AGENT_SESSION_HOST_STATUS_COPY
export function agentSessionHostStatusBody(
presentation: AgentSessionHostStatusPresentation
): AgentJournalPlainStatusItem {
return { kind: 'status', text: AGENT_SESSION_HOST_STATUS_COPY[presentation], presentation }
}
export function isAgentSessionHostStatusPresentation(
presentation: string | undefined
): presentation is AgentSessionHostStatusPresentation {
return presentation !== undefined && Object.hasOwn(AGENT_SESSION_HOST_STATUS_COPY, presentation)
}
+16 -20
View File
@@ -14,7 +14,6 @@ import {
} from './agent-session-refusal-notice'
import {
AGENT_SESSION_WRITE_NOTICE_COPY,
agentSessionNoticeSaysThisChatUnread,
type AgentSessionWriteNoticeSentence
} from './agent-session-write-notice-copy'
import { AGENT_SESSION_REFUSAL_REASONS } from './agent-session-refusal-details'
@@ -63,7 +62,8 @@ const CAUSES: Partial<Record<AgentSessionWireRefusalCode, AgentSessionWriteNotic
agent_session_already_resolved: 'questionChanged',
// Send preparation, for any failed open; the code names nothing else.
agent_session_journal_unreadable: 'historyUnreadable',
// On these writes, only an older host, or the phone reading an unknown method.
// With no reason: a method the host doesn't know, or a host from before reasons. Either way an
// older Orca runs the chat.
structured_agent_session_unsupported: 'unsupported'
}
@@ -341,6 +341,18 @@ describe('the notice for every reason a host names', () => {
}))
)
// An unsupported location or agent, or no chat host, is not fixed by updating Orca. A read asked
// for nothing "this" could name, so it says only that the history didn't load.
it('says an unsupported write only is not available when the host names why', () => {
for (const { failure, write, parts, cell } of cells) {
if (failure.code === 'structured_agent_session_unsupported') {
expect(parts, cell).toEqual(
write === 'read-history' ? ['notDoneReadHistory'] : ['notAvailable']
)
}
}
})
it('has words for every reason', () => {
for (const { words, cell } of cells) {
expect(words, cell).toBeDefined()
@@ -428,7 +440,8 @@ describe('the notice for every reason a host names', () => {
}
const notDone = parts.filter((part) => typeof part === 'string' && part.startsWith('notDone'))
const answeredAway = write === 'answer' && parts.includes('questionChanged')
const unsupported = failure.code === 'structured_agent_session_unsupported'
const unsupported =
failure.code === 'structured_agent_session_unsupported' && write !== 'read-history'
const saysNotDone =
write === 'read-history' && failure.code === 'agent_session_journal_unreadable'
expect(notDone, cell).toEqual(
@@ -580,23 +593,6 @@ describe('a chat whose history the host could not open', () => {
).toEqual(['historyUnreadable'])
})
// A pane headed by such words drops its own "didn't load", so each one must count.
it("knows which read notices already say this chat's history did not load", () => {
for (const failure of [...FAILURES, ...REASONED]) {
for (const retryControl of [false, true]) {
const parts = agentSessionWriteNoticeParts(failure, 'read-history', { retryControl })
// "Chats were saved by a newer Orca" is about every chat, not this one.
const notAboutThisRead =
mayHaveRun(failure) ||
codeOf(failure) === 'structured_agent_session_unsupported' ||
parts.includes('savedByNewerOrca')
expect(agentSessionNoticeSaysThisChatUnread(parts), JSON.stringify(parts)).toBe(
!notAboutThisRead
)
}
}
})
it('says only that the history did not load for any other read refusal', () => {
expect(agentSessionReadHistoryRefusalParts('agent_session_ownership_unknown')).toEqual([
'notDoneReadHistory'
+11 -4
View File
@@ -206,11 +206,14 @@ const REASON_WORDS = {
journalUnavailable: causeWords('historyUnavailable', 'retry', 'tryAgain'),
journalWrittenByNewerOrca: causeWords('savedByNewerOrca', 'updateOrca', 'updateOrcaToKeepUsing')
},
// Thrown, so a client meets these only as an RPC error; the code's words stand.
// Thrown, so a client meets these only as an RPC error. The code's own words ask for an update,
// which only a method the host doesn't know proves; no reason here means an older Orca. An
// unsupported location or agent, or no chat host, is not fixed by updating, and a client missing
// the capability words this with its own older copy.
structured_agent_session_unsupported: {
clientCapabilityMissing: codeWords('updateOrca'),
hostDisabled: codeWords('hostFinding'),
hostUnsupported: codeWords('updateOrca')
clientCapabilityMissing: causeWords('notAvailable', 'hostFinding'),
hostDisabled: causeWords('notAvailable', 'hostFinding'),
hostUnsupported: causeWords('notAvailable', 'hostFinding')
},
agent_session_owner_restart_failed: {}
} satisfies {
@@ -235,6 +238,10 @@ function causeParts(
cause: AgentSessionWriteNoticeSentence,
write: AgentSessionWriteKind
): AgentSessionWriteNoticeSentence[] {
// Says the request didn't happen, but a read asked for nothing "this" could name.
if (cause === 'notAvailable') {
return write === 'read-history' ? [NOT_DONE[write]] : [cause]
}
const saysNotDone =
(write === 'read-history' && AGENT_SESSION_HISTORY_UNREAD_CAUSES.has(cause)) ||
(cause === 'questionChanged' && write === 'answer')
+10 -18
View File
@@ -17,6 +17,7 @@ export const AGENT_SESSION_WRITE_NOTICE_COPY = {
notDoneReadHistory: "This chat's history couldn't be loaded.",
notDoneSend: 'Your message was not sent.',
tryAgainComposerSend: 'Send it again.',
messageNotSaved: "Couldn't save your message.",
notDoneStop: "The agent wasn't stopped.",
notDoneStopTask: "The background task wasn't stopped.",
notDoneStopTasks: "The background tasks weren't stopped.",
@@ -27,15 +28,19 @@ export const AGENT_SESSION_WRITE_NOTICE_COPY = {
restartFailed: "The agent couldn't restart.",
capacity: 'Orca has received too many requests in the last day.',
outcomeUnknown: "Orca couldn't confirm what happened. Check the chat.",
sendOutcomeLost:
"Orca couldn't confirm your message reached the agent. Check the chat, then send it again if needed.",
questionChanged: 'This question was already answered or has changed.',
historyUnreadable: "Orca couldn't read this chat's saved history.",
historyUnusable: 'Unable to load this chat.',
historyUnavailable: "Orca couldn't open this chat's history right now.",
savedByNewerOrca: 'Chats were saved by a newer Orca.',
updateOrcaToKeepUsing: 'Update Orca to keep using them.',
unsupported: "The Orca running this chat doesn't support this. Update Orca, then try again.",
unsupported:
'This needs a newer Orca on the computer running this chat. Update Orca there, then try again.',
notAvailable: "This isn't available in this chat.",
unreachable: "Orca couldn't reach the agent.",
recordFailed: "Orca couldn't record it in this chat's history.",
recordFailed: "Orca couldn't save this to the chat's history.",
conversationCleared: 'This conversation has been cleared.',
openCurrentConversation: 'Open the current conversation to continue.',
clearUnfinished: "The last /clear didn't finish.",
@@ -49,19 +54,19 @@ export const AGENT_SESSION_WRITE_NOTICE_COPY = {
answerFirst: 'Answer the question or approval first.',
backgroundTasksRunning: 'Background tasks are still running.',
waitForBackgroundTasks: 'Wait for the background tasks to finish.',
messagesUnsettled: "A message you sent earlier hasn't been confirmed yet.",
messagesUnsettled: "A message you sent earlier isn't confirmed yet.",
settleEarlierMessage: 'Wait for your earlier message to go through, or retry it.',
optionRejected: "The agent didn't accept this setting.",
goalsUnsupported: "This agent doesn't support goals.",
agentRefused: 'The agent turned this down.',
ownerUnproven: "Orca hasn't confirmed that this chat's previous agent stopped.",
ownerUnproven: 'The previous agent in this chat may still be running.',
reopenChat: 'Reopen the chat to check again.',
terminalAgentHoldsChat: TERMINAL_AGENT_HOLDS_CHAT,
quitTerminalAgent: QUIT_TERMINAL_AGENT,
hostReconciling: 'Orca is still checking on this chat after restarting.',
waitMoment: 'Wait a moment.',
recordUnreadable: "Orca couldn't read this chat's saved state.",
chatNotFound: 'The Orca running this chat has no record of it.',
chatNotFound: "Orca can't find this chat.",
startNewChat: START_NEW_CHAT,
tryAgain: 'Try again.'
} as const
@@ -84,16 +89,3 @@ export type AgentSessionWriteNoticePart =
* again. */
export const AGENT_SESSION_HISTORY_UNREAD_CAUSES: ReadonlySet<AgentSessionWriteNoticeSentence> =
new Set(['historyUnusable', 'historyUnavailable', 'historyUnreadable', 'savedByNewerOrca'])
/** Whether these words already say this chat's history didn't load, so a pane headed by them need
* only add that it keeps trying. "Chats were saved by a newer Orca" names no one chat for "it". */
export function agentSessionNoticeSaysThisChatUnread(
parts: readonly AgentSessionWriteNoticePart[]
): boolean {
return parts.some(
(part) =>
typeof part === 'string' &&
part !== 'savedByNewerOrca' &&
(part === 'notDoneReadHistory' || AGENT_SESSION_HISTORY_UNREAD_CAUSES.has(part))
)
}
-8
View File
@@ -19,20 +19,12 @@ export const NATIVE_CHAT_EMPTY_STATE_COPY = {
title: 'Could not load conversation',
subtitle: 'The transcript could not be read. Toggle back to the terminal to keep working.'
},
// The structured chat's read reconnects on its own; the terminal-backed read does not.
retryingError: {
title: 'Could not load conversation',
subtitle: 'The transcript could not be read. Orca keeps trying to load it.'
},
notAgent: {
title: 'No conversation here',
subtitle: 'This terminal is not running a recognized coding agent.'
}
} as const satisfies Record<string, NativeChatEmptyStateCopy>
/** The retrying half alone, under a title that already says the history didn't load. */
export const NATIVE_CHAT_KEEPS_TRYING_COPY = 'Orca keeps trying to load it.'
/** Resolve the empty-state copy with the agent label substituted for `{{value0}}`.
* For platforms without an i18n layer (mobile). */
export function formatNativeChatEmptyStateCopy(
@@ -33,9 +33,16 @@ import {
export type StructuredAgentSessionSendDisposition = {
entries: StructuredAgentSessionOutboxEntry[]
/** Only for an outcome with no entry left to carry it; a kept entry holds its own failure. */
error: string | null
error: AgentSessionWriteNoticePart[] | null
}
/** A message this client couldn't store to send; the composer's draft or the row's Retry still
* has it. */
export const STRUCTURED_AGENT_SESSION_OUTBOX_NOT_SAVED: readonly AgentSessionWriteNoticePart[] = [
'messageNotSaved',
'tryAgain'
]
type SendDispositionInput = {
entries: readonly StructuredAgentSessionOutboxEntry[]
entry: StructuredAgentSessionOutboxEntry
@@ -247,7 +254,7 @@ export function disposeStructuredAgentSessionSendResult(
if (refusedRedelivery(input.entry, submission)) {
return {
entries: dropEntry(input),
error: 'Message delivery is unconfirmed and Orca will not send it again'
error: ['sendOutcomeLost']
}
}
if (submission.dispatchState === 'accepted') {