Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
cdd0b7a
fix(native-chat): stop flashing a reconnecting line on a stream drop …
brennanb2025 Oct 7, 2026
825d7bd
test(vitest): run agent-launch-instant-tab in the SQLite runtime proj…
Jinwoo-H Oct 7, 2026
9fdd90f
feat(orchestration): a native chat can be a dispatch worker, like a t…
brennanb2025 Oct 7, 2026
b3b6c5d
Give native chat names one source for tabs, sidebar and AI Vault (lis…
brennanb2025 Oct 7, 2026
7a59aa3
fix(worktrees): decide setup before git worktree add on the host's cr…
brennanb2025 Oct 7, 2026
0bdcaf3
fix(claude): start a Claude chat with its saved options and send the …
brennanb2025 Oct 7, 2026
5b1bb78
[STA-6940] Prepare file drops for element owners (PR 2 of 6) (#25748)
brennanb2025 Oct 7, 2026
923ef7c
fix(native-chat): refuse a Command that can't run instead of silently…
brennanb2025 Oct 7, 2026
70c6876
fix(rovo): detect and launch Rovo Dev through acli (#25981)
Jinwoo-H Oct 7, 2026
7c9c3d4
fix(lint): bring structured-agent-session-host back under max-lines (…
Jinwoo-H Oct 7, 2026
d3e1494
test: bound memory used by the runtime Electron audit (#26049)
nwparker Oct 7, 2026
55c86ce
refactor(terminal): report breaches of the two layout invariants; add…
Jinwoo-H Oct 7, 2026
dff65d5
feat(claude): prepare account profiles and shared history (Step 1 of …
brennanb2025 Oct 7, 2026
bfcbcfd
refactor(terminal): move a dragged-out pane in main before the window…
Jinwoo-H Oct 7, 2026
9d8e363
chore(relay): move Asia cell c34 to the general same-cap lists after …
Jinwoo-H Oct 7, 2026
d1fe11c
refactor(terminal): carry pane placement on terminal start, report-on…
Jinwoo-H Oct 7, 2026
76d480f
Fix unsafe test fixtures and the Bun version pin (#26051)
nwparker Oct 7, 2026
7f83e0a
fix(serve): keep every persisted terminal on the phone after serve re…
Jinwoo-H Oct 7, 2026
8464b15
Claude account profiles: dormant routing and consumers (Step 2 of 4) …
brennanb2025 Oct 7, 2026
2f49377
feat(native-chat): Grok as a structured chat over the Agent Client Pr…
brennanb2025 Oct 7, 2026
64bb937
Claude account profiles: dormant WSL guest setup (Step 3 of 4) (#24384)
brennanb2025 Oct 7, 2026
358e4f9
fix(claude): share account history on Windows with junctions and a sa…
Jinwoo-H Oct 7, 2026
988f87e
fix(native-chat): move the attachment failure words to their own modu…
brennanb2025 Oct 7, 2026
d0b0f13
fix(native-chat): hold a queued message until the turn ahead opens (f…
brennanb2025 Oct 7, 2026
6bc5291
fix(orchestration): word the worker brief for a chat worker (#26036)
brennanb2025 Oct 7, 2026
acea59c
refactor(native-chat): remove the records-file and per-chat journal i…
brennanb2025 Oct 7, 2026
0acf039
Keep native contracts on Node and wait for Git upgrade completion (#2…
nwparker Oct 7, 2026
eaceac0
feat(native-chat): searchable model picker (#26011)
AmoabaKelvin Oct 7, 2026
8f42451
feat(native-chat): suggest files on @ and name the triggers (#26017)
AmoabaKelvin Oct 7, 2026
f31d7a4
fix(acp): fail the journal open through AgentSessionJournal.open in t…
Jinwoo-H Oct 7, 2026
a3ecee0
feat(native-chat): one notice card above the composer for the chat's …
brennanb2025 Oct 7, 2026
98e5f5c
Update README downloads badge
github-actions[bot] Oct 7, 2026
c8dcffc
fix(native-chat): pass the @-mention file props in the composer resum…
Jinwoo-H Oct 7, 2026
9ed34f3
Merge remote-tracking branch 'origin/main' into OrcaWin/node-rt-phase…
Oct 7, 2026
4422d58
fix(sync): reconcile main's ratchet bundling and cold-serve hydrate w…
Oct 7, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
2 changes: 1 addition & 1 deletion .github/CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ Thanks for contributing to Orca.

## Local Setup

Install Node 24, pnpm, and the Bun version in [`config/bun-version`](../config/bun-version).
Install Node 24, pnpm, and the Bun version in [`config/.bun-version`](../config/.bun-version).
`pnpm test` runs Vitest on Bun, with Node workers for runtime contracts such as SQLite,
native PTYs, socket liveness, and V8 memory behavior. `pnpm test:node` runs the same suites
entirely on Node. Builds and dependency installation still use Node and pnpm.
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/pr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,7 @@ jobs:
if: needs.code_paths.outputs.static_analysis == 'true'
uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0
with:
bun-version-file: config/bun-version
bun-version-file: config/.bun-version

# Keep each check in its own log while sharing this runner.
- name: Lint
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/unit-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ jobs:

- uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0
with:
bun-version-file: config/bun-version
bun-version-file: config/.bun-version

- uses: actions/download-artifact@v8
continue-on-error: true
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,13 +97,11 @@ describe('production Relay capacity cell admission', () => {
it('admits the same-cap Asia and migration-only cells only under the same-cap allowlist', () => {
for (const cellId of [
'production-gce-c27', 'production-gce-c28', 'production-gce-c29', 'production-gce-c30',
'production-gce-c31',
'production-gce-c31', 'production-gce-c34',
// Migration-only canaries: the US-only capacity rollout never touches them either.
'production-gce-c17', 'production-gce-c18',
// US cells at the Asia shape: the 1,000-cap capacity rollout never touches them.
'production-gce-c32', 'production-gce-c33',
// The migration-only Asia spare.
'production-gce-c34'
'production-gce-c32', 'production-gce-c33'
]) {
const hostname = cellId.slice('production-gce-'.length)
assert.deepEqual(parseProductionCapacityCellArguments([
Expand Down
7 changes: 2 additions & 5 deletions cloud/dev/scripts/relay-production-same-cap-wave.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,15 @@ import { requireSameEvidenceCode } from './relay-evidence-code-provenance.mjs'

// Migration-only by policy: zero hosts and no reservation, so a wave rolls one without
// displacing anybody. It enters and must leave migration-only, never general.
// C34 is an Asia spare that stays migration-only by policy.
export const SAME_CAP_MIGRATION_ONLY_CELLS = [
'production-gce-c17', 'production-gce-c18', 'production-gce-c34'
]
export const SAME_CAP_MIGRATION_ONLY_CELLS = ['production-gce-c17', 'production-gce-c18']

export const SAME_CAP_CELLS = [
'production-gce-c7', 'production-gce-c8', 'production-gce-c9', 'production-gce-c10',
'production-gce-c13', 'production-gce-c14', 'production-gce-c15', 'production-gce-c16',
'production-gce-c19', 'production-gce-c20', 'production-gce-c21', 'production-gce-c22',
'production-gce-c23', 'production-gce-c24', 'production-gce-c25', 'production-gce-c26',
'production-gce-c27', 'production-gce-c28', 'production-gce-c29', 'production-gce-c30',
'production-gce-c31', 'production-gce-c32', 'production-gce-c33',
'production-gce-c31', 'production-gce-c32', 'production-gce-c33', 'production-gce-c34',
...SAME_CAP_MIGRATION_ONLY_CELLS
]

Expand Down
23 changes: 17 additions & 6 deletions cloud/dev/scripts/relay-production-same-cap-wave.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -136,13 +136,11 @@ test('the wave workflow chains exactly ten serial cell jobs', () => {
assert.doesNotMatch(dispatch, /\n cell_11:/)
})

test('lists the C34 spare as migration-only beside C17 and C18, and C30-C33 as general', () => {
assert.deepEqual(
SAME_CAP_MIGRATION_ONLY_CELLS,
['production-gce-c17', 'production-gce-c18', 'production-gce-c34']
)
test('lists only C17 and C18 as migration-only now that C34 is promoted, and C30-C34 as general', () => {
assert.deepEqual(SAME_CAP_MIGRATION_ONLY_CELLS, ['production-gce-c17', 'production-gce-c18'])
for (const cellId of [
'production-gce-c30', 'production-gce-c31', 'production-gce-c32', 'production-gce-c33'
'production-gce-c30', 'production-gce-c31', 'production-gce-c32', 'production-gce-c33',
'production-gce-c34'
]) {
assert.equal(SAME_CAP_CELLS.includes(cellId), true, cellId)
}
Expand Down Expand Up @@ -207,6 +205,19 @@ test('rolls the migration-only cells but never mixes the two classes in one wave
confirmation: `ROLL_RELAY_SAME_CAP ${targetDigest} ${asiaPromoted}`,
canaryRunId: '42'
}).cells, ['production-gce-c30', 'production-gce-c31'])
// C34 is general once its Asia canary promotes it, so a rollback restores it general.
assert.equal(entryAdmission('production-gce-c34'), 'general')
assert.equal(selectorWaveDelta('production-gce-c34'), 2)
const c34Mixed = 'production-gce-c34,production-gce-c17'
assert.throws(() => validateSameCapWave({
mode: 'batch-apply',
cellIds: c34Mixed,
targetDigest,
rollbackDigest,
drainPaceWindowMs: '300000',
confirmation: `ROLL_RELAY_SAME_CAP ${targetDigest} ${c34Mixed}`,
canaryRunId: '42'
}), /all general or all migration-only/)
const c31Mixed = 'production-gce-c31,production-gce-c18'
assert.throws(() => validateSameCapWave({
mode: 'batch-apply',
Expand Down
3 changes: 1 addition & 2 deletions cloud/dev/scripts/relay-same-cap-script-census.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -279,10 +279,9 @@ describe('same-cap roll scripts accept every same-cap cell', () => {
const trusted = SAME_CAP_CELLS.filter((cell) => REHOME_SOURCE_CELLS.has(cell))
// Only a declared rehome source may roll at a trusted protocol at all; the job refuses
// the rest before it plans, and the next test covers them at protocol 0.
// The C34 spare is a rehome source that stays migration-only.
assert.deepEqual(
SAME_CAP_CELLS.filter((cell) => !REHOME_SOURCE_CELLS.has(cell)),
SAME_CAP_MIGRATION_ONLY_CELLS.filter((cell) => cell !== 'production-gce-c34')
SAME_CAP_MIGRATION_ONLY_CELLS
)
for (const [cellId, protocol] of trusted.flatMap((cell) => [[cell, 1], [cell, 3]])) {
const { cap, pool } = cellShape(cellId)
Expand Down
3 changes: 2 additions & 1 deletion cloud/dev/scripts/relay-same-cap-shadow-gate-verdict.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@ export const FLEET_POOL_CELL_IDS = [
'production-gce-c28',
'production-gce-c29',
'production-gce-c30',
'production-gce-c31'
'production-gce-c31',
'production-gce-c34'
]

export const SHADOW_GATE_THRESHOLDS = {
Expand Down
6 changes: 4 additions & 2 deletions cloud/dev/scripts/relay-same-cap-shadow-gate.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -278,6 +278,7 @@ function productionLikeEntries() {
...metricSamples({ cellId: 'production-gce-c29', from: '2026-09-20T20:20:00Z', count: 20 }),
...metricSamples({ cellId: 'production-gce-c30', from: '2026-09-20T20:20:00Z', count: 20 }),
...metricSamples({ cellId: 'production-gce-c31', from: '2026-09-20T20:20:00Z', count: 20 }),
...metricSamples({ cellId: 'production-gce-c34', from: '2026-09-20T20:20:00Z', count: 20 }),
// One director instance's samples from ten minutes before the drain to the window's end.
...directorSamples({ from: '2026-09-20T19:50:00Z', count: 80, payload: {} })
]
Expand Down Expand Up @@ -339,10 +340,10 @@ function gcloudSeam(entries = productionLikeEntries()) {
}
}

test('reads every promoted asia-east2 cell as fleet pool, C31 included', () => {
test('reads every promoted asia-east2 cell as fleet pool, C34 included', () => {
assert.deepEqual(FLEET_POOL_CELL_IDS, [
'production-gce-c27', 'production-gce-c28', 'production-gce-c29', 'production-gce-c30',
'production-gce-c31'
'production-gce-c31', 'production-gce-c34'
])
})

Expand All @@ -363,6 +364,7 @@ test('a healthy roll reads as PASS and names the instance it proved serving', as
'fleetPool:production-gce-c29',
'fleetPool:production-gce-c30',
'fleetPool:production-gce-c31',
'fleetPool:production-gce-c34',
'nonDrain503Budget'
])
assert.deepEqual(report.drain, {
Expand Down
18 changes: 6 additions & 12 deletions cloud/docs/relay-workflows.md
Original file line number Diff line number Diff line change
Expand Up @@ -221,18 +221,12 @@ list because their pool is the US default of 10.

C34 is a sixth Asia cell at the C31 shape in `asia-east2-c`, so the six Asia cells spread 2/2/2. It
was its own topology wave, registered alone as migration-only, and the director was configured with
`cell-ids` set to C34, all on 2026-10-05. It launched as a migration-only spare and now has a
promotion wave of its own, with the same five-minute canary C30 and C31 ran. The canary lands on
C34 because it is the emptiest general Asia cell once promoted. Promotion compares the director's
serving digest and C34's runtime digest with the one `image-digest` input, so C34 must first be
rolled to the director's image. That roll is a same-cap wave that enters and leaves
migration-only, so it moves nobody. C34 stays in the same-cap migration-only list until its promotion
succeeds, because a same-cap job reads a cell's class from that list, not from the selector. It
then moves to the general list and the fleet pool list together, as its own reviewed change,
before any same-cap wave names C34 again. Between promotion and that change, do not run a same-cap
wave on C34; a rollback there would demote it. Do not name it in the multi-target
`promote-general-cell` or `retire-migration-cell` modes, which accept any migration-only cell. It
is a declared rehome source.
`cell-ids` set to C34, all on 2026-10-05. It launched as a migration-only spare and has a promotion
wave of its own, with the same five-minute canary C30 and C31 ran. Promotion compares the
director's serving digest and C34's runtime digest with the one `image-digest` input, so C34 was
first rolled to the director's image as a migration-only same-cap wave. Once its canary promoted
it, it moved to the same-cap general list and the fleet pool list together, so the same-cap job now
rolls it as a general cell and a rollback restores it general. It is a declared rehome source.
Rollback returns
Asia cells to migration-only; it does not destroy the network or use
existing-only. The production topology dispatch remains unavailable until the
Expand Down
2 changes: 1 addition & 1 deletion cloud/infra/terraform/environments/production.tfvars
Original file line number Diff line number Diff line change
Expand Up @@ -450,7 +450,7 @@ relay_gce_cells = {
connection_hard_cap = 3000
connection_unobserved_bound = 60
}
# Asia spare: registered migration-only as a drain landing zone; c completes the 2/2/2 zone spread.
# Sixth Asia cell: launched as a migration-only spare, now general; c completes the 2/2/2 zone spread.
"production-gce-c34" = {
hostname = "c34"
region = "asia-east2"
Expand Down
File renamed without changes.
1 change: 1 addition & 0 deletions config/build-plugins/plain-node-entry-guard.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ const WORKER_THREAD_ENTRY_NAMES = [
'main-thread-hang-watchdog-entry',
'port-scan-command-worker-entry',
'usage-scan-worker-entry',
'claude-profile-setup-worker-entry',
'profile-state-backup-worker-entry',
'profile-state-writer-worker-entry'
] as const
Expand Down
13 changes: 13 additions & 0 deletions config/scripts/build-relay.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import {
RELAY_BUILD_PLATFORMS,
RELAY_VERSION_FILENAME,
RELAY_OPENCODE_SQLITE_READER_FILENAME,
WSL_CLAUDE_PROFILE_HELPER_FILENAME,
relayOptionalArtifactFilenames,
isWindowsRelayPlatform,
relayArtifactFilenames
Expand Down Expand Up @@ -331,6 +332,18 @@ for (const platform of RELAY_BUILD_PLATFORMS) {
.slice(0, 12)
writeFileSync(join(outDir, '.browser-network-version'), `${RELAY_VERSION}+${browserNetworkHash}`)
console.log(`Built WSL browser network relay → ${outDir}/wsl-browser-network-relay.js`)

// Why here, not the relay dirs: only the desktop runs it, inside WSL; SSH hosts never upload it.
await build({
entryPoints: [join(ROOT, 'src/main/claude-accounts/claude-profile-wsl-entry.ts')],
bundle: true,
platform: 'node',
target: 'node18',
format: 'cjs',
outfile: join(outDir, WSL_CLAUDE_PROFILE_HELPER_FILENAME),
minify: true,
define: { 'process.env.NODE_ENV': '"production"' }
})
}

console.log('Relay build complete.')
114 changes: 114 additions & 0 deletions config/scripts/check-runtime-electron-ratchet-graph.test.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
import { afterEach, describe, expect, it } from 'vitest'
import { mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs'
import { tmpdir } from 'node:os'
import path from 'node:path'
import { collectElectronImporters } from './check-runtime-electron-ratchet.mjs'

const roots = []
afterEach(() => {
for (const root of roots.splice(0)) {
rmSync(root, { recursive: true, force: true })
}
})

function fixture(files) {
const root = mkdtempSync(path.join(tmpdir(), 'orca-electron-ratchet-graph-'))
roots.push(root)
for (const [file, source] of Object.entries(files)) {
const absolute = path.join(root, file)
mkdirSync(path.dirname(absolute), { recursive: true })
writeFileSync(absolute, source)
}
return root
}

async function inspectGraph(entries, { separateEntries = false, plugins = [] } = {}) {
let result
const inspect = {
name: 'inspect-electron-ratchet-graph',
setup(pluginBuild) {
if (separateEntries) {
delete pluginBuild.initialOptions.stdin
pluginBuild.initialOptions.entryPoints = entries
}
pluginBuild.onEnd((built) => {
result = built
})
}
}
const importers = await collectElectronImporters(entries, { plugins: [...plugins, inspect] })
return { importers, ...result }
}

function fixtureInputs(result, root) {
return Object.fromEntries(
Object.entries(result.metafile.inputs)
.filter(([file]) => file.includes(path.basename(root)))
.sort(([left], [right]) => left.localeCompare(right))
)
}

describe('the runtime Electron import graph', () => {
it('retains every original CommonJS input and Electron edge when sharing root dependencies', async () => {
const root = fixture({
'entry.mjs': `
export { value } from './shared.cjs'
export { value as imported } from 'conditional-package'
export const load = () => import('./dynamic.mjs')
`,
'future.cjs': `
module.exports = {
value: require('conditional-package').value,
bare: () => require.resolve('electron'),
subpath: () => require.resolve('electron/main'),
addon: () => require('./missing.node'),
desktop: () => require('desktop-package')
}
`,
'shared.cjs': 'exports.value = 42',
'dynamic.mjs': "import 'electron/renderer'; export const value = 1",
'node_modules/conditional-package/package.json': JSON.stringify({
exports: { '.': { import: './import.mjs', require: './require.cjs' } }
}),
'node_modules/conditional-package/import.mjs': "import 'electron'; export const value = 2",
'node_modules/conditional-package/require.cjs': "require('electron/main'); exports.value = 3",
'node_modules/desktop-package/package.json': '{"main":"index.cjs"}',
'node_modules/desktop-package/index.cjs': "require('electron'); exports.value = 4"
})
const injectElectron = {
name: 'inject-future-electron-import',
setup(pluginBuild) {
pluginBuild.onLoad({ filter: /future\.cjs$/ }, (args) => ({
contents: `require('electron/utility')\n${readFileSync(args.path, 'utf8')}`,
loader: 'js'
}))
}
}
const entries = ['entry.mjs', 'future.cjs', 'shared.cjs'].map((file) => path.join(root, file))
const original = await inspectGraph(entries, {
separateEntries: true,
plugins: [injectElectron]
})
const shared = await inspectGraph(entries, { plugins: [injectElectron] })

expect(fixtureInputs(shared, root)).toEqual(fixtureInputs(original, root))
expect(shared.importers).toEqual(original.importers)
expect(shared.importers.map((file) => file.slice(file.indexOf(path.basename(root))))).toEqual([
`${path.basename(root)}/dynamic.mjs`,
`${path.basename(root)}/future.cjs`,
`${path.basename(root)}/node_modules/conditional-package/import.mjs`,
`${path.basename(root)}/node_modules/conditional-package/require.cjs`,
`${path.basename(root)}/node_modules/desktop-package/index.cjs`
])
const future = Object.entries(shared.metafile.inputs).find(([file]) =>
file.endsWith('/future.cjs')
)
expect(future[1].imports).toEqual(
expect.arrayContaining([
expect.objectContaining({ path: 'electron', kind: 'require-resolve', external: true }),
expect.objectContaining({ path: 'electron/main', kind: 'require-resolve', external: true }),
expect.objectContaining({ path: './missing.node', external: true })
])
)
})
})
20 changes: 13 additions & 7 deletions config/scripts/check-runtime-electron-ratchet.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -113,18 +113,24 @@ const externalNativeAddons = {
// Why `plugins`: lets a test add an Electron import to a real file in memory, never on disk.
export async function collectElectronImporters(entryPoints, { plugins = [] } = {}) {
const result = await build({
entryPoints,
// Export every entry through one bundle so shared dependencies are emitted once.
stdin: {
contents: entryPoints
.map(
(entry, index) =>
`export * as entry_${index} from ${JSON.stringify(path.resolve(ROOT, entry))}`
)
.join('\n'),
resolveDir: ROOT,
loader: 'ts',
sourcefile: 'runtime-electron-ratchet-entry.ts'
},
bundle: true,
write: false,
// Why outdir with write:false: esbuild refuses multiple entry points without one,
// even though nothing is emitted — the metafile is all this reads.
outdir: path.join(ROOT, 'runtime-electron-ratchet-metafile-only'),
platform: 'node',
target: 'node20',
// Why splitting: ~2.5k entry points each bundled whole held every copy of the graph in
// memory (2GB+), enough to take down a CI runner; shared chunks keep one copy.
format: 'esm',
splitting: true,
format: 'cjs',
external: EXTERNAL,
metafile: true,
absWorkingDir: ROOT,
Expand Down
Loading
Loading