feat: show runtime checklists and support remote Continue

Display native OpenCode and Continue checklists alongside scheduled tasks, preserving request ownership, explicit clears and saved progress. Bundle both remote runtimes and support managed Continue installation, model routing, cancellation and restart continuation.

Validation: real local and Linux remote model checks, full Electron remote Continue flows, typecheck and lint passed. Full suite: 4707 passed, 67 skipped, one OpenCode compaction failure passed targeted rerun.

Release note: Display native execution checklists above conversations and support managed remote Continue projects with saved progress and cancellation recovery.
This commit is contained in:
mesalogo
2026-09-19 10:47:27 +08:00
parent 164e89253a
commit c7a93803c9
78 changed files with 2266 additions and 238 deletions
+25 -11
View File
@@ -16,6 +16,7 @@ import {
writeFile
} from 'node:fs/promises'
import { basename, join, resolve } from 'node:path'
import { isDeepStrictEqual } from 'node:util'
import { agentPlatformSchema, type AgentPlatform } from '../../shared/agent-target'
import {
agentPackageArchiveName,
@@ -24,6 +25,7 @@ import {
agentPackageInventorySchema,
type AgentPackageCatalog,
type AgentPackageCatalogEntry,
type AgentPackageDescriptor,
type AgentPackageDownloadProgress,
type AgentPackageInventory
} from '../../shared/agent-package-contracts'
@@ -105,6 +107,7 @@ export type AgentPackageManagerOptions = {
}
export type VerifiedRemoteAgentInstallCandidate = {
additionalRuntimes?: AgentPackageDescriptor['additionalRuntimes']
source: UpdateSource
platform: AgentPlatform
architecture: AgentArchitecture
@@ -115,8 +118,8 @@ export type VerifiedRemoteAgentInstallCandidate = {
minor: number
}
remoteRuntime: {
runtimeId: 'opencode'
provider: 'opencode'
runtimeId: 'opencode' | 'continue'
provider: 'opencode' | 'continue'
version: string
bundleDigest: string
protocol: {
@@ -138,8 +141,8 @@ export type VerifiedRemoteAgentEnvironmentCatalog = {
expected: {
agent: { version: string }
runtimes: Array<{
runtimeId: 'opencode'
provider: 'opencode'
runtimeId: 'opencode' | 'continue'
provider: 'opencode' | 'continue'
version: string
}>
}
@@ -170,15 +173,17 @@ export type AcquireInstallArchiveOptions = {
function expectedCatalog(
agentVersion: string,
runtimeVersion: string
runtimeVersion: string,
runtimeId: 'opencode' | 'continue' = 'opencode',
additionalRuntimes: NonNullable<AgentPackageDescriptor['additionalRuntimes']> = []
): VerifiedRemoteAgentEnvironmentCatalog['expected'] {
return {
agent: { version: agentVersion },
runtimes: [{
runtimeId: 'opencode',
provider: 'opencode',
runtimeId,
provider: runtimeId,
version: runtimeVersion
}]
}, ...additionalRuntimes.map(({ runtimeId, provider, version }) => ({ runtimeId, provider, version }))]
}
}
@@ -193,6 +198,7 @@ function candidateFromCatalogEntry(
version: entry.version,
minimumDesktopVersion: entry.minimumDesktopVersion,
agentProtocol: { ...entry.agentProtocol },
...(entry.additionalRuntimes ? { additionalRuntimes: structuredClone(entry.additionalRuntimes) } : {}),
remoteRuntime: {
...entry.remoteRuntime,
protocol: { ...entry.remoteRuntime.protocol }
@@ -217,6 +223,7 @@ function candidateFromVerifiedRecord(
version: descriptor.version,
minimumDesktopVersion: descriptor.minimumDesktopVersion,
agentProtocol: { ...descriptor.agentProtocol },
...(descriptor.additionalRuntimes ? { additionalRuntimes: structuredClone(descriptor.additionalRuntimes) } : {}),
remoteRuntime: {
...descriptor.remoteRuntime,
protocol: { ...descriptor.remoteRuntime.protocol }
@@ -399,7 +406,9 @@ export class AgentPackageManager {
return {
expected: expectedCatalog(
installed.verified.descriptor.version,
installed.verified.descriptor.remoteRuntime.version
installed.verified.descriptor.remoteRuntime.version,
installed.verified.descriptor.remoteRuntime.runtimeId,
installed.verified.descriptor.additionalRuntimes
),
candidate: null,
candidateFailure: {
@@ -414,7 +423,9 @@ export class AgentPackageManager {
}
const expected = expectedCatalog(
entry.version,
entry.remoteRuntime.version
entry.remoteRuntime.version,
entry.remoteRuntime.runtimeId,
entry.additionalRuntimes
)
try {
return {
@@ -710,6 +721,7 @@ export class AgentPackageManager {
candidate.agentProtocol.major &&
descriptor.agentProtocol.minor ===
candidate.agentProtocol.minor &&
isDeepStrictEqual(descriptor.additionalRuntimes ?? [], candidate.additionalRuntimes ?? []) &&
descriptor.remoteRuntime.runtimeId ===
candidate.remoteRuntime.runtimeId &&
descriptor.remoteRuntime.provider ===
@@ -835,6 +847,7 @@ export class AgentPackageManager {
entry.agentProtocol.major ||
descriptor.agentProtocol.minor !==
entry.agentProtocol.minor ||
!isDeepStrictEqual(descriptor.additionalRuntimes ?? [], entry.additionalRuntimes ?? []) ||
descriptor.remoteRuntime.runtimeId !==
entry.remoteRuntime.runtimeId ||
descriptor.remoteRuntime.provider !==
@@ -1981,6 +1994,7 @@ function assertCandidateMatchesCatalogEntry(
entry.agentProtocol.major ||
candidate.agentProtocol.minor !==
entry.agentProtocol.minor ||
!isDeepStrictEqual(candidate.additionalRuntimes ?? [], entry.additionalRuntimes ?? []) ||
candidate.remoteRuntime.runtimeId !==
entry.remoteRuntime.runtimeId ||
candidate.remoteRuntime.provider !==
@@ -2218,4 +2232,4 @@ async function writeAll(
}
offset += bytesWritten
}
}
}
@@ -223,30 +223,35 @@ export async function verifyExtractedAgentPackage(options: {
)
}
}
for (const runtime of [descriptor.remoteRuntime, ...(descriptor.additionalRuntimes ?? [])]) {
const runtimeBundle =
await loadVerifiedRemoteRuntimeResourceBundle(
runtimePaths,
descriptor.architecture,
{
runtimeId: runtime.runtimeId,
verificationEnvironment:
options.verificationEnvironment ?? 'production'
}
)
if (
runtimeBundle.manifest.runtimeId !== runtime.runtimeId ||
runtimeBundle.manifest.provider !== runtime.provider ||
runtimeBundle.manifest.platform !== descriptor.platform ||
runtimeBundle.manifest.runtimeVersion !==
descriptor.remoteRuntime.version ||
runtime.version ||
runtimeBundle.manifest.bundleDigest !==
descriptor.remoteRuntime.bundleDigest ||
runtime.bundleDigest ||
runtimeBundle.manifest.protocol.major !==
descriptor.remoteRuntime.protocol.major ||
runtime.protocol.major ||
runtimeBundle.manifest.protocol.minor !==
descriptor.remoteRuntime.protocol.minor
runtime.protocol.minor
) {
throw new Error(
'Agent package descriptor does not match its Runtime bundle'
)
}
}
return {
rootDirectory,
descriptor,
@@ -765,4 +770,4 @@ export function compareSemanticVersions(
return leftIdentifier > rightIdentifier ? 1 : -1
}
return 0
}
}
@@ -1,4 +1,4 @@
import { join } from 'node:path'
import { dirname, join } from 'node:path'
import type { AgentArchitecture } from '../../shared/agent-installation-contracts'
export type BundledRemoteRuntimeResourcePaths = {
@@ -72,7 +72,8 @@ export function resolveBundledRemoteRuntimeResourcePaths(
export function getBundledRemoteRuntimeRoot(
paths: BundledRemoteRuntimeResourcePaths,
architecture: AgentArchitecture
architecture: AgentArchitecture,
runtimeId: 'opencode' | 'continue' = 'opencode'
): string {
return paths.runtimeRoots[architecture]
return runtimeId === 'opencode' ? paths.runtimeRoots[architecture] : join(dirname(paths.runtimeRoots[architecture]), runtimeId)
}
@@ -103,7 +103,7 @@ export type PackageInstallerAgentIdentity = {
}
export type PackageInstallerRuntimeIdentity = {
runtimeId: 'opencode'
runtimeId: 'opencode' | 'continue'
runtimeVersion: string
bundleDigest: string
manifestDigest: string
@@ -121,6 +121,7 @@ export type PackageInstallerResult = {
archiveSha256: string
agent: PackageInstallerAgentIdentity
runtime: PackageInstallerRuntimeIdentity
additionalRuntimes?: PackageInstallerRuntimeIdentity[]
}
export type PackageInstallerEvent =
@@ -166,6 +167,7 @@ type VerifiedPackage = {
runtimeManifestDigest: string
agent: PackageInstallerAgentIdentity
runtime: PackageInstallerRuntimeIdentity
additionalRuntimes?: PackageInstallerRuntimeIdentity[]
}
type PreparedState = {
@@ -178,6 +180,7 @@ type PreparedState = {
packageSignatureSha256: string
agent: PackageInstallerAgentIdentity
runtime: PackageInstallerRuntimeIdentity
additionalRuntimes?: PackageInstallerRuntimeIdentity[]
}
function canonicalJson(value: unknown): string {
@@ -811,6 +814,7 @@ function assertOuterDescriptor(
'signingKeyId',
'agentProtocol',
'remoteRuntime',
...(descriptor.additionalRuntimes === undefined ? [] : ['additionalRuntimes']),
'contentDigest',
'files'
],
@@ -839,8 +843,8 @@ function assertOuterDescriptor(
'Agent package Runtime identity'
)
if (
remoteRuntime.runtimeId !== 'opencode' ||
remoteRuntime.provider !== 'opencode' ||
!['opencode', 'continue'].includes(String(remoteRuntime.runtimeId)) ||
remoteRuntime.provider !== remoteRuntime.runtimeId ||
!versionPattern.test(String(remoteRuntime.version)) ||
!digestPattern.test(String(remoteRuntime.bundleDigest))
) {
@@ -848,6 +852,18 @@ function assertOuterDescriptor(
}
protocol(descriptor.agentProtocol, 'Agent package protocol')
protocol(remoteRuntime.protocol, 'Agent package Runtime protocol')
if (descriptor.additionalRuntimes !== undefined) {
if (!Array.isArray(descriptor.additionalRuntimes) || descriptor.additionalRuntimes.length > 1) throw new Error('Invalid additional Runtimes')
for (const extra of descriptor.additionalRuntimes) {
if (!isRecord(extra)) throw new Error('Invalid additional Runtime')
exactKeys(extra, ['runtimeId', 'provider', 'version', 'bundleDigest', 'protocol'], 'Additional Runtime')
if (!['opencode', 'continue'].includes(String(extra.runtimeId)) || extra.runtimeId === remoteRuntime.runtimeId ||
extra.provider !== extra.runtimeId || !versionPattern.test(String(extra.version)) || !digestPattern.test(String(extra.bundleDigest))) {
throw new Error('Invalid additional Runtime identity')
}
protocol(extra.protocol, 'Additional Runtime protocol')
}
}
const { contentDigest: _contentDigest, ...content } = descriptor
void _contentDigest
if (
@@ -864,6 +880,11 @@ function assertOuterDescriptor(
'Agent package'
)
const files = parseFiles(descriptor.files, 'Agent package')
const runtimePrefixes = [remoteRuntime, ...((descriptor.additionalRuntimes as JsonRecord[] | undefined) ?? [])]
.map(runtime => `runtime/${String(runtime.runtimeId)}/${String(runtime.bundleDigest).slice(7)}/`)
if (files.some(file => file.path.startsWith('runtime/') && !runtimePrefixes.some(prefix => file.path.startsWith(prefix)))) {
throw new Error('Agent package contains an undeclared Runtime')
}
const expected = new Set([
...files.map((file) => file.path),
PACKAGE_DESCRIPTOR,
@@ -1093,8 +1114,9 @@ function assertRuntimeManifest(
],
'Runtime manifest'
)
const runtime = isRecord(lock.runtimes) && isRecord(lock.runtimes.opencode)
? lock.runtimes.opencode
const remote = descriptor.remoteRuntime as JsonRecord
const runtime = isRecord(lock.runtimes) && isRecord(lock.runtimes[String(remote.runtimeId)])
? lock.runtimes[String(remote.runtimeId)] as JsonRecord
: undefined
const targetKey = descriptor.platform === 'darwin' ? 'darwin-arm64' : String(descriptor.architecture)
const target =
@@ -1103,12 +1125,11 @@ function assertRuntimeManifest(
isRecord(runtime.targets[targetKey])
? runtime.targets[targetKey]
: undefined
const remote = descriptor.remoteRuntime as JsonRecord
if (
manifest.formatVersion !== 2 ||
manifest.product !== 'GoodBuddy' ||
manifest.runtimeId !== 'opencode' ||
manifest.provider !== 'opencode' ||
manifest.runtimeId !== remote.runtimeId ||
manifest.provider !== remote.provider ||
manifest.platform !== descriptor.platform ||
manifest.architecture !== descriptor.architecture ||
!versionPattern.test(String(manifest.runtimeVersion)) ||
@@ -1195,14 +1216,14 @@ function assertRuntimeManifest(
)
const files = parseFiles(manifest.files, 'Runtime manifest')
const digestDirectory = String(manifest.bundleDigest).slice('sha256:'.length)
const prefix = `runtime/opencode/${digestDirectory}/`
const prefix = `runtime/${String(remote.runtimeId)}/${digestDirectory}/`
const expectedRuntimePaths = new Set([
...files.map((file) => `${prefix}${file.path}`),
`${prefix}manifest.json`,
`${prefix}manifest.sig`
])
const actualRuntimePaths = [...packageFiles.keys()].filter((path) =>
path.startsWith('runtime/')
path.startsWith(`runtime/${String(remote.runtimeId)}/`)
)
if (
actualRuntimePaths.length !== expectedRuntimePaths.size ||
@@ -1214,7 +1235,7 @@ function assertRuntimeManifest(
if (!safeManifestPath(file.path)) {
throw new Error(`Runtime manifest path is invalid: ${file.path}`)
}
const expectedMode = file.path === 'bin/opencode' ? '0755' : '0644'
const expectedMode = file.path === entrypoint.path ? '0755' : '0644'
const outer = packageFiles.get(`${prefix}${file.path}`)
if (
file.mode !== expectedMode ||
@@ -1232,20 +1253,20 @@ function assertRuntimeManifest(
'Runtime manifest'
)
const openCodeLicense = runtimeLicenses.find(
(license) => license.package === 'opencode-ai'
(license) => license.package === (remote.runtimeId === 'continue' ? '@continuedev/cli' : 'opencode-ai')
)
if (
openCodeLicense?.version !== manifest.runtimeVersion ||
openCodeLicense?.spdx !== 'MIT' ||
openCodeLicense?.path !== 'licenses/opencode-MIT.txt'
openCodeLicense?.spdx !== (remote.runtimeId === 'continue' ? 'Apache-2.0' : 'MIT') ||
openCodeLicense?.path !== (remote.runtimeId === 'continue' ? 'licenses/continue-Apache-2.0.txt' : 'licenses/opencode-MIT.txt')
) {
throw new Error('Runtime license declaration is invalid')
}
if (
entrypoint.path !== 'bin/opencode' ||
entrypoint.path !== (remote.runtimeId === 'continue' ? 'lib/continue/dist/cn.js' : 'bin/opencode') ||
!files.some(
(file) =>
file.path === 'bin/opencode' &&
file.path === entrypoint.path &&
file.sha256 === entrypoint.sha256
)
) {
@@ -1299,12 +1320,13 @@ function elfArchitecture(bytes: Buffer, platform: Platform): Architecture | unde
function assertExtractedArchitectures(
preparedRoot: string,
architecture: Architecture,
platform: Platform
platform: Platform,
runtimeId: 'opencode' | 'continue'
): void {
for (const path of [
join(preparedRoot, 'agent', 'node'),
...nativeKoffiPaths(platform, architecture).map(path => join(preparedRoot, 'agent', path)),
join(preparedRoot, 'runtime', 'bin', 'opencode')
...(runtimeId === 'opencode' ? [join(preparedRoot, 'runtime', 'bin', 'opencode')] : [])
]) {
if (
elfArchitecture(readFileHeader(path, 64), platform) !== architecture
@@ -1460,13 +1482,14 @@ function verifyArchive(
const runtimeDigest = String(
(descriptor.remoteRuntime as JsonRecord).bundleDigest
).slice('sha256:'.length)
const runtimeId = (descriptor.remoteRuntime as JsonRecord).runtimeId as 'opencode' | 'continue'
const runtimeManifestBytes = readEntry(
handle,
entries,
`runtime/opencode/${runtimeDigest}/manifest.json`
`runtime/${runtimeId}/${runtimeDigest}/manifest.json`
)
assertDeclaredMetadata(
`runtime/opencode/${runtimeDigest}/manifest.json`,
`runtime/${runtimeId}/${runtimeDigest}/manifest.json`,
runtimeManifestBytes,
declaredMap
)
@@ -1481,7 +1504,7 @@ function verifyArchive(
declaredMap
)
const runtimeSignaturePath =
`runtime/opencode/${runtimeDigest}/manifest.sig`
`runtime/${runtimeId}/${runtimeDigest}/manifest.sig`
const runtimeSignatureBytes = readEntry(
handle,
entries,
@@ -1503,6 +1526,16 @@ function verifyArchive(
descriptor,
declaredMap
)
const additionalRuntimes = (descriptor.additionalRuntimes as JsonRecord[] | undefined)?.map(remote => {
const prefix = `runtime/${String(remote.runtimeId)}/${String(remote.bundleDigest).slice(7)}/`
const bytes = readEntry(handle, entries, `${prefix}manifest.json`)
const signature = readEntry(handle, entries, `${prefix}manifest.sig`)
assertDeclaredMetadata(`${prefix}manifest.json`, bytes, declaredMap)
assertDeclaredMetadata(`${prefix}manifest.sig`, signature, declaredMap)
const verified = assertRuntimeManifest(bytes, signature, registry,
parseCanonicalJson(remoteRuntimeLockBytes, 'Remote Runtime lock'), { ...descriptor, remoteRuntime: remote }, declaredMap)
return runtimeIdentity(verified, descriptor)
})
if (!sameSnapshot(snapshot, fstatSync(handle))) {
throw new Error('Agent package archive changed during verification')
}
@@ -1522,6 +1555,7 @@ function verifyArchive(
runtimeManifest: runtime.manifest,
agentManifestSha256,
runtimeManifestDigest: runtime.manifestDigest,
...(additionalRuntimes ? { additionalRuntimes } : {}),
agent: {
installationId: `agent-${agentManifestSha256}`,
agentVersion: String(agentManifest.agentVersion),
@@ -1533,7 +1567,7 @@ function verifyArchive(
supervisor: 'detached-on-demand'
},
runtime: {
runtimeId: 'opencode',
runtimeId,
runtimeVersion: String(runtime.manifest.runtimeVersion),
bundleDigest: String(runtime.manifest.bundleDigest),
manifestDigest: runtime.manifestDigest,
@@ -1646,7 +1680,8 @@ function persistPreparedState(
packageSignatureSha256:
sha256(verified.packageSignatureBytes),
agent: verified.agent,
runtime: verified.runtime
runtime: verified.runtime,
...(verified.additionalRuntimes ? { additionalRuntimes: verified.additionalRuntimes } : {})
}
const bytes = prettyBytes(state)
if (bytes.length > MAXIMUM_METADATA_BYTES) {
@@ -1728,7 +1763,10 @@ function extractPayload(
mkdirSync(join(temporary, 'agent'), { recursive: true, mode: 0o700 })
mkdirSync(join(temporary, 'runtime'), { recursive: true, mode: 0o700 })
const runtimePrefix =
`runtime/opencode/${verified.runtime.bundleDigest.slice('sha256:'.length)}/`
`runtime/${verified.runtime.runtimeId}/${verified.runtime.bundleDigest.slice('sha256:'.length)}/`
const extraPrefixes = (verified.additionalRuntimes ?? []).map(runtime => ({
prefix: `runtime/${runtime.runtimeId}/${runtime.bundleDigest.slice(7)}/`, directory: `runtime-${runtime.runtimeId}`
}))
const declared = new Map(
parseFiles(verified.descriptor.files, 'Agent package')
.map((file) => [file.path, file])
@@ -1757,6 +1795,9 @@ function extractPayload(
...name.slice(runtimePrefix.length).split('/')
)
}
for (const extra of extraPrefixes) {
if (name.startsWith(extra.prefix)) target = join(temporary, extra.directory, ...name.slice(extra.prefix.length).split('/'))
}
if (target !== undefined) {
const actual = streamEntry(handle, entry, target)
const expected = declared.get(name)
@@ -1777,7 +1818,11 @@ function extractPayload(
} finally {
closeSync(handle)
}
assertExtractedArchitectures(temporary, verified.agent.architecture, verified.agent.platform)
assertExtractedArchitectures(temporary, verified.agent.architecture, verified.agent.platform, verified.runtime.runtimeId)
if (verified.additionalRuntimes?.some(runtime => runtime.runtimeId === 'opencode') &&
elfArchitecture(readFileHeader(join(temporary, 'runtime-opencode/bin/opencode'), 64), verified.agent.platform) !== verified.agent.architecture) {
throw new Error('Additional OpenCode executable architecture mismatch')
}
chmodSync(temporary, 0o700)
renameSync(temporary, destinationRoot)
} catch (error) {
@@ -1812,7 +1857,8 @@ export function preparePackage(options: InstallerOptions): PackageInstallerResul
status: 'prepared',
archiveSha256: state.archiveSha256,
agent: state.agent,
runtime: state.runtime
runtime: state.runtime,
...(state.additionalRuntimes ? { additionalRuntimes: state.additionalRuntimes } : {})
}
options.emit?.(result)
return result
@@ -1843,7 +1889,8 @@ export function preparePackage(options: InstallerOptions): PackageInstallerResul
status: 'prepared',
archiveSha256: verified.archiveSha256,
agent: verified.agent,
runtime: verified.runtime
runtime: verified.runtime,
...(verified.additionalRuntimes ? { additionalRuntimes: verified.additionalRuntimes } : {})
}
options.emit?.(result)
return result
@@ -2070,6 +2117,7 @@ function readPreparedState(path: string): PreparedState {
'packageSignatureSha256',
'agent',
'runtime'
,...(value.additionalRuntimes === undefined ? [] : ['additionalRuntimes'])
],
'Prepared package state'
)
@@ -2097,6 +2145,11 @@ function readPreparedState(path: string): PreparedState {
throw new Error('Prepared package state is invalid')
}
const agent = value.agent
if (value.additionalRuntimes !== undefined && (!Array.isArray(value.additionalRuntimes) || value.additionalRuntimes.length > 1 ||
value.additionalRuntimes.some(extra => !isRecord(extra) || !['opencode', 'continue'].includes(String(extra.runtimeId)) ||
extra.runtimeId === (value.runtime as JsonRecord).runtimeId || !digestPattern.test(String(extra.bundleDigest))))) {
throw new Error('Invalid prepared additional Runtimes')
}
const runtime = value.runtime
exactKeys(
agent,
@@ -2141,7 +2194,7 @@ function readPreparedState(path: string): PreparedState {
!isSupportedTarget(agent.platform, agent.architecture) ||
!['x64', 'arm64'].includes(String(agent.architecture)) ||
agent.supervisor !== 'detached-on-demand' ||
runtime.runtimeId !== 'opencode' ||
!['opencode', 'continue'].includes(String(runtime.runtimeId)) ||
!versionPattern.test(String(runtime.runtimeVersion)) ||
!digestPattern.test(String(runtime.bundleDigest)) ||
!digestPattern.test(String(runtime.manifestDigest)) ||
@@ -2155,6 +2208,7 @@ function readPreparedState(path: string): PreparedState {
return {
formatVersion: 1,
archiveSha256: String(value.archiveSha256),
...(value.additionalRuntimes ? { additionalRuntimes: value.additionalRuntimes as PackageInstallerRuntimeIdentity[] } : {}),
releaseKeyRegistrySha256:
String(value.releaseKeyRegistrySha256),
agentRuntimeLockSha256:
@@ -2176,7 +2230,7 @@ function readPreparedState(path: string): PreparedState {
supervisor: 'detached-on-demand'
},
runtime: {
runtimeId: 'opencode',
runtimeId: runtime.runtimeId as 'opencode' | 'continue',
runtimeVersion: String(runtime.runtimeVersion),
bundleDigest: String(runtime.bundleDigest),
manifestDigest: String(runtime.manifestDigest),
@@ -2230,6 +2284,16 @@ function readBoundedRegularFile(
}
}
function runtimeIdentity(runtime: { manifest: JsonRecord; manifestDigest: string }, descriptor: JsonRecord): PackageInstallerRuntimeIdentity {
return {
runtimeId: runtime.manifest.runtimeId as 'opencode' | 'continue',
runtimeVersion: String(runtime.manifest.runtimeVersion), bundleDigest: String(runtime.manifest.bundleDigest),
manifestDigest: runtime.manifestDigest, runtimeAdapterDigest: String(runtime.manifest.adapterDigest),
acpCapabilitiesDigest: String(runtime.manifest.acpCapabilitiesDigest), platform: descriptor.platform as Platform,
architecture: descriptor.architecture as Architecture, protocol: protocol(runtime.manifest.protocol, 'Runtime protocol')
}
}
function assertPreparedIdentity(
agentRoot: string,
runtimeRoot: string,
@@ -2238,12 +2302,17 @@ function assertPreparedIdentity(
agentRuntimeLockBytes: Buffer,
remoteRuntimeLockBytes: Buffer,
descriptorBytes: Buffer,
packageSignatureBytes: Buffer
packageSignatureBytes: Buffer,
selectedRuntime = state.runtime
): void {
const descriptor = parseCanonicalJson(
descriptorBytes,
'Prepared Agent package descriptor'
)
const packagedRuntimes = [descriptor.remoteRuntime, ...((descriptor.additionalRuntimes as JsonRecord[] | undefined) ?? [])] as JsonRecord[]
if (packagedRuntimes.length !== 1 + (state.additionalRuntimes?.length ?? 0)) throw new Error('Prepared Runtime inventory differs from package')
const remoteRuntime = packagedRuntimes.find(runtime => runtime.runtimeId === selectedRuntime.runtimeId)
if (!remoteRuntime) throw new Error('Prepared Runtime is not declared in package')
const declared = parseFiles(
descriptor.files,
'Prepared Agent package'
@@ -2323,7 +2392,7 @@ function assertPreparedIdentity(
remoteRuntimeLockBytes,
'Prepared Remote Runtime lock'
),
descriptor,
{ ...descriptor, remoteRuntime },
packageFiles
)
const agentManifestSha256 = sha256(agentManifestBytes)
@@ -2339,7 +2408,7 @@ function assertPreparedIdentity(
supervisor: 'detached-on-demand'
}
const expectedRuntime: PackageInstallerRuntimeIdentity = {
runtimeId: 'opencode',
runtimeId: runtime.manifest.runtimeId as 'opencode' | 'continue',
runtimeVersion: String(runtime.manifest.runtimeVersion),
bundleDigest: String(runtime.manifest.bundleDigest),
manifestDigest: runtime.manifestDigest,
@@ -2356,7 +2425,7 @@ function assertPreparedIdentity(
}
if (
canonicalJson(state.agent) !== canonicalJson(expectedAgent) ||
canonicalJson(state.runtime) !== canonicalJson(expectedRuntime)
canonicalJson(selectedRuntime) !== canonicalJson(expectedRuntime)
) {
throw new Error(
'Prepared package identity does not match the authenticated archive'
@@ -2473,6 +2542,15 @@ export function commitPackage(options: InstallerOptions): PackageInstallerResult
join(managedRoot, 'runtimes', 'release-keys.json'),
join(managedRoot, 'runtimes', 'remote-runtime-lock.json')
] as const
const extraPublications = (state.additionalRuntimes ?? []).map(runtime => {
ensureOwnedManagedHierarchy(home, runtime.runtimeId)
const source = join(payloadRoot, `runtime-${runtime.runtimeId}`)
const destination = join(managedRoot, 'runtimes', runtime.runtimeId, runtime.bundleDigest.slice(7))
assertPreparedIdentity(agentSourceExists ? agentSource : agentDestination,
existsSync(source) ? source : destination, state, releaseKeyRegistryBytes, agentRuntimeLockBytes,
remoteRuntimeLockBytes, descriptorBytes, packageSignatureBytes, runtime)
return { source, destination }
})
if (!agentSourceExists || !runtimeSourceExists) {
assertPreparedIdentity(
agentSourceExists ? agentSource : agentDestination,
@@ -2507,6 +2585,9 @@ export function commitPackage(options: InstallerOptions): PackageInstallerResult
)
)
}
for (const extra of extraPublications) {
if (existsSync(extra.source)) published.push(publishDirectory(extra.source, extra.destination, inputs.operationRoot))
}
progress('publishing-metadata')
writePrivateFileAtomic(
metadataPaths[0],
@@ -2529,7 +2610,8 @@ export function commitPackage(options: InstallerOptions): PackageInstallerResult
status: 'committed',
archiveSha256: state.archiveSha256,
agent: state.agent,
runtime: state.runtime
runtime: state.runtime,
...(state.additionalRuntimes ? { additionalRuntimes: state.additionalRuntimes } : {})
}
options.emit?.(result)
return result
@@ -277,19 +277,25 @@ describe('createManagedRemoteAcpRuntime', () => {
await runtime.dispose()
})
it('rejects non-OpenCode providers before acquiring remote resources', async () => {
it('rejects unsupported providers before acquiring remote resources', async () => {
const fixture = harness({
selection: { provider: 'continue' }
selection: { provider: 'model', profileId: '00000000-0000-4000-8000-000000000001' }
})
await expect(
createManagedRemoteAcpRuntime(fixture.options)
).rejects.toThrow(/OpenCode Runtime/iu)
).rejects.toThrow(/OpenCode or Continue/iu)
expect(
fixture.options.agentServices.connectionManager.acquire
).not.toHaveBeenCalled()
})
it('rejects an installed Runtime that differs from the selected Continue provider', async () => {
const fixture = harness({ selection: { provider: 'continue' } })
await expect(createManagedRemoteAcpRuntime(fixture.options)).rejects.toThrow(/selected provider/iu)
expect(fixture.releaseConnection).toHaveBeenCalledOnce()
})
it('binds the current Agent and Runtime and owns its connection lease', async () => {
const fixture = harness()
const runtime = await createManagedRemoteAcpRuntime(
@@ -307,6 +313,7 @@ describe('createManagedRemoteAcpRuntime', () => {
expect(
fixture.options.runtimeInstallationManager.activateInstalled
).toHaveBeenCalledWith('host-1', {
runtimeId: 'opencode',
agentInstallationId: 'agent-1'
})
expect(
@@ -43,7 +43,7 @@ type ManagedRemoteTransportIdentity = {
}
type ManagedRemoteRuntimeIdentity = {
runtimeId: 'opencode'
runtimeId: 'opencode' | 'continue'
runtimeVersion: string
bundleDigest: string
runtimeAdapterDigest: string
@@ -80,9 +80,9 @@ export async function createManagedRemoteAcpRuntime(
options: ManagedRemoteAcpRuntimeOptions
): Promise<AgentRuntime> {
const descriptor = options.executionSpace
if (options.selection.provider !== 'opencode') {
if (options.selection.provider !== 'opencode' && options.selection.provider !== 'continue') {
throw new Error(
'Managed remote execution requires the OpenCode Runtime'
'Managed remote execution requires OpenCode or Continue'
)
}
const workspaceBinding = workspaceProjectBinding(descriptor)
@@ -114,8 +114,11 @@ export async function createManagedRemoteAcpRuntime(
const runtimeInstallation =
await options.runtimeInstallationManager.activateInstalled(
descriptor.hostId,
{ agentInstallationId: agent.installationId }
{ agentInstallationId: agent.installationId, runtimeId: options.selection.provider }
)
if (runtimeInstallation.runtimeId !== options.selection.provider) {
throw new Error('Installed Runtime does not match the selected provider')
}
const capabilities = await activeConnection.refreshCapabilities()
assertConnectionMatches(workspaceBinding, agent, activeConnection)
const runtime = runtimeIdentityFromCapabilities(
@@ -137,8 +140,8 @@ export async function createManagedRemoteAcpRuntime(
transport: ManagedRemoteTransportIdentity
): AcpRemoteRuntime => {
const remoteOptions: AcpRemoteRuntimeOptions = {
runtimeId: 'opencode',
label: 'OpenCode(远程托管)',
runtimeId: runtime.runtimeId,
label: `${runtime.runtimeId === 'continue' ? 'Continue' : 'OpenCode'}(远程托管)`,
workspacePath: descriptor.remoteRootPath,
identity: {
controllerId,
@@ -180,7 +183,7 @@ export async function createManagedRemoteAcpRuntime(
...(options.usage
? {
usage: {
runtime: 'opencode',
runtime: runtime.runtimeId,
provider: options.usage.provider,
model: options.usage.model
}
@@ -296,7 +299,7 @@ class ManagedRemoteRuntimeResources {
connection: this.connection,
openIdentity: {
bindingId,
runtimeId: 'opencode',
runtimeId: this.runtime.runtimeId,
runtimeBundleDigest: this.runtime.bundleDigest,
workspaceIdentity: this.workspaceIdentity
}
@@ -429,7 +432,7 @@ class ManagedRemoteAcpRuntime implements AgentRuntime {
readonly requiresToolApproval = false
readonly supportsScopedDataTools = false
readonly capability = 'chat' as const
readonly runtimeId = 'opencode' as const
get runtimeId(): 'opencode' | 'continue' { return this.remote.runtimeId as 'opencode' | 'continue' }
#disposePromise?: Promise<void>
#transitionTail: Promise<void> = Promise.resolve()
#activeRuns = 0
@@ -660,7 +663,7 @@ function runtimeIdentityFromCapabilities(
>['capabilities']
): ManagedRemoteRuntimeIdentity {
if (
installation.runtimeId !== 'opencode' ||
(installation.runtimeId !== 'opencode' && installation.runtimeId !== 'continue') ||
(installation.platform !== 'linux' && !(installation.platform === 'darwin' && installation.architecture === 'arm64'))
) {
throw new Error(
@@ -668,7 +671,7 @@ function runtimeIdentityFromCapabilities(
)
}
const runtime: ManagedRemoteRuntimeIdentity = {
runtimeId: 'opencode',
runtimeId: installation.runtimeId,
runtimeVersion: installation.runtimeVersion,
bundleDigest: installation.bundleDigest,
runtimeAdapterDigest: installation.runtimeAdapterDigest,
@@ -718,7 +721,7 @@ function assertRuntimeAdvertised(
(entry) => entry.name === RUNTIME_ACP_CAPABILITY.name
)
const advertised = capabilities.runtimes.filter(
(entry) => entry.runtimeId === 'opencode'
(entry) => entry.runtimeId === runtime.runtimeId
)
if (
capability === undefined ||
@@ -93,7 +93,7 @@ export class ManagedRemoteExecutionServices {
)
).installationId
signal.throwIfAborted()
if (runtimeId !== 'opencode') {
if (runtimeId !== 'opencode' && runtimeId !== 'continue') {
throw new Error(
'Remote Runtime identity is unsupported'
)
@@ -186,6 +186,7 @@ describe('ManagedRemoteProjectRuntimeValidator', () => {
expect(test.calls).toEqual(['model', 'activate', 'refresh'])
expect(test.activateInstalled).toHaveBeenCalledWith('host-1', {
runtimeId: 'opencode',
agentInstallationId,
signal: test.input.signal
})
@@ -209,7 +210,7 @@ describe('ManagedRemoteProjectRuntimeValidator', () => {
expect(() => lease.assertCurrent()).toThrow(/not ready/iu)
})
it('rejects non-OpenCode selections before installation', async () => {
it('rejects unsupported selections before installation', async () => {
const test = harness({
selection: {
provider: 'model',
@@ -218,12 +219,28 @@ describe('ManagedRemoteProjectRuntimeValidator', () => {
})
await expect(test.validator.validate(test.input)).rejects.toThrow(
/OpenCode Runtime/iu
/OpenCode or Continue/iu
)
expect(test.activateInstalled).not.toHaveBeenCalled()
expect(test.refreshCapabilities).not.toHaveBeenCalled()
})
it('validates Continue against its own installed bundle and capability', async () => {
const refreshed = advertisedCapabilities()
refreshed.runtimes[0]!.runtimeId = 'continue'
const test = harness({
selection: { provider: 'continue' },
installed: { ...installation, runtimeId: 'continue' },
refreshed
})
const lease = await test.validator.validate(test.input)
expect(test.activateInstalled).toHaveBeenCalledWith('host-1', {
runtimeId: 'continue', agentInstallationId, signal: test.input.signal
})
expect(() => lease.assertCurrent()).not.toThrow()
lease.release()
})
it('requires the installed Runtime architecture to exactly match the Agent', async () => {
const test = harness({ architecture: 'arm64' })
@@ -46,10 +46,10 @@ export class ManagedRemoteProjectRuntimeValidator
): Promise<RemoteProjectRuntimeValidationLease> {
input.signal.throwIfAborted()
if (
input.selection.provider !== 'opencode'
input.selection.provider !== 'opencode' && input.selection.provider !== 'continue'
) {
throw new Error(
'Managed remote projects require the OpenCode Runtime'
'Managed remote projects require OpenCode or Continue'
)
}
assertInputIdentity(input)
@@ -72,7 +72,8 @@ export class ManagedRemoteProjectRuntimeValidator
input.host.hostId,
{
signal: input.signal,
agentInstallationId: input.agent.installationId
agentInstallationId: input.agent.installationId,
runtimeId: input.selection.provider
}
)
input.signal.throwIfAborted()
@@ -164,7 +165,7 @@ function assertInstallationIdentity(
installation: RemoteRuntimeInstallationIdentity
): void {
if (
installation.runtimeId !== 'opencode' ||
installation.runtimeId !== input.selection.provider ||
installation.platform !== input.agent.platform ||
installation.architecture !== input.agent.architecture
) {
@@ -186,7 +187,7 @@ function assertRuntimeCapability(
capability.name === RUNTIME_MODEL_BRIDGE_CAPABILITY_NAME
)
const runtimes = capabilities.runtimes.filter(
(runtime) => runtime.runtimeId === 'opencode'
(runtime) => runtime.runtimeId === installation.runtimeId
)
if (
acp === undefined ||
@@ -594,6 +594,24 @@ describe('ProtocolRemoteRuntimeChannel', () => {
await fixture.channel.close()
})
it('allows cold native initialization beyond the ordinary control timeout without resending', async () => {
const fixture = await openChannel()
vi.useFakeTimers()
try {
fixture.client.responder = async (method, params) => {
if (method === 'runtime/startPrompt') await new Promise(resolve => setTimeout(resolve, 20_000))
return defaultResponse(method, params)
}
const started = fixture.channel.startOwnedPrompt({
bindingId: 'binding-1', operationId: 'operation-1', requestId: 'operation-1',
prompt: [{ type: 'text', text: 'hello' }]
})
await vi.advanceTimersByTimeAsync(20_000)
await expect(started).resolves.toMatchObject({ sessionId: 'session-owned-1' })
expect(fixture.client.requests.filter(request => request.method === 'runtime/startPrompt')).toHaveLength(1)
} finally { vi.useRealTimers(); await fixture.channel.close() }
})
it('attaches the exact operation when a prompt start response is lost', async () => {
const fixture = await openChannel()
const deadlineAt = new Date(Date.now() + 10_000).toISOString()
@@ -1116,7 +1116,10 @@ export class ProtocolRemoteRuntimeChannel
this.#state.client,
method,
params,
this.#state.controlTimeoutMs,
// Cold OpenCode ACP initialization on the shared Host exceeds 15 seconds.
method === 'runtime/startPrompt'
? Math.max(this.#state.controlTimeoutMs, 30_000)
: this.#state.controlTimeoutMs,
this.#lifetime.signal
)
this.#assertCurrent()
@@ -389,21 +389,23 @@ export class RemoteEnvironmentPreparer implements Preparer {
signal.throwIfAborted()
emit(method, 'installing-runtime')
for (const runtime of [identity.runtime, ...(identity.additionalRuntimes ?? [])]) {
assertAgentCommandSucceeded(
await lease.runAgentRuntimeAction(
installationId,
{
kind: 'runtime-activate',
runtimeId: identity.runtime.runtimeId,
bundleDigest: identity.runtime.bundleDigest,
architecture: identity.runtime.architecture,
runtimeId: runtime.runtimeId,
bundleDigest: runtime.bundleDigest,
architecture: runtime.architecture,
forceVerification: true
},
signal
),
'OpenCode Runtime 激活'
`${runtime.runtimeId} Runtime 激活`
)
signal.throwIfAborted()
}
}
async #prepareUploadedArchive(options: {
@@ -693,6 +695,14 @@ function assertPackageIdentity(
identity: SshRemotePackageIdentity,
candidate: VerifiedRemoteAgentInstallCandidate
): void {
const extras = identity.additionalRuntimes ?? []
const expected = candidate.additionalRuntimes ?? []
if (extras.length !== expected.length || extras.some((runtime, index) => {
const item = expected[index]!
return runtime.runtimeId !== item.runtimeId || runtime.runtimeVersion !== item.version ||
runtime.bundleDigest !== item.bundleDigest || runtime.platform !== candidate.platform ||
runtime.architecture !== candidate.architecture || !isDeepStrictEqual(runtime.protocol, item.protocol)
})) throw new Error('Additional Runtime identity does not match the signed package')
if (
identity.archiveSha256 !== candidate.sha256 ||
identity.agent.agentVersion !== candidate.version ||
@@ -21,7 +21,6 @@ import { remoteHostTargetIdentityKey } from './remote-host-target-identity'
const MAXIMUM_METADATA_BYTES = 1024 * 1024
const RUNTIME_REGISTRY_PATH =
'.goodbuddy/runtimes/registry.json'
const OPENCODE_ROOT = '.goodbuddy/runtimes/opencode'
export type RemoteRuntimeInstallationPhase =
| 'inspecting-host'
@@ -45,6 +44,7 @@ export interface RemoteRuntimeInstallationTargetResolver {
}
export type RemoteRuntimeActivationRequestOptions = {
runtimeId?: 'opencode' | 'continue'
signal?: AbortSignal
onProgress?: (phase: RemoteRuntimeInstallationPhase) => void
agentInstallationId?: VerifiedAgentInstallationId
@@ -117,7 +117,8 @@ export class RemoteRuntimeInstallationManager {
this.#coordinator.assertAvailable()
options.signal?.throwIfAborted()
emitOne(options.onProgress, 'inspecting-host')
const agentKey = options.agentInstallationId ?? ''
const runtimeId = options.runtimeId ?? 'opencode'
const agentKey = `${options.agentInstallationId ?? ''}\0${runtimeId}`
const earlyCached =
this.#coordinator.cachedForHost(hostId, agentKey)
if (earlyCached !== undefined) {
@@ -154,6 +155,7 @@ export class RemoteRuntimeInstallationManager {
this.#activateRegistered(
target,
options.agentInstallationId,
runtimeId,
signal,
progress
)
@@ -171,6 +173,7 @@ export class RemoteRuntimeInstallationManager {
async #activateRegistered(
target: SshConnectionPoolTarget,
agentInstallationId: VerifiedAgentInstallationId | undefined,
runtimeId: 'opencode' | 'continue',
signal: AbortSignal,
progress: (phase: RemoteRuntimeInstallationPhase) => void
): Promise<RemoteRuntimeInstallationIdentity> {
@@ -182,7 +185,7 @@ export class RemoteRuntimeInstallationManager {
const probe = await lease.runAgentBootstrapProbe(signal)
if (!probe.ready) {
throw new RemoteRuntimeInstallationError(
`Remote host cannot activate OpenCode Runtime: ${probe.reason}`,
`Remote host cannot activate ${runtimeId} Runtime: ${probe.reason}`,
'incompatible',
probe.reason
)
@@ -222,12 +225,12 @@ export class RemoteRuntimeInstallationManager {
)
const current = registry.current.find(
(entry) =>
entry.runtimeId === 'opencode' &&
entry.runtimeId === runtimeId &&
entry.architecture === probe.architecture
)
if (current === undefined) {
throw new RemoteRuntimeInstallationError(
'The Host has no current OpenCode Runtime for this architecture',
`The Host has no current ${runtimeId} Runtime for this architecture`,
'incompatible'
)
}
@@ -236,7 +239,7 @@ export class RemoteRuntimeInstallationManager {
current.runtimeAdapterDigest
if (runtimeAdapterDigest === undefined) {
const manifestPath =
`${OPENCODE_ROOT}/${digestDirectoryName(
`.goodbuddy/runtimes/${runtimeId}/${digestDirectoryName(
current.bundleDigest
)}/manifest.json`
const manifest =
@@ -288,7 +291,7 @@ export class RemoteRuntimeInstallationManager {
}
if (isMissingPathError(error)) {
throw new RemoteRuntimeInstallationError(
'The Host has no current OpenCode Runtime',
`The Host has no current ${runtimeId} Runtime`,
'incompatible',
error
)
@@ -39,6 +39,7 @@ type RuntimeBundleVerifier = (
) => Promise<VerifiedRuntimeBundle>
export type RemoteRuntimeResourceLoaderOptions = {
runtimeId?: 'opencode' | 'continue'
verificationEnvironment?: 'production' | 'test'
verifyRuntimeBundle?: RuntimeBundleVerifier
}
@@ -74,7 +75,7 @@ export async function loadVerifiedRemoteRuntimeResourceBundle(
const metadata =
await loadRemoteRuntimeVerificationMetadata(paths)
const bundleDirectory = await resolveSingleDigestDirectory(
getBundledRemoteRuntimeRoot(paths, architecture)
getBundledRemoteRuntimeRoot(paths, architecture, options.runtimeId)
)
const verifier = options.verifyRuntimeBundle ?? verifyRuntimeBundle
const verified = await verifier(bundleDirectory, {
@@ -89,7 +90,8 @@ export async function loadVerifiedRemoteRuntimeResourceBundle(
bundleDirectory,
architecture,
metadata.canonicalReleaseKeyRegistryBytes,
metadata.canonicalRemoteRuntimeLockBytes
metadata.canonicalRemoteRuntimeLockBytes,
options.runtimeId ?? 'opencode'
)
}
@@ -258,15 +260,16 @@ async function validateVerifiedBundle(
bundleDirectory: string,
architecture: AgentArchitecture,
keyRegistryBytes: Buffer,
runtimeLockBytes: Buffer
runtimeLockBytes: Buffer,
runtimeId: 'opencode' | 'continue'
): Promise<VerifiedRemoteRuntimeResourceBundle> {
const manifest = remoteRuntimeBundleManifestSchema.parse(
verified.manifest
)
if (
verified.bundleDirectory !== bundleDirectory ||
manifest.runtimeId !== 'opencode' ||
manifest.provider !== 'opencode' ||
manifest.runtimeId !== runtimeId ||
manifest.provider !== runtimeId ||
(manifest.platform !== 'linux' && !(manifest.platform === 'darwin' && manifest.architecture === 'arm64')) ||
manifest.architecture !== architecture
) {