diff --git a/config/scripts/ci-shard-timings.json b/config/scripts/ci-shard-timings.json index 4390af42d65..62e9ab990b3 100644 --- a/config/scripts/ci-shard-timings.json +++ b/config/scripts/ci-shard-timings.json @@ -3141,7 +3141,6 @@ "src/main/ipc/repos/repo-creation-git-availability.test.ts": 124, "src/main/ipc/repos/repo-ipc-wsl-move-fence.test.ts": 2855, "src/main/ipc/repos/repo-remove-for-host-teardown.test.ts": 423, - "src/main/ipc/runtime-agent-launch-capability.test.ts": 1720, "src/main/ipc/runtime-environments/orcad-delta-move-handlers.test.ts": 949, "src/main/ipc/runtime-environments/orcad-runtime-conversion-handlers.test.ts": 569, "src/main/ipc/runtime-environments/orcad-runtime-lifecycle-handlers.test.ts": 32, @@ -3989,7 +3988,6 @@ "src/main/opencode/overlay-dir-gc-lifecycle.test.ts": 67, "src/main/opencode/overlay-dir-gc.test.ts": 81, "src/main/opencode/overlay-source-ownership.test.ts": 78, - "src/main/opencode/status-plugin-shared-server-capability.test.ts": 42, "src/main/opencode2/status-plugin-setup-lifetime.test.ts": 110, "src/main/orca-profiles/other-profile-workspace-catalog.test.ts": 377, "src/main/orca-profiles/profile-active-transfer-worker.test.ts": 2010, @@ -5459,7 +5457,6 @@ "src/main/runtime/rpc/methods/orchestration/worker/worker-start-prompt-contract.test.ts": 3293, "src/main/runtime/rpc/methods/orchestration/worker/worker-start-readiness-settlement.test.ts": 541, "src/main/runtime/rpc/methods/orchestration/worker/worker-start-turn-observation.test.ts": 43, - "src/main/runtime/rpc/methods/orchestration/worker/worker-stop-capability.test.ts": 478, "src/main/runtime/rpc/methods/orchestration/worker/worker-stop-exit-race.test.ts": 3668, "src/main/runtime/rpc/methods/orchestration/worker/worker-stop-liveness-verdict.test.ts": 3343, "src/main/runtime/rpc/methods/orchestration/worker/worker-terminal-custody-at-creation.test.ts": 3239, diff --git a/src/main/ipc/runtime-agent-launch-capability.test.ts b/src/main/ipc/runtime-agent-launch-capability.test.ts deleted file mode 100644 index 05de3300f08..00000000000 --- a/src/main/ipc/runtime-agent-launch-capability.test.ts +++ /dev/null @@ -1,143 +0,0 @@ -/** Exercise advertised IPC capabilities through the real host gate, including the local-renderer launch path. */ - -import { beforeEach, describe, expect, it, vi } from 'vitest' -import type { RuntimeCapability } from '../../shared/protocol-version' -import { AGENT_LAUNCH_RUNTIME_CAPABILITY } from '../../shared/agent-launch-runtime-capability' -import { - resolveRpcCallerIdentity, - rpcCallerOperationKey, - type RpcCallerIdentity -} from '../runtime/rpc/rpc-caller-identity' - -type AdvertisedClient = { - clientKind?: 'mobile' | 'runtime' - clientCapabilities?: readonly RuntimeCapability[] - caller?: RpcCallerIdentity - pairedDeviceId?: string -} - -const { handlers, advertised } = vi.hoisted( - (): { - handlers: Map unknown> - advertised: { unary: AdvertisedClient[]; streaming: AdvertisedClient[] } - } => ({ - handlers: new Map(), - advertised: { unary: [], streaming: [] } - }) -) - -vi.mock('electron', () => ({ - BrowserWindow: { fromWebContents: vi.fn() }, - ipcMain: { - handle: vi.fn((channel: string, handler: (event: unknown, args?: unknown) => unknown) => { - handlers.set(channel, handler) - }), - on: vi.fn(), - removeAllListeners: vi.fn(), - removeHandler: vi.fn() - } -})) - -vi.mock('../runtime/rpc/dispatcher', () => ({ - RpcDispatcher: class { - dispatch(_request: unknown, options: AdvertisedClient): Promise { - advertised.unary.push(options) - return Promise.resolve({ ok: true, result: {} }) - } - - dispatchStreaming( - _request: unknown, - _emit: (response: string) => void, - options: AdvertisedClient - ): Promise { - advertised.streaming.push(options) - // Keep streaming open; the handler attaches only a cleanup callback. - return new Promise(() => {}) - } - } -})) - -const { registerRuntimeHandlers } = await import('./runtime') -const { supportsAgentLaunch } = await import('../runtime/rpc/methods/agent-launch') -const { agentLaunchOperationCallerKey } = await import('../runtime/rpc/methods/agent-launch-replay') - -function rendererEvent() { - const mainFrame = {} - return { - sender: { id: 1, mainFrame, on: vi.fn(), once: vi.fn(), isDestroyed: () => false }, - senderFrame: mainFrame - } -} - -function invoke(channel: string, args: unknown): void { - const handler = handlers.get(channel) - if (!handler) { - throw new Error(`no handler registered for ${channel}`) - } - void handler(rendererEvent(), args) -} - -/** Missing dispatch must fail rather than pass the gate via undefined clientKind. */ -function onlyAdvertisedClient(records: readonly AdvertisedClient[]): AdvertisedClient { - const client = records[0] - if (!client) { - throw new Error('the handler dispatched nothing') - } - return client -} - -function withoutLaunchCapability(client: AdvertisedClient): AdvertisedClient { - return { - clientKind: client.clientKind, - clientCapabilities: client.clientCapabilities?.filter( - (capability) => capability !== AGENT_LAUNCH_RUNTIME_CAPABILITY - ) - } -} - -describe('desktop renderer reaching agent.launch on its own main process', () => { - beforeEach(() => { - handlers.clear() - advertised.unary.length = 0 - advertised.streaming.length = 0 - // oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: registerRuntimeHandlers reaches only the sender-lifecycle cleanup on this path; any other runtime method it called would throw here rather than read a wrong value. - registerRuntimeHandlers({ cleanupSubscriptionsForConnection: vi.fn() } as never) - }) - - it('advertises the launch capability on the unary path', () => { - invoke('runtime:call', { - method: 'agent.launch', - params: { agent: 'claude', target: { kind: 'existing', worktree: 'id:wt-7' } } - }) - - const client = onlyAdvertisedClient(advertised.unary) - expect(client.clientKind).toBe('runtime') - expect(supportsAgentLaunch(client)).toBe(true) - expect(supportsAgentLaunch(withoutLaunchCapability(client))).toBe(false) - }) - - it('advertises the same set on the streaming path', () => { - invoke('runtime:call', { method: 'status.get' }) - invoke('runtime:subscribe', { subscriptionId: 'sub-1', method: 'session.tabs.watch' }) - - const streaming = onlyAdvertisedClient(advertised.streaming) - expect(streaming.clientCapabilities).toEqual( - onlyAdvertisedClient(advertised.unary).clientCapabilities - ) - expect(supportsAgentLaunch(streaming)).toBe(true) - }) - - it('names the desktop as its caller on both paths, so its launches replay under one identity', () => { - invoke('runtime:call', { method: 'status.get' }) - invoke('runtime:subscribe', { subscriptionId: 'sub-1', method: 'session.tabs.watch' }) - - for (const client of [advertised.unary, advertised.streaming].map(onlyAdvertisedClient)) { - const caller = resolveRpcCallerIdentity(client) - expect(caller).toEqual({ kind: 'desktop' }) - expect(agentLaunchOperationCallerKey({ caller })).toBe('trusted-local:desktop') - } - const { caller: _named, ...unnamed } = onlyAdvertisedClient(advertised.unary) - expect(resolveRpcCallerIdentity(unnamed)).toBeUndefined() - expect(rpcCallerOperationKey({ kind: 'local-cli' })).toBe('trusted-local:runtime') - }) -}) diff --git a/src/main/opencode/status-plugin-shared-server-capability.test.ts b/src/main/opencode/status-plugin-shared-server-capability.test.ts deleted file mode 100644 index fa0691ff88c..00000000000 --- a/src/main/opencode/status-plugin-shared-server-capability.test.ts +++ /dev/null @@ -1,53 +0,0 @@ -import { afterEach, describe, expect, it, vi } from 'vitest' -import { getStatusPluginPostSource } from './status-plugin-post-source' -import { getStatusPluginRuntimeStateSource } from './status-plugin-runtime-state-source' - -afterEach(() => { - vi.unstubAllEnvs() - vi.unstubAllGlobals() - vi.restoreAllMocks() -}) -describe('generated shared-server capability without a test-only environment flag', () => { - it.each([ - { argv: ['opencode', 'serve'], major: 0, capability: '1', expected: true }, - { - argv: ['opencode', '/embedded/cli.js', '--log-level', 'none', 'serve'], - major: 0, - capability: '1', - expected: true - }, - { - argv: ['opencode', '--log-level', 'serve', 'run', 'serve'], - major: 0, - capability: '1', - expected: false - }, - { argv: ['opencode', 'run', '--', 'serve'], major: 0, capability: '1', expected: false }, - { argv: ['opencode', '--', 'serve'], major: 0, capability: '1', expected: false }, - { argv: ['opencode', 'serve'], major: 2, capability: '1', expected: false }, - { argv: ['opencode', 'serve'], major: 0, capability: '', expected: false } - ])( - 'recognizes only an opted-in legacy serve context: %j', - async ({ argv, major, capability, expected }) => { - vi.stubEnv('ORCA_PANE_KEY', 'tab-a:aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa') - vi.stubEnv('ORCA_OPENCODE_PLUGIN_API', undefined) - vi.spyOn(process, 'argv', 'get').mockReturnValue(argv) - const posts: Record[] = [] - vi.stubGlobal('fetch', async (_input: unknown, init: RequestInit) => { - posts.push(JSON.parse(String(init.body))) - return new Response('{}', { status: 200 }) - }) - const source = [ - ...getStatusPluginRuntimeStateSource(), - ...getStatusPluginPostSource('/hook/opencode'), - `reportingOpenCodeMajor = ${major};`, - 'return post("SessionBusy", { sessionID: "ses_test" });' - ].join('\n') - const run = new Function('resolveHookCoords', source) - await run(() => ({ port: 12345, token: 'fixture', openCodeTui: capability })) - expect(posts).toHaveLength(1) - expect(posts[0].opencodeSharedServer === 1).toBe(expected) - expect(posts[0].opencodeMajor).toBe(major || undefined) - } - ) -}) diff --git a/src/main/plugins/catalog/plugin-marketplace-projection.test.ts b/src/main/plugins/catalog/plugin-marketplace-projection.test.ts deleted file mode 100644 index 2fce3e034eb..00000000000 --- a/src/main/plugins/catalog/plugin-marketplace-projection.test.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { describe, expect, it } from 'vitest' -import { pluginMarketplaceListingSchema } from '../../../shared/plugins/catalog/plugin-marketplace' -import { pluginMarketplaceListing } from './plugin-marketplace-projection' - -describe('pluginMarketplaceListing trust', () => { - const listing = (catalogUrl: string, pluginKey: string) => { - const source = { kind: 'git' as const, url: catalogUrl, ref: 'main' } - const marketplace = pluginMarketplaceListingSchema.parse({ - name: 'Orca Plugins', - owner: 'stablyai', - plugins: [ - { - id: pluginKey, - source: { kind: 'git', url: 'https://github.com/stablyai/orca-plugins.git', ref: 'v1' } - } - ] - }) - return pluginMarketplaceListing({ - source: { id: 'a'.repeat(32), source, addedAt: 0 }, - snapshot: { - schemaVersion: 2, - sourceId: 'a'.repeat(32), - source, - marketplaceCommit: 'b'.repeat(40), - fetchedAt: 0, - marketplace - }, - entry: marketplace.plugins[0]!, - hostVersion: null, - blocked: null - }) - } - - it('marks official what the official catalog lists, whatever the id', () => { - const official = 'https://github.com/stablyai/orca-plugins.git' - expect(listing(official, 'stablyai.spreadsheet').official).toBe(true) - expect(listing(official, 'stablyai.orca-gleam').official).toBe(true) - expect( - listing('https://github.com/acme/orca-plugins.git', 'stablyai.orca-gleam').official - ).toBe(false) - }) -}) diff --git a/src/main/plugins/plugin-list-projection.test.ts b/src/main/plugins/plugin-list-projection.test.ts index 72717b2fe0e..95f626ba349 100644 --- a/src/main/plugins/plugin-list-projection.test.ts +++ b/src/main/plugins/plugin-list-projection.test.ts @@ -67,89 +67,7 @@ function serviceWith( } as unknown as PluginService } -describe('buildPluginList contribution icons', () => { - it('projects editor and new-file icons as installed image URLs, and new files without paths', async () => { - const drawManifest = pluginManifestSchema.parse({ - manifestVersion: 2, - execution: 'sandboxed-ui', - id: 'draw', - publisher: 'samples', - name: 'Draw', - version: '1.0.0', - engines: { orca: '>=1.4.245' }, - contributes: { - customEditors: [ - { id: 'draw', title: 'Draw', extensions: ['.draw'], entry: 'e.html', icon: 'tab.png' } - ], - newFiles: [ - { id: 'drawing', title: 'New drawing', extension: '.draw', template: 'empty.draw' } - ] - } - }) - const hash = 'c'.repeat(64) - const [entry] = await buildPluginList( - serviceWith({ - pluginKey: 'samples.draw', - rootDir: join(tmpdir(), 'plugins', 'draw'), - manifest: drawManifest, - consentFingerprint: 'sha256-current', - consentContentHash: hash, - contentHash: hash, - isDev: false - }), - emptyPluginLockfile() - ) - expect(entry?.customEditors?.[0]?.icon).toBe( - `orca-plugin-image://installed/samples.draw/${hash}/editor-0` - ) - // Why no template path: clients ask main for the text, never for a file. - expect(entry?.newFiles).toEqual([{ id: 'drawing', title: 'New drawing', extension: '.draw' }]) - }) -}) - describe('buildPluginList consent identity', () => { - it('projects the exact current fingerprint for an optimistic consent write', async () => { - const plugin: ValidDiscoveredPlugin = { - pluginKey: 'samples.demo', - rootDir: join(tmpdir(), 'plugins', 'demo'), - manifest, - consentFingerprint: 'sha256-current', - contentHash: null, - isDev: true - } - - expect((await buildPluginList(serviceWith(plugin), emptyPluginLockfile()))[0]).toMatchObject({ - pluginKey: plugin.pluginKey, - consentFingerprint: 'sha256-current', - status: 'pending', - // Why declarative: a capability makes it more than content, with no worker in sight. - execution: 'declarative' - }) - }) - - it('projects supervised backoff as restarting instead of running', async () => { - const plugin: ValidDiscoveredPlugin = { - pluginKey: 'samples.demo', - rootDir: join(tmpdir(), 'plugins', 'demo'), - manifest, - consentFingerprint: 'sha256-current', - contentHash: null, - isDev: true - } - - expect( - ( - await buildPluginList( - serviceWith(plugin, { - activation: 'approved', - worker: { state: 'restarting', restarts: 2 } - }), - emptyPluginLockfile() - ) - )[0] - ).toMatchObject({ status: 'restarting', restarts: 2 }) - }) - it('does not attribute a shadowing dev plugin to the installed source', async () => { const plugin: ValidDiscoveredPlugin = { pluginKey: 'samples.demo', @@ -193,104 +111,6 @@ describe('buildPluginList consent identity', () => { expect(projected.name).toBe('invalid-development-plugin-1') expect(JSON.stringify(projected)).not.toContain(invalid.rootDir) }) - - it('projects exact VM lifecycle commands for declarative consent', async () => { - const recipeManifest = pluginManifestSchema.parse({ - ...manifest, - contributes: { vmRecipes: [{ path: 'recipes/cloud.json' }] } - }) - const plugin: ValidDiscoveredPlugin = { - pluginKey: 'samples.demo', - rootDir: join(tmpdir(), 'plugins', 'demo'), - manifest: recipeManifest, - consentFingerprint: 'sha256-current', - consentContentHash: 'a'.repeat(64), - contentHash: null, - isDev: true - } - - expect( - ( - await buildPluginList( - serviceWith(plugin, { - vmRecipes: [ - { - pluginKey: plugin.pluginKey, - recipe: { - id: 'cloud', - name: 'Cloud', - create: './create.sh', - destroyDisabled: true - } - } - ] - }), - emptyPluginLockfile() - ) - )[0]?.vmRecipes - ).toEqual([ - { - id: 'cloud', - name: 'Cloud', - commands: [ - { phase: 'create', command: './create.sh' }, - { phase: 'destroy', command: 'none' } - ] - } - ]) - }) - - it('projects command handlers and normalized keybindings for consent and dispatch', async () => { - const commandManifest = pluginManifestSchema.parse({ - ...manifest, - contributes: { - commands: [ - { id: 'tasks', title: 'Open Tasks', context: 'workspace', action: 'view.tasks' } - ], - keybindings: [{ command: 'tasks', key: 'mod+alt+t' }] - } - }) - const plugin: ValidDiscoveredPlugin = { - pluginKey: 'samples.demo', - rootDir: join(tmpdir(), 'plugins', 'demo'), - manifest: commandManifest, - consentFingerprint: 'sha256-current', - consentContentHash: 'a'.repeat(64), - contentHash: null, - isDev: true - } - - const [entry] = await buildPluginList(serviceWith(plugin), emptyPluginLockfile()) - // Why: a worker-less commands plugin still runs instructions, so the page must not say content only. - expect(entry).toMatchObject({ hasWorker: false, execution: 'declarative' }) - expect( - ( - await buildPluginList( - serviceWith(plugin, { - commands: [ - { - pluginKey: plugin.pluginKey, - id: 'tasks', - title: 'Open Tasks', - context: 'workspace', - handler: { type: 'built-in', action: 'view.tasks' }, - keybindings: [{ key: 'Mod+Alt+T', when: 'workspace' }] - } - ] - }), - emptyPluginLockfile() - ) - )[0]?.commands - ).toEqual([ - { - id: 'tasks', - title: 'Open Tasks', - context: 'workspace', - handler: { type: 'built-in', action: 'view.tasks' }, - keybindings: [{ key: 'Mod+Alt+T', when: 'workspace' }] - } - ]) - }) }) describe('buildPluginList I/O', () => { @@ -320,41 +140,4 @@ describe('buildPluginList I/O', () => { expect(entry?.source?.kind).toBe('git') expect(fsCalls.count).toBe(0) }) - - it('reports the catalog ref a marketplace install came from', async () => { - const plugin: ValidDiscoveredPlugin = { - pluginKey: 'samples.demo', - rootDir: join(tmpdir(), 'plugins', 'samples.demo', 'a'.repeat(64)), - manifest, - consentFingerprint: 'sha256-current', - contentHash: 'a'.repeat(64), - isDev: false - } - const lock = emptyPluginLockfile() - lock.plugins[plugin.pluginKey] = { - pluginKey: plugin.pluginKey, - version: '1.0.0', - source: { - kind: 'marketplace', - marketplace: { - url: 'https://github.com/acme/catalog.git', - ref: 'stable', - resolvedCommit: 'f'.repeat(40) - }, - plugin: { url: 'https://github.com/samples/demo.git', ref: 'main' } - }, - resolvedCommit: 'c'.repeat(40), - contentHash: 'a'.repeat(64), - consentFingerprint: 'sha256-current', - installedAt: 1 - } - - const [entry] = await buildPluginList(serviceWith(plugin), lock) - - expect(entry?.source?.marketplace).toEqual({ - reference: 'https://github.com/acme/catalog.git', - ref: 'stable', - resolvedCommit: 'f'.repeat(40) - }) - }) }) diff --git a/src/main/runtime/orca-runtime-tests/mobile-web-bundle-capability.spec.ts b/src/main/runtime/orca-runtime-tests/mobile-web-bundle-capability.spec.ts deleted file mode 100644 index 991d2044fb1..00000000000 --- a/src/main/runtime/orca-runtime-tests/mobile-web-bundle-capability.spec.ts +++ /dev/null @@ -1,60 +0,0 @@ -import { mkdtempSync, rmSync, writeFileSync } from 'node:fs' -import { tmpdir } from 'node:os' -import { join } from 'node:path' -import { afterEach, describe, expect, it } from 'vitest' -import { createRuntime } from '../orca-runtime-test-fixtures.spec' -import { resetBundledMobileWebBundleCacheForTests } from '../bundled-mobile-web-bundle' -import { - installMobileWebBundleAppPath, - writeSyntheticMobileWebBundle -} from '../rpc/methods/mobile-web-bundle.test-fixture' -import { MOBILE_WEB_BUNDLE_CAPABILITY } from '../../../shared/mobile-web-bundle/mobile-web-bundle-capability' - -// The aggregate shares this process cache; each fixture must restore it. -function statusCapabilitiesForInstall(appPath: string): readonly string[] { - resetBundledMobileWebBundleCacheForTests() - installMobileWebBundleAppPath(appPath) - const { capabilities } = createRuntime().getStatus() - if (!capabilities) { - // A missing capability list would make absence assertions pass vacuously. - throw new Error('status.get answered no capability list') - } - return capabilities -} - -function withInstallRoot(prefix: string, run: (install: string) => void): void { - const install = mkdtempSync(join(tmpdir(), prefix)) - try { - run(install) - } finally { - rmSync(install, { recursive: true, force: true }) - } -} - -describe('OrcaRuntimeService mobile web bundle capability', () => { - afterEach(() => { - resetBundledMobileWebBundleCacheForTests() - }) - - it('omits the mobile web bundle capability where the install carries no bundle', () => { - withInstallRoot('orca-mobile-web-absent-', (install) => { - expect(statusCapabilitiesForInstall(install)).not.toContain(MOBILE_WEB_BUNDLE_CAPABILITY) - }) - }) - - it('advertises the mobile web bundle capability where the install carries one', () => { - withInstallRoot('orca-mobile-web-present-', (install) => { - writeSyntheticMobileWebBundle(join(install, 'out', 'mobile-web'), 4) - expect(statusCapabilitiesForInstall(install)).toContain(MOBILE_WEB_BUNDLE_CAPABILITY) - }) - }) - - it('omits the capability where the bundle manifest does not parse', () => { - withInstallRoot('orca-mobile-web-unparseable-', (install) => { - const root = join(install, 'out', 'mobile-web') - writeSyntheticMobileWebBundle(root, 5) - writeFileSync(join(root, 'manifest.json'), '{"schemaVersion":2}', 'utf8') - expect(statusCapabilitiesForInstall(install)).not.toContain(MOBILE_WEB_BUNDLE_CAPABILITY) - }) - }) -}) diff --git a/src/main/runtime/orca-runtime.test.ts b/src/main/runtime/orca-runtime.test.ts index 6cec562476a..4496ae60547 100644 --- a/src/main/runtime/orca-runtime.test.ts +++ b/src/main/runtime/orca-runtime.test.ts @@ -12,7 +12,6 @@ await import('./orca-runtime-tests/paired-settings.spec') await import('./orca-runtime-tests/runtime-availability.spec') await import('./orca-runtime-tests/browser-capabilities.spec') await import('./orca-runtime-tests/browser-capabilities-part-02.spec') -await import('./orca-runtime-tests/mobile-web-bundle-capability.spec') await import('./orca-runtime-tests/managed-server-capability.spec') await import('./orca-runtime-tests/window-authority.spec') await import('./orca-runtime-tests/terminal-handles.spec') diff --git a/src/main/runtime/rpc/methods/orchestration/worker/worker-stop-capability.test.ts b/src/main/runtime/rpc/methods/orchestration/worker/worker-stop-capability.test.ts deleted file mode 100644 index 61832c64c6b..00000000000 --- a/src/main/runtime/rpc/methods/orchestration/worker/worker-stop-capability.test.ts +++ /dev/null @@ -1,59 +0,0 @@ -import { describe, expect, it, vi } from 'vitest' -import { ORCHESTRATION_CONTRACT_RUNTIME_CAPABILITY } from '../../../../../../shared/protocol-version' -import type { OrcaRuntimeService } from '../../../../orca-runtime' -import type { OrchestrationDb } from '../../../../orchestration/db' -import { ORCHESTRATION_WORKER_STOP_METHODS } from './worker-stop' - -describe('federated worker stop capability', () => { - it('does not trust a legacy server stop receipt', async () => { - const markWorkerStopUnknown = vi.fn((_dispatchId: string, reason: string) => ({ - state: 'stop_unknown', - last_error: reason - })) - const db = { - getFederatedDispatch: vi.fn(() => ({ - dispatch_id: 'ctx_remote', - environment_id: 'environment_linux', - environment_name: 'linux', - peer_fingerprint: 'peer-linux' - })), - beginWorkerStop: vi.fn(() => ({ disposition: 'started' })), - markWorkerStopUnknown - } as unknown as OrchestrationDb - const callOrchestrationWorkerServer = vi.fn(async (_environmentId, method) => { - if (method === 'status.get') { - return { capabilities: [ORCHESTRATION_CONTRACT_RUNTIME_CAPABILITY] } - } - return { state: 'stopped', processAction: 'closed_agent_terminal' } - }) - const runtime = { - getOrchestrationDb: () => db, - getRuntimeId: () => 'runtime_current', - resolveOrchestrationWorkerServer: () => ({ - environmentId: 'environment_linux', - name: 'linux', - peerFingerprint: 'peer-linux' - }), - callOrchestrationWorkerServer - } as unknown as OrcaRuntimeService - const method = ORCHESTRATION_WORKER_STOP_METHODS[0]! - - await expect( - method.handler(method.params!.parse({ dispatch: 'ctx_remote' }), { - runtime, - orchestrationMutation: { - callerFingerprint: 'coordinator', - requestId: 'request_stop', - method: 'orchestration.workerStop', - payloadHash: 'hash' - } - }) - ).resolves.toMatchObject({ state: 'stop_unknown', processAction: 'none' }) - expect(db.beginWorkerStop).toHaveBeenCalledWith('ctx_remote', 'runtime_current') - expect(markWorkerStopUnknown).toHaveBeenCalledWith( - 'ctx_remote', - 'Connected server linux cannot prove the worker stop outcome.' - ) - expect(callOrchestrationWorkerServer).toHaveBeenCalledTimes(1) - }) -}) diff --git a/src/main/runtime/rpc/orcad-feature-readiness-manifest.test.ts b/src/main/runtime/rpc/orcad-feature-readiness-manifest.test.ts deleted file mode 100644 index 9192d7cbe8f..00000000000 --- a/src/main/runtime/rpc/orcad-feature-readiness-manifest.test.ts +++ /dev/null @@ -1,129 +0,0 @@ -import { readFileSync } from 'node:fs' -import { join } from 'node:path' -import { describe, expect, it } from 'vitest' -import { ALL_RPC_METHODS } from './methods' -import { buildRegistry } from './core' -import { ACCOUNT_METHODS } from './methods/accounts' -import { resolveOrcadFeatureRequirement, type OrcadFeature } from './orcad-feature-readiness' - -// Every method's declared needs for these params; param-level ones read them. -const REGISTRY = buildRegistry(ALL_RPC_METHODS) -const requiresOf = (name: string, params: unknown = {}): readonly OrcadFeature[] => { - const method = REGISTRY.get(name) - expect(method, name).toBeDefined() - return resolveOrcadFeatureRequirement(method?.requires, params) -} - -// Launches that start an agent (and so read its account home); a terminal create is one only with an agent. -const AGENT_LAUNCHES = [ - 'terminal.createAgentSession', - 'agentSession.create', - 'git.generateCommitMessage', - 'orchestration.workerStart', - 'orchestration.federationAttachStart' -] -const PARAM_GATED_LAUNCHES = [ - 'terminal.ensureAgentSession', - 'terminal.create', - 'session.tabs.createTerminal', - 'agent.launch', - 'agent.launchReplay' -] - -describe('which RPC methods wait for an orcad startup phase', () => { - it('gates the account services, search index, orchestration and orphan adoption', () => { - for (const method of ALL_RPC_METHODS) { - const expected: OrcadFeature[] = [] - // Claude/Codex account services only; usage credentials and Antigravity live elsewhere. - if ( - ACCOUNT_METHODS.some((account) => account.name === method.name) && - method.name !== 'accounts.setHostLaunchEnv' && - method.name !== 'accounts.unsubscribe' - ) { - expected.push('accounts') - } - if (AGENT_LAUNCHES.includes(method.name)) { - expected.push('accounts') - } - if (method.name.startsWith('orchestration.')) { - expected.push('orchestration') - } - if (/^aiVault\.(searchSessions|searchStatus|setSearchEnabled)$/.test(method.name)) { - expected.push('sessionSearch') - } - if (method.name === 'terminal.adoptOrphans') { - expected.push('unshownPlacement') - } - if (PARAM_GATED_LAUNCHES.includes(method.name)) { - expect(typeof method.requires, method.name).toBe('function') - continue - } - expect({ name: method.name, requires: requiresOf(method.name) }).toEqual({ - name: method.name, - requires: expected - }) - } - }) - - it('gates a terminal create only when it starts an agent; plain shells stay ungated', () => { - for (const name of ['terminal.create', 'session.tabs.createTerminal']) { - expect(requiresOf(name, {}), name).toEqual([]) - expect(requiresOf(name, { command: 'ls -la' }), name).toEqual([]) - expect(requiresOf(name, { command: 'claude --resume' }), name).toEqual(['accounts']) - expect(requiresOf(name, { command: 'codex' }), name).toEqual(['accounts']) - expect(requiresOf(name, { launchAgent: 'claude' }), name).toEqual(['accounts']) - expect(requiresOf(name, { launchConfig: { agentArgs: '', agentEnv: {} } }), name).toEqual([ - 'accounts' - ]) - expect(requiresOf(name, { resumeProviderSession: { id: 's' } }), name).toEqual(['accounts']) - } - expect(requiresOf('session.tabs.createTerminal', { agent: 'claude' })).toEqual(['accounts']) - }) - - it('gates only an explicit ensureAgentSession; an attach is restore latency', () => { - expect(requiresOf('terminal.ensureAgentSession', { kind: 'explicit' })).toEqual(['accounts']) - expect(requiresOf('terminal.ensureAgentSession', { kind: 'automatic' })).toEqual([]) - }) - - it('gates agent.launch unless it reuses a live terminal', () => { - for (const name of ['agent.launch', 'agent.launchReplay']) { - expect(requiresOf(name, {}), name).toEqual(['accounts']) - expect(requiresOf(name, { reuseTerminal: { handle: 'h' } }), name).toEqual([]) - } - }) - - it('never gates what every client calls at connect', () => { - for (const name of [ - 'status.get', - 'repo.list', - 'session.tabs.subscribe', - 'terminal.list', - 'worktree.teardownMissingTerminals', - 'accounts.setHostLaunchEnv' - ]) { - expect(ALL_RPC_METHODS.some((method) => method.name === name)).toBe(true) - expect(requiresOf(name)).toEqual([]) - } - }) - - // No behavior change yet: orcad opens every latch before RPC starts accepting calls. - it('opens every latch before orcad starts RPC', () => { - const read = (file: string): string => - readFileSync(join(__dirname, '../../orcad', file), 'utf8') - const entry = read('orcad-entry.ts') - const restore = read('orcad-restored-terminal-authority.ts') - const rpcStart = entry.indexOf("await timed('rpc', () => rpc.start())") - const restored = entry.indexOf('await restoreOrcadTerminalAuthority(') - expect(rpcStart).toBeGreaterThan(0) - expect(restored).toBeGreaterThan(0) - expect(restored).toBeLessThan(rpcStart) - for (const feature of ['accounts', 'sessionSearch']) { - const opened = entry.indexOf(`featureReadiness.open('${feature}')`) - expect(opened, feature).toBeGreaterThan(0) - expect(opened, feature).toBeLessThan(rpcStart) - } - for (const feature of ['orchestration', 'unshownPlacement']) { - expect(restore).toContain(`readiness.open('${feature}')`) - } - }) -}) diff --git a/src/main/runtime/runtime-client-settings-projections.test.ts b/src/main/runtime/runtime-client-settings-projections.test.ts index a1f939c6d8c..6df73cf14c3 100644 --- a/src/main/runtime/runtime-client-settings-projections.test.ts +++ b/src/main/runtime/runtime-client-settings-projections.test.ts @@ -1,6 +1,5 @@ import { vi } from 'vitest' import './runtime-client-settings-linear-team-projection.spec' -import './runtime-client-settings-source-control-projection.spec' vi.mock('../claude/agent-view-service', () => ({ getClaudeAgentViewService: () => ({ reconcile: async () => {} }) diff --git a/src/main/runtime/runtime-client-settings-source-control-projection.spec.ts b/src/main/runtime/runtime-client-settings-source-control-projection.spec.ts deleted file mode 100644 index 7b28ea7f778..00000000000 --- a/src/main/runtime/runtime-client-settings-source-control-projection.spec.ts +++ /dev/null @@ -1,91 +0,0 @@ -import { describe, expect, it } from 'vitest' -import { RuntimeClientSettingsController } from './runtime-client-settings' -import { createGlobalSettingsFixture } from '../../shared/global-settings-test-fixture' -import type { GlobalSettings } from '../../shared/global-settings-types' -import { SourceControlAiSettings } from '../../shared/rpc-contract/git-params' -import { liftActionPermissionArgs } from '../../shared/source-control-action-permission-lift' -import { getDefaultSourceControlAiSettings } from '../../shared/source-control-ai' - -// settings.get is an allowlist: omitted per-action agent recipes look unsaved to paired AI buttons. -function projectionOf(settings: Partial) { - // oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: get() reads nothing but store.getSettings(); every other RuntimeStore member is unreachable from that path. - return new RuntimeClientSettingsController({ getSettings: () => settings } as never).get() -} - -function hostSettings(sourceControlAi: GlobalSettings['sourceControlAi']): Partial { - return { ...createGlobalSettingsFixture({ workspaceDir: '/w' }), sourceControlAi } -} - -describe('RuntimeClientSettingsController source control launch recipes', () => { - it('publishes the recipe saved for a launch action', () => { - const recipe = liftActionPermissionArgs({ - agentId: 'codex' as const, - agentArgs: '-c model=o3 --fast', - launchPreferences: { - agentId: 'claude', - permissionMode: 'ask', - modelSelection: { - base: 'inherit', - options: { effort: { agentId: 'codex', value: null } } - } - } - }) - const source = { ...getDefaultSourceControlAiSettings(), actions: { fixChecks: recipe } } - expect(SourceControlAiSettings.parse(source).actions?.fixChecks).toEqual(recipe) - const projected = projectionOf(hostSettings(source)) - expect(projected.sourceControlAi.actions.fixChecks?.launchPreferences).toEqual( - recipe.launchPreferences - ) - expect(projected.sourceControlAi.actions.fixChecks).toMatchObject({ - agentId: 'codex', - agentArgs: '--fast', - argumentChoices: { syntaxAgent: 'codex', nodes: [{ at: 0, text: '-c model=o3' }] } - }) - }) - - it('publishes a recipe saved under the legacy launch defaults key', () => { - const projected = projectionOf( - hostSettings({ - ...getDefaultSourceControlAiSettings(), - actions: undefined, - launchActionDefaults: { resolveConflicts: { agentId: 'claude' } } - }) - ) - expect(projected.sourceControlAi.actions.resolveConflicts).toMatchObject({ agentId: 'claude' }) - }) - - it('publishes launch actions only, not the text generation recipes', () => { - const projected = projectionOf(hostSettings(getDefaultSourceControlAiSettings())) - expect(Object.keys(projected.sourceControlAi.actions).sort()).toEqual([ - 'fixChecks', - 'fixCommitFailure', - 'fixPushFailure', - 'resolveComments', - 'resolveConflicts' - ]) - }) -}) - -describe('RuntimeClientSettingsController source control list order', () => { - it('publishes the group order so paired clients list changes the same way', () => { - expect(projectionOf({ sourceControlGroupOrder: 'staged-first' }).sourceControlGroupOrder).toBe( - 'staged-first' - ) - expect(projectionOf({}).sourceControlGroupOrder).toBe('changes-first') - }) - - it('publishes the compare base and whitespace choices desktop diffs use', () => { - const projected = projectionOf({ - sourceControlCompareAgainstUpstream: true, - diffShowWhitespace: true - }) - expect(projected).toMatchObject({ - sourceControlCompareAgainstUpstream: true, - diffShowWhitespace: true - }) - expect(projectionOf({})).toMatchObject({ - sourceControlCompareAgainstUpstream: false, - diffShowWhitespace: false - }) - }) -})