Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
1f804dd
Use bounded OpenCode context for vault session continuation
nwparker Oct 2, 2026
8bb7fc1
Read real OpenCode sessions in terminal-backed native Chat
nwparker Oct 2, 2026
763afa8
fix(opencode): publish approval cards for permission requests
nwparker Oct 2, 2026
9d8dbbc
Send OpenCode native approval through its Enter selector
nwparker Oct 2, 2026
b9b3c95
Resolve mobile Chat readability for folder workspaces
nwparker Oct 2, 2026
430ada0
Bound OpenCode part batches and preserve v2 image attachments
nwparker Oct 2, 2026
211ea32
Prefer live migrated OpenCode sessions over legacy copies
nwparker Oct 2, 2026
a5c605a
Consolidate mobile Chat eligibility test imports
nwparker Oct 2, 2026
581e9ae
Consolidate OpenCode SQLite protocol type imports
nwparker Oct 2, 2026
54cf5f9
Update native chat settings contract for both OpenCode agents
nwparker Oct 2, 2026
7eaa92c
fix(native-chat): reconcile bounded OpenCode transcript reads
nwparker Oct 3, 2026
8193052
fix(native-chat): dispatch OpenCode questions safely
nwparker Oct 3, 2026
7f28ded
fix(native-chat): keep native discovery and transcript windows current
nwparker Oct 3, 2026
16068a2
Merge frozen main into OpenCode native Chat corrections
nwparker Oct 3, 2026
b032867
feat(accounts): link standalone GLM Coding Plans (#24618)
nwparker Oct 3, 2026
80dbe23
fix(native-chat): route transcript requests through shared SQLite worker
nwparker Oct 3, 2026
77ad467
fix(ci): prevent concurrent pnpm refresh during mobile typechecks (#2…
nwparker Oct 3, 2026
54ddebf
test(terminal): restore the live fish fixture prerequisites (#24947)
nwparker Oct 3, 2026
08ee7ba
fix(shortcuts): defer TUI editing chords in terminal-first mode (#24640)
nwparker Oct 3, 2026
843607b
Register supervised Qoder China and Qwen Code (#24616)
nwparker Oct 3, 2026
8f26bfa
Use the measured pnpm lookup policy automatically in hosted root CI (…
nwparker Oct 3, 2026
535c68c
fix(native-chat): keep OpenCode history usable at read limits
nwparker Oct 3, 2026
055871a
Continue Antigravity IDE and 2.0 history in new CLI conversations (#2…
nwparker Oct 3, 2026
e8402b4
fix(opencode): retry timed-out SSH plugin updates (#24666)
nwparker Oct 3, 2026
53f9ea7
fix(opencode): keep Go credentials private and resolve backend keys (…
nwparker Oct 3, 2026
896d011
Preserve current credential discovery while integrating native transc…
nwparker Oct 3, 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
  •  
  •  
  •  
42 changes: 32 additions & 10 deletions .github/actions/install-node-dependencies/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ inputs:
required: false
default: 'true'
cache-pnpm-store-lookup-only:
description: On non-PR producers, refresh existing stores without downloading and save stores on a miss.
description: Auto uses measured hosted Node 24 root installs; true forces lookup, false retains archive restoration.
required: false
default: 'false'
default: auto
cache-pnpm-verification:
description: Restore pnpm's policy-checked lockfile verification record.
required: false
Expand Down Expand Up @@ -67,6 +67,30 @@ outputs:
runs:
using: composite
steps:
- name: Resolve pnpm store mode
id: pnpm-store-mode
if: >-
github.event_name != 'pull_request' && inputs.cache-pnpm-store != 'false' &&
(inputs.cache-pnpm-store-lookup-only == 'true' ||
(inputs.cache-pnpm-store-lookup-only == 'auto' &&
inputs.cache-dependency-path == 'pnpm-lock.yaml' &&
runner.environment == 'github-hosted' && job.container.id == '' &&
(runner.os == 'Linux' || runner.os == 'macOS' || runner.os == 'Windows') &&
(runner.arch == 'X64' || runner.arch == 'ARM64') &&
(inputs.node-version == '' || inputs.node-version == '24')))
shell: bash
env:
LOOKUP_REQUEST: ${{ inputs.cache-pnpm-store-lookup-only }}
run: |
lookup_only=true
case "$LOOKUP_REQUEST" in
[aA][uU][tT][oO])
# Hosted runners have Node for this manifest-only check before toolchain setup.
lookup_only="$(node -p 'const p = require("./package.json"); p.engines?.node === "24" && typeof p.packageManager === "string" && p.packageManager.split("+")[0] === "pnpm@12.8.1"')"
;;
esac
printf 'lookup-only=%s\n' "$lookup_only" >> "$GITHUB_OUTPUT"

# setup-node needs pnpm on PATH to locate and restore its store.
- name: Setup pnpm
uses: pnpm/setup@v2
Expand All @@ -80,7 +104,7 @@ runs:
uses: actions/setup-node@v6
with:
node-version-file: package.json
cache: ${{ github.event_name != 'pull_request' && inputs.cache-pnpm-store != 'false' && inputs.cache-pnpm-store-lookup-only != 'true' && 'pnpm' || '' }}
cache: ${{ github.event_name != 'pull_request' && inputs.cache-pnpm-store != 'false' && steps.pnpm-store-mode.outputs.lookup-only != 'true' && 'pnpm' || '' }}
cache-dependency-path: ${{ inputs.cache-dependency-path }}
package-manager-cache: false

Expand All @@ -90,7 +114,7 @@ runs:
uses: actions/setup-node@v6
with:
node-version: ${{ inputs.node-version }}
cache: ${{ github.event_name != 'pull_request' && inputs.cache-pnpm-store != 'false' && inputs.cache-pnpm-store-lookup-only != 'true' && 'pnpm' || '' }}
cache: ${{ github.event_name != 'pull_request' && inputs.cache-pnpm-store != 'false' && steps.pnpm-store-mode.outputs.lookup-only != 'true' && 'pnpm' || '' }}
cache-dependency-path: ${{ inputs.cache-dependency-path }}
package-manager-cache: false

Expand All @@ -104,11 +128,11 @@ runs:
!(runner.arch == 'X64' && contains(inputs.cache-dependency-path, 'mobile/pnpm-lock.yaml')) &&
!((runner.arch == 'X64' || runner.arch == 'ARM64') && inputs.cache-dependency-path == 'pnpm-lock.yaml')) ||
(github.event_name != 'pull_request' && inputs.cache-pnpm-store != 'false' &&
inputs.cache-pnpm-store-lookup-only == 'true')
steps.pnpm-store-mode.outputs.lookup-only == 'true')
shell: bash
env:
LOCKFILE_HASH: ${{ hashFiles(inputs.cache-dependency-path) }}
STORE_LOOKUP_ONLY: ${{ github.event_name != 'pull_request' && inputs.cache-pnpm-store-lookup-only == 'true' }}
STORE_LOOKUP_ONLY: ${{ steps.pnpm-store-mode.outputs.lookup-only == 'true' }}
run: |
test -n "$LOCKFILE_HASH"
cache_path="$(pnpm store path --silent)"
Expand Down Expand Up @@ -137,12 +161,10 @@ runs:
# Producers can refresh access and publish misses without downloading existing archives.
- name: Keep pnpm download store without restoring
id: pnpm-store-lookup
if: >-
github.event_name != 'pull_request' && inputs.cache-pnpm-store != 'false' &&
inputs.cache-pnpm-store-lookup-only == 'true'
if: steps.pnpm-store-mode.outputs.lookup-only == 'true'
uses: actions/cache@v5
with:
# Post-job saves cannot resolve the composite's internal step outputs.
# Twice-nested composite cleanup loses internal step outputs.
path: ${{ env.ORCA_PNPM_STORE_CACHE_PATH }}
key: node-cache-${{ runner.os }}-${{ steps.pnpm-store.outputs.arch }}-pnpm-${{ hashFiles(inputs.cache-dependency-path) }}
lookup-only: true
Expand Down
6 changes: 3 additions & 3 deletions .github/workflows/mobile.yml
Original file line number Diff line number Diff line change
Expand Up @@ -114,11 +114,11 @@ jobs:
- name: Install dependencies
run: pnpm install --frozen-lockfile

# pnpm entrypoints can auto-install; finish each before the next mutates node_modules.
# Call installed tools so pnpm's dependency refresh cannot race between checks.
- name: Typecheck
id: production-types
background: true
run: pnpm typecheck
run: node node_modules/typescript/bin/tsc --noEmit

- wait: production-types

Expand All @@ -127,7 +127,7 @@ jobs:
# drifted. This fails when a test file that checks today stops checking, when a test leaves
# the program, and on @ts-nocheck; the baseline may only shrink.
- name: Typecheck tests (ratchet)
run: pnpm run check:tests-typecheck
run: node scripts/check-tests-typecheck-ratchet.mjs

# This includes the bridged replay of the whole recording corpus, which used to be a second
# step of its own behind RPC_FOUNDATION_BRIDGE=1. A gate nobody can forget to set is the point:
Expand Down
24 changes: 18 additions & 6 deletions config/scripts/ci-dependency-download-cache.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ describe('CI dependency download caches', () => {
expect(action.inputs['cache-dependency-path'].default).toBe('pnpm-lock.yaml')
for (const step of action.runs.steps.filter((step) => step.uses === 'actions/setup-node@v6')) {
expect(step.with.cache).toBe(
"${{ github.event_name != 'pull_request' && inputs.cache-pnpm-store != 'false' && inputs.cache-pnpm-store-lookup-only != 'true' && 'pnpm' || '' }}"
"${{ github.event_name != 'pull_request' && inputs.cache-pnpm-store != 'false' && steps.pnpm-store-mode.outputs.lookup-only != 'true' && 'pnpm' || '' }}"
)
expect(step.with['cache-dependency-path']).toBe('${{ inputs.cache-dependency-path }}')
expect(step.with['package-manager-cache']).toBe(false)
Expand Down Expand Up @@ -41,7 +41,7 @@ describe('CI dependency download caches', () => {
"github.event_name == 'pull_request' && inputs.cache-pnpm-store != 'false' && !((runner.os == 'Linux' || runner.os == 'macOS') && (runner.arch == 'X64' || runner.arch == 'ARM64') && inputs.cache-dependency-path == 'pnpm-lock.yaml') && (runner.os != 'Windows' || !(runner.arch == 'X64' && contains(inputs.cache-dependency-path, 'mobile/pnpm-lock.yaml')) && !((runner.arch == 'X64' || runner.arch == 'ARM64') && inputs.cache-dependency-path == 'pnpm-lock.yaml'))"
)
expect(resolve.if).toBe(
`${restore.if} || (github.event_name != 'pull_request' && inputs.cache-pnpm-store != 'false' && inputs.cache-pnpm-store-lookup-only == 'true')`
`${restore.if} || (github.event_name != 'pull_request' && inputs.cache-pnpm-store != 'false' && steps.pnpm-store-mode.outputs.lookup-only == 'true')`
)
expect(restore.uses).toBe('actions/cache/restore@v5')
expect(restore.with.path).toBe('${{ steps.pnpm-store.outputs.path }}')
Expand Down Expand Up @@ -72,11 +72,9 @@ describe('CI dependency download caches', () => {
it('keeps producer lookup optional and compatible with the existing store archive', () => {
const lookup = action.runs.steps.find((step) => step.id === 'pnpm-store-lookup')
const restore = action.runs.steps.find((step) => step.id === 'pnpm-store-restore')
expect(action.inputs['cache-pnpm-store-lookup-only'].default).toBe('false')
expect(action.inputs['cache-pnpm-store-lookup-only'].default).toBe('auto')
expect(lookup.uses).toBe('actions/cache@v5')
expect(lookup.if).toBe(
"github.event_name != 'pull_request' && inputs.cache-pnpm-store != 'false' && inputs.cache-pnpm-store-lookup-only == 'true'"
)
expect(lookup.if).toBe("steps.pnpm-store-mode.outputs.lookup-only == 'true'")
expect(lookup.with).toEqual({
path: '${{ env.ORCA_PNPM_STORE_CACHE_PATH }}',
key: restore.with.key,
Expand Down Expand Up @@ -203,6 +201,16 @@ describe('CI dependency download caches', () => {
const context = {
github: { event_name: event },
runner: { os, arch },
steps: {
'pnpm-store-mode': {
outputs: {
'lookup-only':
event !== 'pull_request' && storeCache !== 'false' && lookupOnly === 'true'
? 'true'
: ''
}
}
},
inputs: {
'cache-pnpm-store': storeCache,
'cache-pnpm-store-lookup-only': lookupOnly,
Expand All @@ -218,6 +226,10 @@ describe('CI dependency download caches', () => {
const evaluate = (expression) =>
runInNewContext(
expression
.replaceAll(
'steps.pnpm-store-mode.outputs.lookup-only',
'steps["pnpm-store-mode"].outputs["lookup-only"]'
)
.replaceAll(
'inputs.cache-pnpm-store-lookup-only',
'inputs["cache-pnpm-store-lookup-only"]'
Expand Down
150 changes: 150 additions & 0 deletions config/scripts/ci-pnpm-store-mode.test.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
import { mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs'
import { tmpdir } from 'node:os'
import { join } from 'node:path'
import { runInNewContext } from 'node:vm'
import { parse } from 'yaml'
import { describe, expect, it } from 'vitest'
import { runProcessSync } from './script-child-process.mjs'

const action = parse(readFileSync('.github/actions/install-node-dependencies/action.yml', 'utf8'))
const mode = action.runs.steps.find((step) => step.id === 'pnpm-store-mode')
const defaultContext = {
github: { event_name: 'push' },
runner: { os: 'Linux', arch: 'X64', environment: 'github-hosted' },
job: { container: { id: '' } },
inputs: {
'cache-pnpm-store': 'true',
'cache-pnpm-store-lookup-only': 'auto',
'cache-dependency-path': 'pnpm-lock.yaml',
'node-version': ''
}
}
const expression = mode.if.replaceAll(/inputs\.([\w-]+)/g, 'inputs["$1"]')

function eligible(changes) {
const context = structuredClone(defaultContext)
for (const [name, fields] of Object.entries(changes)) {
Object.assign(context[name], fields)
}
return runInNewContext(expression, context)
}

function resolveMode(request, node, manager) {
const directory = mkdtempSync(join(tmpdir(), 'orca-store-mode-'))
const output = join(directory, 'output')
try {
writeFileSync(
join(directory, 'package.json'),
JSON.stringify({ engines: { node }, packageManager: manager })
)
const result = runProcessSync({
program: 'bash',
args: ['-e', '-o', 'pipefail', '-c', mode.run],
cwd: directory,
env: { ...process.env, LOOKUP_REQUEST: request, GITHUB_OUTPUT: output }
})
expect(result.code, result.stderr || result.stdout).toBe(0)
return readFileSync(output, 'utf8')
} finally {
rmSync(directory, { recursive: true, force: true })
}
}

describe('automatic pnpm store mode', () => {
it.each([
['auto', '24', 'pnpm@12.8.1', '', true],
['auto', '25', 'pnpm@12.8.1', 'pnpm', false],
['auto', '24', 'pnpm@13.0.0', 'pnpm', false],
['true', '25', 'pnpm@13.0.0', '', true]
])(
'routes resolved %s mode for Node %s / %s into both cache steps',
(request, node, manager, cache, lookup) => {
const context = structuredClone(defaultContext)
context.inputs['cache-pnpm-store-lookup-only'] = request
const resolved = resolveMode(request, node, manager).split('=')[1].trim()
const evaluate = (value) =>
runInNewContext(
value
.replaceAll(/inputs\.([\w-]+)/g, 'inputs["$1"]')
.replaceAll(
'steps.pnpm-store-mode.outputs.lookup-only',
'steps["pnpm-store-mode"].outputs["lookup-only"]'
),
{ ...context, steps: { 'pnpm-store-mode': { outputs: { 'lookup-only': resolved } } } }
)
const nodeSetup = action.runs.steps.find((step) => step.id === 'default-node')
expect(evaluate(nodeSetup.with.cache.slice(3, -2))).toBe(cache)
expect(evaluate(action.runs.steps.find((step) => step.id === 'pnpm-store-lookup').if)).toBe(
lookup
)
}
)

it.each(
['Linux', 'Windows', 'macOS'].flatMap((os) => ['X64', 'ARM64'].map((arch) => [os, arch]))
)('qualifies the measured %s/%s hosted root context', (os, arch) => {
expect(eligible({ runner: { os, arch } })).toBe(true)
})

it.each([
['PR', { github: { event_name: 'pull_request' } }],
['opted-out store', { inputs: { 'cache-pnpm-store': 'false' } }],
['opted-out lookup', { inputs: { 'cache-pnpm-store-lookup-only': 'false' } }],
['unknown request', { inputs: { 'cache-pnpm-store-lookup-only': 'other' } }],
[
'mixed lockfiles',
{ inputs: { 'cache-dependency-path': 'pnpm-lock.yaml\nmobile/pnpm-lock.yaml' } }
],
['custom lockfile', { inputs: { 'cache-dependency-path': 'cloud/pnpm-lock.yaml' } }],
['Node 25', { inputs: { 'node-version': '25' } }],
['job container', { job: { container: { id: 'container-id' } } }],
['self-hosted runner', { runner: { environment: 'self-hosted' } }],
['unknown host kind', { runner: { environment: '' } }],
['unmeasured architecture', { runner: { arch: 'X86' } }],
['unmeasured OS', { runner: { os: 'other' } }]
])('retains the legacy policy for %s', (_name, changes) => {
expect(eligible(changes)).toBe(false)
})

it('allows an explicit request to preserve the existing force-lookup contract', () => {
expect(
eligible({
inputs: {
'cache-pnpm-store-lookup-only': 'true',
'node-version': '25',
'cache-dependency-path': 'custom-lock.yaml'
},
runner: { environment: 'self-hosted' },
job: { container: { id: 'container-id' } }
})
).toBe(true)
expect(
eligible({
github: { event_name: 'pull_request' },
inputs: { 'cache-pnpm-store-lookup-only': 'true' }
})
).toBe(false)
})

it.each([
['24', 'pnpm@12.8.1', 'true'],
['24', 'pnpm@12.8.1+sha512.fixture', 'true'],
['25', 'pnpm@12.8.1', 'false'],
['24.x', 'pnpm@12.8.1', 'false'],
['24', 'pnpm@12.8.2', 'false'],
['24', 'pnpm@12.8.10', 'false'],
['24', undefined, 'false'],
[undefined, 'pnpm@12.8.1', 'false'],
['24', 12, 'false']
])('checks manifest Node %s and manager %s before choosing lookup', (node, manager, expected) => {
expect(resolveMode('auto', node, manager)).toBe(`lookup-only=${expected}\n`)
})

it('checks uppercase auto requests consistently with GitHub expression comparisons', () => {
expect(resolveMode('AUTO', '25', 'pnpm@12.8.1')).toBe('lookup-only=false\n')
})

it('does not constrain an explicit request to the automatic manifest profile', () => {
expect(resolveMode('true', '25', 'pnpm@13.0.0')).toBe('lookup-only=true\n')
})
})
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ const REVIEWED_COMPUTED_PATHS = [
'${{ env.ORCA_PNPM_STORE_CACHE_PATH }}',
// Only pnpm's lockfile-verified.jsonl record, never Metro transforms.
'${{ steps.verification-cache.outputs.path }}',
"${{ github.event_name != 'pull_request' && inputs.cache-pnpm-store != 'false' && inputs.cache-pnpm-store-lookup-only != 'true' && 'pnpm' || '' }} store"
"${{ github.event_name != 'pull_request' && inputs.cache-pnpm-store != 'false' && steps.pnpm-store-mode.outputs.lookup-only != 'true' && 'pnpm' || '' }} store"
]

/** Every step a workflow runs, descending into the repository's own composite actions. */
Expand Down
42 changes: 42 additions & 0 deletions config/scripts/mobile-typecheck-workflow.test.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { readFileSync } from 'node:fs'
import { describe, expect, it } from 'vitest'
import { parse } from 'yaml'

const workflow = parse(readFileSync('.github/workflows/mobile.yml', 'utf8'))
const packageJson = JSON.parse(readFileSync('mobile/package.json', 'utf8'))
const job = workflow.jobs.verify
const steps = job.steps

describe('mobile verification command ownership', () => {
it('runs the declared checks through installed tools without a script-time install', () => {
const production = steps.find((step) => step.name === 'Typecheck')
const tests = steps.find((step) => step.name === 'Typecheck tests (ratchet)')

expect(packageJson.scripts.typecheck).toMatch(/^tsc\b/)
expect(production.run).toBe(`node node_modules/typescript/bin/${packageJson.scripts.typecheck}`)
expect(tests.run).toBe(packageJson.scripts['check:tests-typecheck'])
expect(tests.run).toMatch(/^node\s/)
expect(job.defaults.run['working-directory']).toBe('mobile')
for (const name of ['typecheck', 'check:tests-typecheck']) {
expect(packageJson.scripts[`pre${name}`]).toBeUndefined()
expect(packageJson.scripts[`post${name}`]).toBeUndefined()
}
})

it('finishes installation and joins production types before checking test types', () => {
const installIndex = steps.findIndex((step) => step.name === 'Install dependencies')
const productionIndex = steps.findIndex((step) => step.name === 'Typecheck')
const ratchetIndex = steps.findIndex((step) => step.name === 'Typecheck tests (ratchet)')
const waitIndex = steps.findIndex((step) => step.wait === steps[productionIndex].id)
const testIndex = steps.findIndex((step) => step.name === 'Test')

expect(steps[installIndex].run).toBe('pnpm install --frozen-lockfile')
expect(steps[installIndex].background ?? false).toBe(false)
expect(installIndex).toBeLessThan(productionIndex)
expect(steps[productionIndex].background).toBe(true)
expect(productionIndex).toBeLessThan(ratchetIndex)
expect(waitIndex).toBeGreaterThan(productionIndex)
expect(waitIndex).toBeLessThan(ratchetIndex)
expect(ratchetIndex).toBeLessThan(testIndex)
})
})
Loading
Loading