Skip to content

sync: upstream a45bd06eac into swaplabs/main - #47

Merged
kiwidream merged 10 commits into
swaplabs/mainfrom
main
Oct 22, 2026
Merged

kiwidream merged 10 commits into
swaplabs/mainfrom
main

Conversation

@kiwidream

@kiwidream kiwidream commented Oct 22, 2026 •

Copy link
Copy Markdown
Files Added Deleted Net
Test 33 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​1609 $\color{#cf222e}{\Huge{\mathbf{−}}}$​20 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​1589
Prod 111 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​2662 $\color{#cf222e}{\Huge{\mathbf{−}}}$​932 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​1730

ELI5

Upstream Orca moved forward 10 commits since this morning's sync (#38). This PR merges them into swaplabs/main. They merge cleanly: none of them collides with a change the fork carries.

What Changed

Upstream range ec5eaf96c1..a45bd06eac, 10 commits (compare). The head is the fork's mirror main, so the PR adds no fork commit; GitHub makes the merge commit.

Upstream's 10 commits:

  • 726c572: a long paste is attached as a text file, and a too-large message is flagged before it is sent;
  • 97d4518: Session History shows per-agent reading progress;
  • 199fc7b: a Pi chat shows the turns made outside it;
  • f8aa685, c941099: phone and paired-desktop layout changes, dark behind the layout-owner build;
  • 9613563: one commit step for both terminal start lanes (pty refactor);
  • a45bd06, 2366072, de2700d, 6af2163: refactors that colocate the stored-agent contract, browser cookie validation and native-chat command rules.

Why

Daily upstream sync.

Linked Issue

N/A: daily upstream sync.

Visual Proof

N/A: upstream code only. The fork changes no line here.

Testing

  • Trial merge: git merge-tree of a45bd06eac into swaplabs/main (66ee345e87) is clean, with 0 conflicts.
  • Clean-merge risks: the only files both sides changed since ec5eaf96c1 are the 8 locale catalogs; each merged catalog parses with no duplicate key. No fork file references a module upstream moved or deleted in this range.
  • Typecheck of the trial merge: pnpm tc, all four projects, "No type errors".
  • Fork tests: every unit test file the fork adds or changes, 116 files: 1,457 passed, 8 skipped. The fork's orca-runtime-tests/worktree-ps-agent-row-dismissal.spec.ts runs inside orca-runtime.test.ts, which passes apart from one upstream test this host refuses to sign a fixture commit for (repository-project-operations-part-02.spec.ts).
  • Changed-code gate: check-changed-code-quality.mjs with ORCA_SYNC_PR_DIFF_BASE=a45bd06eac: 0 findings across 330 fork files.

AI Disclosure

Prepared by the "Orca upstream sync" automation (Claude Code, Claude Opus 5.5).

Agent skill upstream boundary

  • Not applicable: this PR merges upstream's history; it adds no agent skill.

Notes

orca-fork-ops status for swaplabs/main (66ee345e87) against upstream a45bd06eac, before this merge:

No upstream release tags were missing from the fork.

🤖 Generated with Claude Code


View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need.

nwparker and others added 10 commits October 22, 2026 07:57
* fix(session-history): freeze scan workload before checking candidates

* Show per-agent Session History reading progress

* Keep reduced-motion behavior in progress primitive

* test(session-history): handle native fixture path separators

* test(session-history): control bucket fixture enumeration order

* test: cover finished empty history progress rows

* test(session-history): preserve scoped fixture timestamps

* fix(session-history): repair integrated test and locale catalog

* ci: refresh history checks against current main

* ci: refresh history checks after catalog repair
…mark reach a server's own layout (dark, layout-owner build only; stacked on stablyai#31714) (stablyai#31869)

* wip(h5c-1): server workspace layout intents go to the server's main; positions per stream

* wip(h5c-1): e2e without a cast

* fix(h5c-1): a routed move never moves here; routed callers read the server's stream; a restarted main starts its positions over

* fix(h5c-1): layout-apply-send back under its line limit

* test(h5c-1): a routed answer reaches the handler with the server's stream key

* fix(terminal): a CLI or phone close through main keeps a pinned tab, as the window-era close did

* feat(terminal): main applies phone closes, renames, pane titles, sleeps and agent-launch tabs itself, with or without a window (layout-owner build)

* fix(terminal): a tab close through main also ends the tab's dormant SSH relay session; agent-launch tabs main writes keep the window's focus rule, prompt and outcome (layout-owner build)

* feat(terminal): main starts a new tab's first terminal under the window's ids, and the pane attaches to it (layout-owner build)

* test(terminal): tests that counted the window's create calls or publications see main's start of a new tab

* fix(terminal): an account-switch restart binds only into a pane main's layout holds, with no window tree (layout-owner build, L197)

* test(terminal): the new-tab start tests hold no type assertions; the start's ledger row is L252

* fix(terminal): a new tab's pane attaches to any terminal main binds it to, and a failed start keeps the tab's startup

* test(terminal): a Claude account-switch restart's tests draw the pane main holds

* fix(terminal): a launch pane main takes back still tells the window why, which drops its hold and prompt; a rename through main answers the name it stored (layout-owner build)

* fix(terminal): a new tab another owner's terminal won attaches to it, and a failed start's startup never runs twice

* fix(terminal): main's pane close reads its stop result as a type only, so no runtime module loads before its base class

* fix(terminal): a phone's close through main drops the closed rows from what phones list at once, not at the window's next publish (layout-owner build)

* chore(terminal): mark the phone-close row drop interim (ledger L260)

* test(worktree-create): a new local tab's startup counts as run when main starts it (layout-owner build)

* test(worktree-create): record main's starts without a type assertion

* fix(terminal): a tab closed before main declines its start gets no startup back (layout-owner build)

* fix(terminal): the window's new tab starts in main when its own window asks over IPC, which labels itself a client (layout-owner build)

* feat(h5c-2): a paired desktop's writes to a server workspace its frames draw land in that server's main (layout-owner build)

- Routes setTabProps, setChatPane and non-terminal renameTab to the server's main as layout.apply;
  closes, starts, sleeps and terminal names keep the RPCs that main now applies (session.tabs.close,
  terminal.close, terminal.split, session.tabs.createTerminal, terminal.rename). A window-style
  closeTabs is never routed: main takes it as already stopped and stops nothing.
- A close the server refuses (a pinned tab) redraws the workspace from the server's frames, so the
  window's optimistic drop never outlives the answer; same for a refused pane close.
- A frame-drawn workspace keeps none of the mirror's ledgers (L240): no tab-id mappings, close or
  focus intents; they stay only for servers without layout.intents.v1 until the Phase 2 cutoff.
  Its pane-geometry push is skipped (it goes as intents); pane renames still push.
- e2e: paired-desktop-server-layout-writes covers close (layout, session.tabs, terminal stop),
  rename, split and new tab on a windowless switch-on serve.

* fix(h5c-2): an undelivered close of a frame-drawn tab is held for replay; a launch-agent clear stays off the server route

* refactor(h5c-2): the undelivered-close test moves to its own module, keeping the lifecycle file under its line cap

* fix(layout): a paired desktop's pane detach, handle-less split and default-tabs mark on a server workspace go to the server's main (layout-owner build, L272/L276/L275)

* fix(layout): a pane a paired server's main detached takes the terminal the server's frame drew, not the desktop's possibly stale one (layout-owner build, L272)

* test(layout): the detach test resets the drawn layout after each case

* test(layout): drop the in-test reset the afterEach now covers
* feat(native-chat): a Pi chat shows the turns made outside it

When a Pi chat's agent opens its conversation, Orca asks Pi for the entries
saved after the point the chat last accounted for (get_entries since that
entry id) and adds the terminal's turns once, in Pi's order, under the
"Continued outside this chat." divider. That point is saved on the
conversation handle after each chat turn and at each open. When the chat's
own unsaved work and the terminal's share a suffix that can't be split
(Orca died mid-turn), that suffix is skipped rather than duplicated, and
later terminal turns show normally. A Pi reply over the 8 MiB line limit now
fails only the optional read that asked for it instead of ending the chat.

* refactor(native-chat): share the catch-up open runner across agents

The Codex catch-up's agent-neutral parts move to one module: the open-time
envelope (its own deadline and cancellation, never gating the open), the
bounded staged batch, the single settlement commit that re-checks the
journal before writing, and the "Continued outside this chat." divider.
Codex keeps only how it reads its saved turns and what the chat already
holds. No behavior change; the other agents' catch-up builds on this
instead of copying it.

* refactor(pi): catch up through the shared catch-up runner instead of a copy

* chore: remove unrelated pnpm executable metadata

* refactor(native-chat): the catch-up runner reads the chat's view only when the agent asks

* refactor(pi): read the chat's view through the runner's view()

* chore: remove regenerated pnpm executable metadata

* fix(native-chat): the catch-up deadline also bounds waiting for its commit to be written

A journal write barrier that never settled after the import was admitted held
the provider's open past the catch-up deadline. The wait now ends with the
deadline or the open's cancellation, like every other step.

* test(pi): check Pi's session entry ids and get_entries since against the pinned real Pi

* test: type the history catch-up sink fixture without assertions

* fix(native-chat): bound and revoke history preparation

* test: the runner's journal fake answers the cursor Pi's view reads

* feat(native-chat): validate expected saved items during history catch-up

* fix(pi): preserve outside history and optional read recovery

* test(pi): fail RPC smoke promptly when the child stops
…i#31596)

* refactor(pty): one commit step for both terminal start lanes

The window's pty:spawn commit and the host runtime controller's commit were two
copies of the same job (ownership, binding save, registration, size, hidden
mark, publication, reservation settle), so every fix had to land twice.

Both lanes now call one commitPtySpawn and supply what differs as data: the
binding each lane builds, its registration facts, pane key and aliases, the
Codex launch env and memory session id. Three inputs still select behavior:
hiddenDelivery (the renderer-scoped mark is refreshed here; the runtime-owned
mark stays at the host runner's await boundary), earlyExit (the window drains an
exit during start, the host throws) and joinPayload (what reservation joiners
and the caller receive).

One publication order for both lanes: note commit, Codex pane account, renderer
hidden mark, handle, size set with reflow, old-id size cleanup, serializer
readiness, restore records, command detector, telemetry, pane key and aliases,
memory registry, resume identity, inventory, Codex lifecycle, settle. Every
moved step was audited for read/write independence. Stable-owner adoption and
the adopted-claim publication set are unchanged.

Also pins that an agent launch whose record cannot open takes its tab back and
spawns nothing, and re-labels three TEMPORARY comments to their current owners.

* fix(pty): preserve adopted claim return scheduling

* fix(pty): keep shared commit reply types precise
…lects the moved tab as the window does (dark, layout-owner build only) (stablyai#31924)

* fix(session-tabs): a tab main's groups lack keeps the place its writer listed it, so a headless phone lists a chat first (layout-owner build, L283)

On a host with no window, a rebuild writes its rows under a group id main's layout lacks. The row
writer's reorder treated every row of that group main does not hold (a chat, a browser page) as a
stray and appended it to the active group, so a chat the user moved first came back last, in both
the groups and the tab list. Strays now go after the id they follow in the writer's order (else
first), the same rule a group main does hold already used; the window publication path shares it.

* fix(session-tabs): a phone's split move and its create after a split tab go through main's layout on a windowless host (layout-owner build, L288, L289)

On a host with no window, a phone's split move only rewrote the runtime's own rows, and its new
terminal anchored after a split tab reached main's layout with no anchor. Every write of the rows
takes main's groups and order, so the split collapsed back into one group and the new tab landed
after the trailing tab. The split is now main's splitGroup command (the moved tab selected in its
new group, as a desktop drop), and the create passes the anchor's tab to main's createTerminalTab,
as a window's create does. An older phone sends no anchor and still gets the end.

* test(session-tabs): type the split move's async reply in the dispatch stub, and read an optional group map (L289)

* fix(session-tabs): a phone's split goes to main's layout only for a tab main holds, as the window sends it (layout-owner build, L289 review)

With the Store's write notices, today's runtime-only split already reached main's groups through
the session write; what it lost on a windowless host was the selection: the moved tab and its new
group did not become the active ones, as they do after a window's drop. The split stays main's
splitGroup for a tab main's groups hold, with the window's checks: a drop main's groups make a
no-op sends nothing (the window's pane-column rule, now in shared), and a refusal is dropped
silently. A row main does not hold (a chat or browser tab) splits as today, with no error. The
create anchor and its layout-owner hand-off move to their own module. The surface test fixture's
session writes now notify listeners, as the Store's do.

* chore(session-tabs): log when main refuses a phone's split, as the window logs its own refusals

* fix(session-tabs): a phone's terminal created after a chat or browser tab main holds lands after it, in its group (layout-owner build, L288 follow-up)

Since browser and chat tabs went main-first, main's layout holds them, but a windowless create
handed main the row id (agent-session:<id>), which is not main's tab id, so main put the new tab
at the end of its first group. The anchor is now resolved to main's own tab, as a window's create
names it; main's read of a workspace's layout is one method the tab-move path shares.

* fix(session-tabs): one move path: main's tab move selects a split tab in its new group and skips the window's no-op split (layout-owner build, L289)

A phone's split now goes only through main's tab move (stablyai#31812); this branch's own split routing is
gone. For a split only, main's tab move now does what the window's drop does: it sends nothing for
a drop that changes nothing (the window's pane-column check, reused), and after main applies the
split it selects the moved tab and makes its new group active. Refusals keep that path's codes.

* fix(session-tabs): resolve a create's anchor to main's tab inside the tab-move module, so only the old-client boundary's listed importers read old-client rows (L288 follow-up)

* perf(session-tabs): read main's layout for a create's anchor only when main places it, so switch-off and appending phones skip that load

* fix(session-tabs): restore the row-to-tab lookup the split selection and create anchor use, after stablyai#31925 dropped its import (layout-owner build, L288, L289)

stablyai#31925 removed the L280 bridge in main's tab move, and with it the move's last use of
resolveSessionTabRow, so it dropped the import; this branch's split selection and create-anchor
lookup still resolve a row to main's tab through it. A row main's layout does not hold is now
refused by main's move (tab_not_found) as stablyai#31925 rules; the phone test asserts that.

* test(session-tabs): the L283 phone-order test opens its chat as the host does and uses a store that announces its writes (layout-owner build)

Since stablyai#31925, main moves every tab of a workspace it owns and refuses a row its layout does not
hold, and since D3b a chat the host opens is saved in main's layout. The test now opens the chat
through the host's publish path and gives the runtime a profile-like store (partitions listed,
writes announced), so the reorder reaches main's layout and its commit reaches the rows.

* test(session-tabs): type the L283 test's profile-like store as the Store contract declares it

* revert(session-tabs): drop the stray-placement change; main's layout holds chats and moves every row now (L283)

Since D3b a chat the host opens is saved in main's layout, and since stablyai#31925 a phone cannot move a
row main does not hold, so the rebuilt-group stray this placed no longer reaches a phone; the
remaining strays are brief, and appending them gives the same order. withStrayIn and its callers
return to main's behaviour, with its unit test. The phone-order spec stays as a guard that a
windowless chat moved first stays first after a rebuild.
…fore it is sent (stablyai#30398)

* fix(native-chat): attach a long paste as a text file, and say a message is too large before it is sent

A paste of 32 KB or more (or one that would push the message past the 256 KB
send limit) into a structured chat is saved as a file the agent reads, through
the same pending-chip and @path route a dropped file takes: Orca's paste folder
here, or the chat's attachment store on its paired server. Claude gets the paste
folder as an allowed directory. A message still over the limit is handed back
before it is sent, on desktop and the phone, with words naming the limit instead
of the generic 'not sent, send it again'.

* test: Claude launch parity snapshot grants the paste folder

* fix(native-chat): a long paste replaces the selection at once and settles in place

A long paste now puts an inline Pasted text pill where it went, replacing any
selected text immediately, and the saved file (or inline text, or the original
selection on failure) replaces that same pill when the save finishes, in its own
chat's draft. Nothing reads the caret later, so order and position hold. The
paste never joins the image row, so image numbers are unchanged. The size check
measures the real message body with the selection replaced and images included.
A server that keeps no files takes the paste inline when the message still fits.
A pending paste a restart lost shows Paste not saved, and Send says so.

* fix(native-chat): type the pasted-text parser and the composer's missing draft scope

* fix(native-chat): admit paste references before goals and commands

* fix(native-chat): resolve staged paste paths without editing the draft

* fix(native-chat): consolidate paste imports for code quality checks

* fix(native-chat): retain checked string types for saved paste paths

* fix(native-chat): retain completed paste facts through history and delimit paths

* fix(native-chat): keep structured question rendering within file limit

* fix(native-chat): pasted or inserted text that looks like a paste token stays literal

* fix(web): keep the browser's UI bridge under the file length limit

* style: format the files stablyai#29982 merged unformatted

* fix(native-chat): retain oversized drafts before send admission

* fix(native-chat): keep the composer handle's merge resolution to main's destructure
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 22, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-22T17:52:21.684212Z a45bd06 PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@kiwidream
kiwidream merged commit 412ee33 into swaplabs/main Oct 22, 2026
70 of 71 checks passed
@kiwidream

Copy link
Copy Markdown
Author

Merged by the "Orca upstream sync" automation under SwapLabsInc/SwapLabs#223's rule, where CI no longer gates a sync.

  • Gate: a trial merge onto cfe8685da9, revalidated after LOCAL: post a Slack note when a fork build publishes or fails #48 moved the base:
    • typecheck clean;
    • 117 of the fork's 118 test files pass; the one failure is upstream's repository-project-operations-part-02.spec.ts, whose fixture commit this host refuses to sign;
    • the changed-code gate reports 0 findings.
  • Checks at merge: 45 pass, 21 skipped, and 1 fails: WSL orcad E2E / WSL2 distro (ubuntu-24.04), the known WSL runner-setup failure in upstream's job. None are pending.
  • Reviewers: Codex finished with no findings. Tenki never reviewed; it has reviewed no PR since 2 October (SwapLabsInc/SwapLabs#225). There are no review threads.
  • Slack: the merged note was posted with the build run, 39830490153.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants