feat(core): add embedded v2 session runtime and tool foundation (#30632)

This commit is contained in:
Kit Langton
2026-06-03 23:02:17 -04:00
committed by GitHub
parent c35267776a
commit 76ee87ead8
215 changed files with 31344 additions and 3278 deletions

View File

@@ -1,7 +1,9 @@
import { describe, expect } from "bun:test"
import fs from "fs/promises"
import { realpathSync } from "node:fs"
import { tmpdir } from "node:os"
import { Effect, Exit, Stream } from "effect"
import path from "node:path"
import { Effect, Exit, Fiber, Stream } from "effect"
import { ChildProcess } from "effect/unstable/process"
import { AppProcess } from "@opencode-ai/core/process"
import { testEffect } from "../lib/effect"
@@ -11,6 +13,18 @@ const it = testEffect(AppProcess.defaultLayer)
const NODE = process.execPath
const cmd = (...args: string[]) => ChildProcess.make(NODE, args)
const waitForFile = (file: string) =>
Effect.promise(async () => {
while (true) {
try {
return await fs.readFile(file, "utf8")
} catch (error) {
if ((error as NodeJS.ErrnoException).code !== "ENOENT") throw error
await new Promise<void>((resolve) => setTimeout(resolve, 10))
}
}
})
describe("AppProcess", () => {
describe("run", () => {
it.effect(
@@ -118,6 +132,50 @@ describe("AppProcess", () => {
expect(result.command).toBe(`${NODE} -e process.stdout.write('hi')`)
}),
)
if (process.platform !== "win32") {
it.live(
"timeout cleans up the scoped child process",
Effect.acquireUseRelease(
Effect.promise(() => fs.mkdtemp(path.join(tmpdir(), "opencode-process-timeout-"))),
(directory) => {
const ready = path.join(directory, "ready")
const settled = path.join(directory, "settled")
const script = `const fs=require('fs');fs.writeFileSync(${JSON.stringify(ready)},String(process.pid));process.on('SIGTERM',()=>{fs.writeFileSync(${JSON.stringify(settled)},'settled');process.exit(0)});setInterval(()=>{},60000)`
return Effect.gen(function* () {
const svc = yield* AppProcess.Service
const exit = yield* Effect.exit(svc.run(cmd("-e", script), { timeout: "1 second" }))
expect(Exit.isFailure(exit)).toBe(true)
expect(yield* waitForFile(ready)).toMatch(/^\d+$/)
expect(yield* waitForFile(settled)).toBe("settled")
})
},
(directory) => Effect.promise(() => fs.rm(directory, { recursive: true, force: true })),
),
5_000,
)
it.live(
"fiber interruption cleans up the scoped child process after readiness",
Effect.acquireUseRelease(
Effect.promise(() => fs.mkdtemp(path.join(tmpdir(), "opencode-process-interrupt-"))),
(directory) => {
const ready = path.join(directory, "ready")
const settled = path.join(directory, "settled")
const script = `const fs=require('fs');fs.writeFileSync(${JSON.stringify(ready)},String(process.pid));process.on('SIGTERM',()=>{fs.writeFileSync(${JSON.stringify(settled)},'settled');process.exit(0)});setInterval(()=>{},60000)`
return Effect.gen(function* () {
const svc = yield* AppProcess.Service
const fiber = yield* svc.run(cmd("-e", script)).pipe(Effect.forkChild)
expect(yield* waitForFile(ready)).toMatch(/^\d+$/)
yield* Fiber.interrupt(fiber)
expect(yield* waitForFile(settled)).toBe("settled")
})
},
(directory) => Effect.promise(() => fs.rm(directory, { recursive: true, force: true })),
),
5_000,
)
}
})
describe("inherited platform methods", () => {