Compare commits

...

2 Commits

Author SHA1 Message Date
Kit Langton 2d116655c4 test(effect): stabilize runner active shell check 2026-04-30 21:30:30 -04:00
Kit Langton 8eae3a287e refactor(session): yield instance context in llm 2026-04-30 21:03:14 -04:00
2 changed files with 21 additions and 6 deletions
+7 -3
View File
@@ -7,7 +7,7 @@ import { mergeDeep, pipe } from "remeda"
import { GitLabWorkflowLanguageModel } from "gitlab-ai-provider" import { GitLabWorkflowLanguageModel } from "gitlab-ai-provider"
import { ProviderTransform } from "@/provider/transform" import { ProviderTransform } from "@/provider/transform"
import { Config } from "@/config/config" import { Config } from "@/config/config"
import { Instance } from "@/project/instance" import { InstanceState } from "@/effect/instance-state"
import type { Agent } from "@/agent/agent" import type { Agent } from "@/agent/agent"
import type { MessageV2 } from "./message-v2" import type { MessageV2 } from "./message-v2"
import { Plugin } from "@/plugin" import { Plugin } from "@/plugin"
@@ -268,7 +268,7 @@ const live: Layer.Layer<
const bridge = yield* EffectBridge.make() const bridge = yield* EffectBridge.make()
const approvedToolsForSession = new Set<string>() const approvedToolsForSession = new Set<string>()
workflowModel.approvalHandler = Instance.bind(async (approvalTools) => { workflowModel.approvalHandler = InstanceState.bind(async (approvalTools) => {
const uniqueNames = [...new Set(approvalTools.map((t: { name: string }) => t.name))] as string[] const uniqueNames = [...new Set(approvalTools.map((t: { name: string }) => t.name))] as string[]
// Auto-approve tools that were already approved in this session // Auto-approve tools that were already approved in this session
// (prevents infinite approval loops for server-side MCP tools) // (prevents infinite approval loops for server-side MCP tools)
@@ -330,6 +330,10 @@ const live: Layer.Layer<
}) })
: undefined : undefined
const opencodeProjectID = input.model.providerID.startsWith("opencode")
? (yield* InstanceState.context).project.id
: undefined
return streamText({ return streamText({
onError(error) { onError(error) {
l.error("stream error", { l.error("stream error", {
@@ -369,7 +373,7 @@ const live: Layer.Layer<
headers: { headers: {
...(input.model.providerID.startsWith("opencode") ...(input.model.providerID.startsWith("opencode")
? { ? {
"x-opencode-project": Instance.project.id, "x-opencode-project": opencodeProjectID,
"x-opencode-session": input.sessionID, "x-opencode-session": input.sessionID,
"x-opencode-request": input.user.id, "x-opencode-request": input.user.id,
"x-opencode-client": Flag.OPENCODE_CLIENT, "x-opencode-client": Flag.OPENCODE_CLIENT,
+14 -3
View File
@@ -263,14 +263,25 @@ describe("Runner", () => {
Effect.gen(function* () { Effect.gen(function* () {
const s = yield* Scope.Scope const s = yield* Scope.Scope
const runner = Runner.make<string>(s) const runner = Runner.make<string>(s)
const fiber = yield* runner.ensureRunning(Effect.never.pipe(Effect.as("x"))).pipe(Effect.forkChild) const started = yield* Deferred.make<void>()
yield* Effect.sleep("10 millis") const fiber = yield* runner
.ensureRunning(
Effect.gen(function* () {
yield* Deferred.succeed(started, undefined)
return yield* Effect.never.pipe(Effect.as("x"))
}),
)
.pipe(Effect.forkChild)
yield* Deferred.await(started).pipe(Effect.timeout("250 millis"))
yield* Effect.gen(function* () {
while (runner.state._tag !== "Running") yield* Effect.yieldNow
}).pipe(Effect.timeout("250 millis"))
const exit = yield* runner.startShell(Effect.succeed("nope")).pipe(Effect.exit) const exit = yield* runner.startShell(Effect.succeed("nope")).pipe(Effect.exit)
expect(Exit.isFailure(exit)).toBe(true) expect(Exit.isFailure(exit)).toBe(true)
yield* runner.cancel yield* runner.cancel
yield* Fiber.await(fiber) yield* Fiber.await(fiber).pipe(Effect.timeout("250 millis"))
}), }),
) )