Files
orca/mobile/src/session/session-write-reply-schema.ts
T
Brennan Benson 64e544a442 feat(mobile): Resume in New CLI from a phone chat tab (#29783)
* feat(mobile): Resume in New CLI from a phone chat tab

A chat tab on the phone had only Close in its long-press menu, so a chat
that would not load left no way to reach its conversation. The phone now
offers Resume in New CLI, like the desktop tab menu: the chat's host
prepares the conversation and the phone opens a copy in a new terminal tab.

The resume and fork startup builders move to src/shared so the desktop and
the phone build the same command.

* test(mobile): type the fake terminal create instead of casting its calls

* fix(mobile): pass the host's fork source as is; match the quoted args in the test

* test(mobile): give the fake host client the untyped mock the other client fakes use

* style: format the shared resume builder and phone fork files

* Fix mobile chat CLI fork creation on owning host

* Fix mobile chat fork lint and split create owner helpers

* Keep chat fork beside phone anchor across tab groups

* Combine mobile terminal runtime type imports

* Repair mobile create CI contracts after chat fork extraction

* Classify chat fork preflight refusals and preserve mobile create recordings

* Keep route-owned create cleanup quiet after unmount

* Preserve typed runtime repair refusal in chat fork preflight

* Preserve checked store through mobile chat fork preflight

* Register mobile chat fork runtime fixture before test setup

* Remove unused mobile create recording value

* style(mobile): format the chat fork create files; move fork reply messages into the fork feedback module

Formatting pushed the create hook past its line limit, so the fork-only reply message, partial-success warning and host capability check now live beside the other fork messages. Same order and copy as before.

* Keep quick command mutation aligned with formatted request

* Keep mobile chat CLI forks in terminal view

* Pass mobile chat resolution its existing controller arguments

* Match the mobile create fixture mock layout

* Keep mobile chat CLI forks open when placement is unavailable

* Brace placement contract fixture checks

* test(mobile): keep the prompt controller mock under its line limit after the main merge

* Expose pane admission in the runtime test fixture type

* Complete the chat fork contract controller fixture
2026-10-16 18:10:24 -07:00

47 lines
2.3 KiB
TypeScript

import { z } from 'zod'
import { salvagedOptional } from '../../../src/shared/zod-salvage'
// The session screen's writes: terminal input from native chat and the image surfaces, the tab
// strip's rename/close/activate, the New Tab terminal create, the terminal menu's display-mode
// toggle, the markdown tab save and the worktree review-notes write.
// Checked against the terminal-send envelope src/main/runtime/rpc/methods/terminal.ts answers with
// and RuntimeMarkdownSaveTabResult in src/shared/mobile-markdown-document.ts.
/**
* The six writes whose reply body no call site reads.
*
* Rename, close, tab-close, focus, tab-activate and the review-notes write are all decided by the
* acceptance verdict alone — use-mobile-session-close-actions.ts:50/76/113 read `accepted` and
* nothing else, the two activation sends are never interpreted at all, and
* use-mobile-session-diff-comments.ts:53 discards the interpretation. Declaring a member on any of
* them would be a requirement with no reader behind it.
*/
export const sessionWriteUnreadReplySchema = z.unknown()
/**
* The terminal tab a New Tab create answers with.
*
* `id` is required and non-empty: use-mobile-session-terminal-create-actions.ts reads it unguarded
* three times — the pending-active ref, `setActiveSessionTabId` and the strip's dedupe — so a reply
* without one seated a tab the strip could never address again. `type` is a literal because the
* whole tab is spread into the strip, where `type` picks the arm: a create answering a markdown or
* browser tab must be refused, not rendered as a terminal with no handle. `terminal`, `title` and
* `terminalTheme` stay optional and keep main's own guards (`typeof === 'string'`, `||`, `??`)
* behind them, and unknown members pass through so the strip keeps what a newer host sends.
*/
export const sessionCreatedTerminalTabSchema = z
.looseObject({
tab: z.looseObject({
type: z.literal('terminal'),
id: z.string().min(1),
terminal: salvagedOptional('terminal', z.string().nullable()),
title: salvagedOptional('title', z.string()),
terminalTheme: salvagedOptional('terminalTheme', z.unknown())
})
})
.transform((reply) => reply.tab)
export function createdTerminalDesktopWarning(raw: unknown): boolean {
return z.looseObject({ warning: z.literal('desktop-tab-not-shown') }).safeParse(raw).success
}