// @effect-diagnostics nodeBuiltinImport:offimport * as NodePath from "node:path";import * as NodeOS from "node:os";import * as NodeFSP from "node:fs/promises";import * as NodeURL from "node:url"; import * as NodeServices from "@effect/platform-node/NodeServices";import { assert, it } from "@effect/vitest";import * as Deferred from "effect/Deferred";import * as Effect from "effect/Effect";import * as Fiber from "effect/Fiber";import * as Layer from "effect/Layer";import * as Ref from "effect/Ref";import * as Schema from "effect/Schema";import * as Stream from "effect/Stream";import * as TestClock from "effect/testing/TestClock"; import { ApprovalRequestId, GrokSettings, ProviderDriverKind, ProviderInstanceId, ThreadId, TurnId, type ProviderRuntimeEvent,} from "@t3tools/contracts"; import { ServerConfig } from "../../config.ts";import { grokPromptSettlementBelongsToContext, isGrokEnterPlanModeToolCall, makeGrokAdapter, nextGrokPlanModeActive, selectGrokPermissionOptionId,} from "./GrokAdapter.ts";import { execScriptSource, writeFakeCli } from "../../testUtils/fakeCli.ts";import { HostProcessPlatform } from "@t3tools/shared/hostProcess";const decodeGrokSettings = Schema.decodeSync(GrokSettings); const __dirname = NodePath.dirname(NodeURL.fileURLToPath(import.meta.url));const mockAgentPath = NodePath.join(__dirname, "../../../scripts/acp-mock-agent.ts");// Stopping a session kills the agent with SIGTERM; Windows terminates the// process instead, so the mock never sees a signal to log.const windowsHost = HostProcessPlatform.defaultValue() === "win32"; async function makeMockGrokWrapper(extraEnv?: Record<string, string>) { const dir = await NodeFSP.mkdtemp(NodePath.join(NodeOS.tmpdir(), "grok-acp-mock-")); return writeFakeCli({ directory: dir, name: "fake-grok", env: extraEnv ?? {}, source: execScriptSource({ scriptPath: mockAgentPath }), });} function waitForFileContent( filePath: string, attempts = 40, expectedContent?: string,): Effect.Effect<string> { const readAttempt = (remainingAttempts: number): Effect.Effect<string> => Effect.gen(function* () { if (remainingAttempts <= 0) { return yield* Effect.die(new Error(`Timed out waiting for file content at ${filePath}`)); } const raw = yield* Effect.tryPromise(() => NodeFSP.readFile(filePath, "utf8")).pipe( Effect.orElseSucceed(() => ""), ); if ( raw.trim().length > 0 && (expectedContent === undefined || raw.includes(expectedContent)) ) { return raw; } yield* Effect.sleep("25 millis"); return yield* readAttempt(remainingAttempts - 1); }); return readAttempt(attempts);} async function readJsonLines(filePath: string) { const raw = await NodeFSP.readFile(filePath, "utf8"); return raw .split("\n") .map((line) => line.trim()) .filter((line) => line.length > 0) .map((line) => JSON.parse(line) as Record<string, unknown>);} const grokAdapterTestLayer = ServerConfig.layerTest(process.cwd(), { prefix: "t3code-grok-adapter-test-",}).pipe(Layer.provideMerge(NodeServices.layer)); const makeTestAdapter = (binaryPath: string, options?: Parameters<typeof makeGrokAdapter>[1]) => makeGrokAdapter(decodeGrokSettings({ binaryPath }), options).pipe(Effect.orDie); it("detects enter_plan_mode tool calls from title and rawInput", () => { assert.isTrue( isGrokEnterPlanModeToolCall({ title: "enter_plan_mode", data: { toolCallId: "1" }, }), ); assert.isTrue( isGrokEnterPlanModeToolCall({ title: "Plan mode entered", data: { toolCallId: "1", rawInput: { variant: "EnterPlanMode" } }, }), ); assert.isFalse( isGrokEnterPlanModeToolCall({ title: "write", data: { toolCallId: "1", rawInput: { file_path: "/tmp/x", content: "y" } }, }), );}); it("only sets planModeActive after a successful enter_plan_mode", () => { const enter = { title: "enter_plan_mode", data: { toolCallId: "1" }, }; assert.isFalse(nextGrokPlanModeActive(false, { ...enter, status: "pending" })); assert.isTrue(nextGrokPlanModeActive(false, { ...enter, status: "inProgress" })); assert.isTrue(nextGrokPlanModeActive(false, { ...enter, status: "completed" })); assert.isFalse(nextGrokPlanModeActive(false, { ...enter, status: "failed" })); assert.isFalse(nextGrokPlanModeActive(true, { ...enter, status: "failed" })); assert.isTrue( nextGrokPlanModeActive(true, { title: "write", status: "completed", data: { toolCallId: "2" }, }), );}); function grokPermissionRequest( options: ReadonlyArray<{ readonly optionId: string; readonly kind: "allow_once" | "allow_always" | "reject_once" | "reject_always"; }>,) { return { sessionId: "mock-session-1", toolCall: { toolCallId: "tool-call-1", title: "cat package.json", kind: "execute" as const, status: "pending" as const, }, options: options.map((option) => ({ optionId: option.optionId, name: option.kind, kind: option.kind, })), };} it("maps Always allow to allow_once when Grok omits allow_always", () => { const request = grokPermissionRequest([ { optionId: "allow-once", kind: "allow_once" }, { optionId: "reject-once", kind: "reject_once" }, ]); assert.equal(selectGrokPermissionOptionId(request, "acceptForSession"), "allow-once"); assert.equal(selectGrokPermissionOptionId(request, "accept"), "allow-once"); assert.equal(selectGrokPermissionOptionId(request, "decline"), "reject-once");}); it("prefers allow_always when Grok offers it", () => { const request = grokPermissionRequest([ { optionId: "allow-once", kind: "allow_once" }, { optionId: "allow-always", kind: "allow_always" }, { optionId: "reject-once", kind: "reject_once" }, ]); assert.equal(selectGrokPermissionOptionId(request, "acceptForSession"), "allow-always"); assert.equal(selectGrokPermissionOptionId(request, "accept"), "allow-once");}); it("requires a settlement to match the live Grok turn", () => { const staleTurnId = TurnId.make("stale-turn"); const replacementTurnId = TurnId.make("replacement-turn"); assert.isFalse( grokPromptSettlementBelongsToContext({ liveAcpSessionId: "session-1", expectedAcpSessionId: "session-1", liveActiveTurnId: replacementTurnId, liveSessionActiveTurnId: replacementTurnId, turnId: staleTurnId, }), ); assert.isFalse( grokPromptSettlementBelongsToContext({ liveAcpSessionId: "replacement-session", expectedAcpSessionId: "stale-session", liveActiveTurnId: staleTurnId, liveSessionActiveTurnId: staleTurnId, turnId: staleTurnId, }), ); assert.isTrue( grokPromptSettlementBelongsToContext({ liveAcpSessionId: "session-1", expectedAcpSessionId: "session-1", liveActiveTurnId: staleTurnId, liveSessionActiveTurnId: staleTurnId, turnId: staleTurnId, }), );}); it.layer(grokAdapterTestLayer)("GrokAdapterLive", (it) => { it.effect("sends runtime context with the current model without changing saved prompts", () => Effect.gen(function* () { const threadId = ThreadId.make("grok-runtime-context"); const tempDir = yield* Effect.promise(() => NodeFSP.mkdtemp(NodePath.join(NodeOS.tmpdir(), "grok-runtime-context-")), ); const requestLogPath = NodePath.join(tempDir, "requests.ndjson"); const wrapperPath = yield* Effect.promise(() => makeMockGrokWrapper({ T3_ACP_REQUEST_LOG_PATH: requestLogPath }), ); const adapter = yield* makeTestAdapter(wrapperPath); yield* adapter.startSession({ threadId, cwd: process.cwd(), runtimeMode: "full-access", modelSelection: { instanceId: ProviderInstanceId.make("grok"), model: "grok-mock-alt" }, }); yield* adapter.sendTurn({ threadId, input: "First prompt" }); yield* adapter.sendTurn({ threadId, input: "Second prompt", modelSelection: { instanceId: ProviderInstanceId.make("grok"), model: "grok-4.6", options: [{ id: "reasoningEffort", value: "low" }], }, }); const snapshot = yield* adapter.readThread(threadId); assert.deepEqual( snapshot.turns.map((turn) => turn.items), [ [ { prompt: [{ type: "text", text: "First prompt" }], result: { stopReason: "end_turn" }, }, ], [ { prompt: [{ type: "text", text: "Second prompt" }], result: { stopReason: "end_turn" }, }, ], ], ); yield* adapter.stopSession(threadId); const requests = yield* Effect.promise(() => readJsonLines(requestLogPath)); const prompts = requests .filter((request) => request.method === "session/prompt") .map( (request) => (request.params as { prompt: Array<{ type: string; text: string }> }).prompt, ); assert.equal(prompts.length, 2); assert.deepEqual(prompts[0]?.[0], { type: "text", text: "First prompt" }); assert.include(prompts[0]?.[1]?.text, "Grok harness, as grok-mock-alt"); assert.deepEqual(prompts[1]?.[0], { type: "text", text: "Second prompt" }); assert.include(prompts[1]?.[1]?.text, "Grok harness, as grok-4.6"); assert.include(prompts[1]?.[1]?.text, "with low reasoning effort"); assert.include(prompts[1]?.[1]?.text, "embed images and videos"); }), ); it.effect("starts a session and maps mock ACP prompt flow to runtime events", () => Effect.gen(function* () { const threadId = ThreadId.make("grok-mock-thread"); const wrapperPath = yield* Effect.promise(() => makeMockGrokWrapper()); const adapter = yield* makeTestAdapter(wrapperPath); const runtimeEvents: ProviderRuntimeEvent[] = []; const turnCompleted = yield* Deferred.make<void>(); const runtimeEventsFiber = yield* Stream.runForEach(adapter.streamEvents, (event) => Effect.sync(() => { runtimeEvents.push(event); }).pipe( Effect.andThen( event.type === "turn.completed" ? Deferred.succeed(turnCompleted, undefined) : Effect.void, ), ), ).pipe(Effect.forkChild); const session = yield* adapter.startSession({ threadId, provider: ProviderDriverKind.make("grok"), cwd: process.cwd(), runtimeMode: "full-access", modelSelection: { instanceId: ProviderInstanceId.make("grok"), model: "grok-mock-alt" }, }); assert.equal(session.provider, "grok"); assert.equal(session.model, "grok-mock-alt"); assert.deepStrictEqual(session.resumeCursor, { schemaVersion: 1, sessionId: "mock-session-1", }); yield* adapter.sendTurn({ threadId, input: "hello grok", attachments: [], }); yield* Deferred.await(turnCompleted); yield* Fiber.interrupt(runtimeEventsFiber); const types = runtimeEvents.map((e) => e.type); assert.includeMembers(types, [ "session.started", "session.state.changed", "thread.started", "turn.started", "item.started", "content.delta", "turn.completed", ] as const); const delta = runtimeEvents.find((e) => e.type === "content.delta"); assert.isDefined(delta); if (delta?.type === "content.delta") { assert.equal(delta.payload.delta, "hello from mock"); } yield* adapter.stopSession(threadId); }), ); it.effect.skipIf(windowsHost)("closes the ACP child process when a session stops", () => Effect.gen(function* () { const threadId = ThreadId.make("grok-stop-session-close"); const tempDir = yield* Effect.promise(() => NodeFSP.mkdtemp(NodePath.join(NodeOS.tmpdir(), "grok-adapter-exit-log-")), ); const exitLogPath = NodePath.join(tempDir, "exit.log"); const wrapperPath = yield* Effect.promise(() => makeMockGrokWrapper({ T3_ACP_EXIT_LOG_PATH: exitLogPath, }), ); const adapter = yield* makeTestAdapter(wrapperPath); yield* adapter.startSession({ threadId, provider: ProviderDriverKind.make("grok"), cwd: process.cwd(), runtimeMode: "full-access", modelSelection: { instanceId: ProviderInstanceId.make("grok"), model: "grok-build" }, }); yield* adapter.stopSession(threadId); const exitLog = yield* waitForFileContent(exitLogPath); assert.include(exitLog, "SIGTERM"); }), ); it.effect("reports a Grok session running only while the prompt is in flight", () => Effect.gen(function* () { const threadId = ThreadId.make("grok-session-ready-after-prompt"); const wrapperPath = yield* Effect.promise(() => makeMockGrokWrapper({ T3_ACP_EMIT_TOOL_CALLS: "1", }), ); const adapter = yield* makeTestAdapter(wrapperPath); const requestOpened = yield* Deferred.make<Extract<ProviderRuntimeEvent, { type: "request.opened" }>>(); const eventsFiber = yield* Stream.runForEach(adapter.streamEvents, (event) => event.type === "request.opened" ? Deferred.succeed(requestOpened, event).pipe(Effect.ignore) : Effect.void, ).pipe(Effect.forkChild); yield* adapter.startSession({ threadId, provider: ProviderDriverKind.make("grok"), cwd: process.cwd(), runtimeMode: "approval-required", modelSelection: { instanceId: ProviderInstanceId.make("grok"), model: "grok-build" }, }); const sendTurnFiber = yield* adapter .sendTurn({ threadId, input: "check lifecycle", attachments: [] }) .pipe(Effect.forkChild); const requestOpenedEvent = yield* Deferred.await(requestOpened); const runningSessions = yield* adapter.listSessions(); const runningSession = runningSessions.find((session) => session.threadId === threadId); assert.equal(runningSession?.status, "running"); assert.isDefined(runningSession?.activeTurnId); yield* adapter.respondToRequest( threadId, ApprovalRequestId.make(String(requestOpenedEvent.requestId)), "accept", ); yield* Fiber.join(sendTurnFiber); const readySessions = yield* adapter.listSessions(); const readySession = readySessions.find((session) => session.threadId === threadId); assert.equal(readySession?.status, "ready"); assert.isUndefined(readySession?.activeTurnId); yield* Fiber.interrupt(eventsFiber); yield* adapter.stopSession(threadId); }), ); it.effect("restores ready without completing an unstarted turn when preparation fails", () => Effect.gen(function* () { const threadId = ThreadId.make("grok-preparation-failure-while-connecting"); const wrapperPath = yield* Effect.promise(() => makeMockGrokWrapper()); const adapter = yield* makeTestAdapter(wrapperPath); const runtimeEvents: ProviderRuntimeEvent[] = []; const runtimeEventsFiber = yield* Stream.runForEach(adapter.streamEvents, (event) => Effect.sync(() => { runtimeEvents.push(event); }), ).pipe(Effect.forkChild); yield* adapter.startSession({ threadId, provider: ProviderDriverKind.make("grok"), cwd: process.cwd(), runtimeMode: "full-access", modelSelection: { instanceId: ProviderInstanceId.make("grok"), model: "grok-build" }, }); const error = yield* Effect.flip( adapter.sendTurn({ threadId, input: "prepare invalid attachment", attachments: [ { type: "image", id: "missing-image", name: "missing.png", mimeType: "image/png", sizeBytes: 1, }, ], }), ); for (let yieldAttempt = 0; yieldAttempt < 4; yieldAttempt += 1) { yield* Effect.yieldNow; } const turnCompletedEvent = runtimeEvents.find( (event): event is Extract<ProviderRuntimeEvent, { type: "turn.completed" }> => event.type === "turn.completed", ); const readySessions = yield* adapter.listSessions(); const readySession = readySessions.find((session) => session.threadId === threadId); assert.equal(error._tag, "ProviderAdapterRequestError"); assert.isUndefined(turnCompletedEvent); assert.equal(readySession?.status, "ready"); assert.isUndefined(readySession?.activeTurnId); yield* Fiber.interrupt(runtimeEventsFiber); yield* adapter.stopSession(threadId); }), ); it.effect("completes a Grok turn from xAI prompt completion when the prompt RPC hangs", () => Effect.gen(function* () { const threadId = ThreadId.make("grok-xai-prompt-complete-fallback"); const wrapperPath = yield* Effect.promise(() => makeMockGrokWrapper({ T3_ACP_EMIT_XAI_PROMPT_COMPLETE_THEN_HANG: "1", T3_ACP_EMIT_FOREIGN_SESSION_UPDATES: "1", }), ); const adapter = yield* makeTestAdapter(wrapperPath); const runtimeEvents: ProviderRuntimeEvent[] = []; const turnCompleted = yield* Deferred.make<void>(); const runtimeEventsFiber = yield* Stream.runForEach(adapter.streamEvents, (event) => Effect.sync(() => { runtimeEvents.push(event); }).pipe( Effect.andThen( event.type === "turn.completed" ? Deferred.succeed(turnCompleted, undefined)Lines 1–500 of 2460. Load more
// @effect-diagnostics nodeBuiltinImport:off import * as NodePath from "node:path"; import * as NodeOS from "node:os"; import * as NodeFSP from "node:fs/promises"; import * as NodeURL from "node:url"; import * as NodeServices from "@effect/platform-node/NodeServices"; import { assert, it } from "@effect/vitest"; import * as Deferred from "effect/Deferred"; import * as Effect from "effect/Effect"; import * as Fiber from "effect/Fiber"; import * as Layer from "effect/Layer"; import * as Ref from "effect/Ref"; import * as Schema from "effect/Schema"; import * as Stream from "effect/Stream"; import * as TestClock from "effect/testing/TestClock"; import { ApprovalRequestId, GrokSettings, ProviderDriverKind, ProviderInstanceId, ThreadId, TurnId, type ProviderRuntimeEvent, } from "@t3tools/contracts"; import { ServerConfig } from "../../config.ts"; import { grokPromptSettlementBelongsToContext, isGrokEnterPlanModeToolCall, makeGrokAdapter, nextGrokPlanModeActive, selectGrokPermissionOptionId, } from "./GrokAdapter.ts"; import { execScriptSource, writeFakeCli } from "../../testUtils/fakeCli.ts"; import { HostProcessPlatform } from "@t3tools/shared/hostProcess"; const decodeGrokSettings = Schema.decodeSync(GrokSettings); const __dirname = NodePath.dirname(NodeURL.fileURLToPath(import.meta.url)); const mockAgentPath = NodePath.join(__dirname, "../../../scripts/acp-mock-agent.ts"); // Stopping a session kills the agent with SIGTERM; Windows terminates the // process instead, so the mock never sees a signal to log. const windowsHost = HostProcessPlatform.defaultValue() === "win32"; async function makeMockGrokWrapper(extraEnv?: Record<string, string>) { const dir = await NodeFSP.mkdtemp(NodePath.join(NodeOS.tmpdir(), "grok-acp-mock-")); return writeFakeCli({ directory: dir, name: "fake-grok", env: extraEnv ?? {}, source: execScriptSource({ scriptPath: mockAgentPath }), }); } function waitForFileContent( filePath: string, attempts = 40, expectedContent?: string, ): Effect.Effect<string> { const readAttempt = (remainingAttempts: number): Effect.Effect<string> => Effect.gen(function* () { if (remainingAttempts <= 0) { return yield* Effect.die(new Error(`Timed out waiting for file content at ${filePath}`)); } const raw = yield* Effect.tryPromise(() => NodeFSP.readFile(filePath, "utf8")).pipe( Effect.orElseSucceed(() => ""), ); if ( raw.trim().length > 0 && (expectedContent === undefined || raw.includes(expectedContent)) ) { return raw; } yield* Effect.sleep("25 millis"); return yield* readAttempt(remainingAttempts - 1); }); return readAttempt(attempts); } async function readJsonLines(filePath: string) { const raw = await NodeFSP.readFile(filePath, "utf8"); return raw .split("\n") .map((line) => line.trim()) .filter((line) => line.length > 0) .map((line) => JSON.parse(line) as Record<string, unknown>); } const grokAdapterTestLayer = ServerConfig.layerTest(process.cwd(), { prefix: "t3code-grok-adapter-test-", }).pipe(Layer.provideMerge(NodeServices.layer)); const makeTestAdapter = (binaryPath: string, options?: Parameters<typeof makeGrokAdapter>[1]) => makeGrokAdapter(decodeGrokSettings({ binaryPath }), options).pipe(Effect.orDie); it("detects enter_plan_mode tool calls from title and rawInput", () => { assert.isTrue( isGrokEnterPlanModeToolCall({ title: "enter_plan_mode", data: { toolCallId: "1" }, }), ); assert.isTrue( isGrokEnterPlanModeToolCall({ title: "Plan mode entered", data: { toolCallId: "1", rawInput: { variant: "EnterPlanMode" } }, }), ); assert.isFalse( isGrokEnterPlanModeToolCall({ title: "write", data: { toolCallId: "1", rawInput: { file_path: "/tmp/x", content: "y" } }, }), ); }); it("only sets planModeActive after a successful enter_plan_mode", () => { const enter = { title: "enter_plan_mode", data: { toolCallId: "1" }, }; assert.isFalse(nextGrokPlanModeActive(false, { ...enter, status: "pending" })); assert.isTrue(nextGrokPlanModeActive(false, { ...enter, status: "inProgress" })); assert.isTrue(nextGrokPlanModeActive(false, { ...enter, status: "completed" })); assert.isFalse(nextGrokPlanModeActive(false, { ...enter, status: "failed" })); assert.isFalse(nextGrokPlanModeActive(true, { ...enter, status: "failed" })); assert.isTrue( nextGrokPlanModeActive(true, { title: "write", status: "completed", data: { toolCallId: "2" }, }), ); }); function grokPermissionRequest( options: ReadonlyArray<{ readonly optionId: string; readonly kind: "allow_once" | "allow_always" | "reject_once" | "reject_always"; }>, ) { return { sessionId: "mock-session-1", toolCall: { toolCallId: "tool-call-1", title: "cat package.json", kind: "execute" as const, status: "pending" as const, }, options: options.map((option) => ({ optionId: option.optionId, name: option.kind, kind: option.kind, })), }; } it("maps Always allow to allow_once when Grok omits allow_always", () => { const request = grokPermissionRequest([ { optionId: "allow-once", kind: "allow_once" }, { optionId: "reject-once", kind: "reject_once" }, ]); assert.equal(selectGrokPermissionOptionId(request, "acceptForSession"), "allow-once"); assert.equal(selectGrokPermissionOptionId(request, "accept"), "allow-once"); assert.equal(selectGrokPermissionOptionId(request, "decline"), "reject-once"); }); it("prefers allow_always when Grok offers it", () => { const request = grokPermissionRequest([ { optionId: "allow-once", kind: "allow_once" }, { optionId: "allow-always", kind: "allow_always" }, { optionId: "reject-once", kind: "reject_once" }, ]); assert.equal(selectGrokPermissionOptionId(request, "acceptForSession"), "allow-always"); assert.equal(selectGrokPermissionOptionId(request, "accept"), "allow-once"); }); it("requires a settlement to match the live Grok turn", () => { const staleTurnId = TurnId.make("stale-turn"); const replacementTurnId = TurnId.make("replacement-turn"); assert.isFalse( grokPromptSettlementBelongsToContext({ liveAcpSessionId: "session-1", expectedAcpSessionId: "session-1", liveActiveTurnId: replacementTurnId, liveSessionActiveTurnId: replacementTurnId, turnId: staleTurnId, }), ); assert.isFalse( grokPromptSettlementBelongsToContext({ liveAcpSessionId: "replacement-session", expectedAcpSessionId: "stale-session", liveActiveTurnId: staleTurnId, liveSessionActiveTurnId: staleTurnId, turnId: staleTurnId, }), ); assert.isTrue( grokPromptSettlementBelongsToContext({ liveAcpSessionId: "session-1", expectedAcpSessionId: "session-1", liveActiveTurnId: staleTurnId, liveSessionActiveTurnId: staleTurnId, turnId: staleTurnId, }), ); }); it.layer(grokAdapterTestLayer)("GrokAdapterLive", (it) => { it.effect("sends runtime context with the current model without changing saved prompts", () => Effect.gen(function* () { const threadId = ThreadId.make("grok-runtime-context"); const tempDir = yield* Effect.promise(() => NodeFSP.mkdtemp(NodePath.join(NodeOS.tmpdir(), "grok-runtime-context-")), ); const requestLogPath = NodePath.join(tempDir, "requests.ndjson"); const wrapperPath = yield* Effect.promise(() => makeMockGrokWrapper({ T3_ACP_REQUEST_LOG_PATH: requestLogPath }), ); const adapter = yield* makeTestAdapter(wrapperPath); yield* adapter.startSession({ threadId, cwd: process.cwd(), runtimeMode: "full-access", modelSelection: { instanceId: ProviderInstanceId.make("grok"), model: "grok-mock-alt" }, }); yield* adapter.sendTurn({ threadId, input: "First prompt" }); yield* adapter.sendTurn({ threadId, input: "Second prompt", modelSelection: { instanceId: ProviderInstanceId.make("grok"), model: "grok-4.6", options: [{ id: "reasoningEffort", value: "low" }], }, }); const snapshot = yield* adapter.readThread(threadId); assert.deepEqual( snapshot.turns.map((turn) => turn.items), [ [ { prompt: [{ type: "text", text: "First prompt" }], result: { stopReason: "end_turn" }, }, ], [ { prompt: [{ type: "text", text: "Second prompt" }], result: { stopReason: "end_turn" }, }, ], ], ); yield* adapter.stopSession(threadId); const requests = yield* Effect.promise(() => readJsonLines(requestLogPath)); const prompts = requests .filter((request) => request.method === "session/prompt") .map( (request) => (request.params as { prompt: Array<{ type: string; text: string }> }).prompt, ); assert.equal(prompts.length, 2); assert.deepEqual(prompts[0]?.[0], { type: "text", text: "First prompt" }); assert.include(prompts[0]?.[1]?.text, "Grok harness, as grok-mock-alt"); assert.deepEqual(prompts[1]?.[0], { type: "text", text: "Second prompt" }); assert.include(prompts[1]?.[1]?.text, "Grok harness, as grok-4.6"); assert.include(prompts[1]?.[1]?.text, "with low reasoning effort"); assert.include(prompts[1]?.[1]?.text, "embed images and videos"); }), ); it.effect("starts a session and maps mock ACP prompt flow to runtime events", () => Effect.gen(function* () { const threadId = ThreadId.make("grok-mock-thread"); const wrapperPath = yield* Effect.promise(() => makeMockGrokWrapper()); const adapter = yield* makeTestAdapter(wrapperPath); const runtimeEvents: ProviderRuntimeEvent[] = []; const turnCompleted = yield* Deferred.make<void>(); const runtimeEventsFiber = yield* Stream.runForEach(adapter.streamEvents, (event) => Effect.sync(() => { runtimeEvents.push(event); }).pipe( Effect.andThen( event.type === "turn.completed" ? Deferred.succeed(turnCompleted, undefined) : Effect.void, ), ), ).pipe(Effect.forkChild); const session = yield* adapter.startSession({ threadId, provider: ProviderDriverKind.make("grok"), cwd: process.cwd(), runtimeMode: "full-access", modelSelection: { instanceId: ProviderInstanceId.make("grok"), model: "grok-mock-alt" }, }); assert.equal(session.provider, "grok"); assert.equal(session.model, "grok-mock-alt"); assert.deepStrictEqual(session.resumeCursor, { schemaVersion: 1, sessionId: "mock-session-1", }); yield* adapter.sendTurn({ threadId, input: "hello grok", attachments: [], }); yield* Deferred.await(turnCompleted); yield* Fiber.interrupt(runtimeEventsFiber); const types = runtimeEvents.map((e) => e.type); assert.includeMembers(types, [ "session.started", "session.state.changed", "thread.started", "turn.started", "item.started", "content.delta", "turn.completed", ] as const); const delta = runtimeEvents.find((e) => e.type === "content.delta"); assert.isDefined(delta); if (delta?.type === "content.delta") { assert.equal(delta.payload.delta, "hello from mock"); } yield* adapter.stopSession(threadId); }), ); it.effect.skipIf(windowsHost)("closes the ACP child process when a session stops", () => Effect.gen(function* () { const threadId = ThreadId.make("grok-stop-session-close"); const tempDir = yield* Effect.promise(() => NodeFSP.mkdtemp(NodePath.join(NodeOS.tmpdir(), "grok-adapter-exit-log-")), ); const exitLogPath = NodePath.join(tempDir, "exit.log"); const wrapperPath = yield* Effect.promise(() => makeMockGrokWrapper({ T3_ACP_EXIT_LOG_PATH: exitLogPath, }), ); const adapter = yield* makeTestAdapter(wrapperPath); yield* adapter.startSession({ threadId, provider: ProviderDriverKind.make("grok"), cwd: process.cwd(), runtimeMode: "full-access", modelSelection: { instanceId: ProviderInstanceId.make("grok"), model: "grok-build" }, }); yield* adapter.stopSession(threadId); const exitLog = yield* waitForFileContent(exitLogPath); assert.include(exitLog, "SIGTERM"); }), ); it.effect("reports a Grok session running only while the prompt is in flight", () => Effect.gen(function* () { const threadId = ThreadId.make("grok-session-ready-after-prompt"); const wrapperPath = yield* Effect.promise(() => makeMockGrokWrapper({ T3_ACP_EMIT_TOOL_CALLS: "1", }), ); const adapter = yield* makeTestAdapter(wrapperPath); const requestOpened = yield* Deferred.make<Extract<ProviderRuntimeEvent, { type: "request.opened" }>>(); const eventsFiber = yield* Stream.runForEach(adapter.streamEvents, (event) => event.type === "request.opened" ? Deferred.succeed(requestOpened, event).pipe(Effect.ignore) : Effect.void, ).pipe(Effect.forkChild); yield* adapter.startSession({ threadId, provider: ProviderDriverKind.make("grok"), cwd: process.cwd(), runtimeMode: "approval-required", modelSelection: { instanceId: ProviderInstanceId.make("grok"), model: "grok-build" }, }); const sendTurnFiber = yield* adapter .sendTurn({ threadId, input: "check lifecycle", attachments: [] }) .pipe(Effect.forkChild); const requestOpenedEvent = yield* Deferred.await(requestOpened); const runningSessions = yield* adapter.listSessions(); const runningSession = runningSessions.find((session) => session.threadId === threadId); assert.equal(runningSession?.status, "running"); assert.isDefined(runningSession?.activeTurnId); yield* adapter.respondToRequest( threadId, ApprovalRequestId.make(String(requestOpenedEvent.requestId)), "accept", ); yield* Fiber.join(sendTurnFiber); const readySessions = yield* adapter.listSessions(); const readySession = readySessions.find((session) => session.threadId === threadId); assert.equal(readySession?.status, "ready"); assert.isUndefined(readySession?.activeTurnId); yield* Fiber.interrupt(eventsFiber); yield* adapter.stopSession(threadId); }), ); it.effect("restores ready without completing an unstarted turn when preparation fails", () => Effect.gen(function* () { const threadId = ThreadId.make("grok-preparation-failure-while-connecting"); const wrapperPath = yield* Effect.promise(() => makeMockGrokWrapper()); const adapter = yield* makeTestAdapter(wrapperPath); const runtimeEvents: ProviderRuntimeEvent[] = []; const runtimeEventsFiber = yield* Stream.runForEach(adapter.streamEvents, (event) => Effect.sync(() => { runtimeEvents.push(event); }), ).pipe(Effect.forkChild); yield* adapter.startSession({ threadId, provider: ProviderDriverKind.make("grok"), cwd: process.cwd(), runtimeMode: "full-access", modelSelection: { instanceId: ProviderInstanceId.make("grok"), model: "grok-build" }, }); const error = yield* Effect.flip( adapter.sendTurn({ threadId, input: "prepare invalid attachment", attachments: [ { type: "image", id: "missing-image", name: "missing.png", mimeType: "image/png", sizeBytes: 1, }, ], }), ); for (let yieldAttempt = 0; yieldAttempt < 4; yieldAttempt += 1) { yield* Effect.yieldNow; } const turnCompletedEvent = runtimeEvents.find( (event): event is Extract<ProviderRuntimeEvent, { type: "turn.completed" }> => event.type === "turn.completed", ); const readySessions = yield* adapter.listSessions(); const readySession = readySessions.find((session) => session.threadId === threadId); assert.equal(error._tag, "ProviderAdapterRequestError"); assert.isUndefined(turnCompletedEvent); assert.equal(readySession?.status, "ready"); assert.isUndefined(readySession?.activeTurnId); yield* Fiber.interrupt(runtimeEventsFiber); yield* adapter.stopSession(threadId); }), ); it.effect("completes a Grok turn from xAI prompt completion when the prompt RPC hangs", () => Effect.gen(function* () { const threadId = ThreadId.make("grok-xai-prompt-complete-fallback"); const wrapperPath = yield* Effect.promise(() => makeMockGrokWrapper({ T3_ACP_EMIT_XAI_PROMPT_COMPLETE_THEN_HANG: "1", T3_ACP_EMIT_FOREIGN_SESSION_UPDATES: "1", }), ); const adapter = yield* makeTestAdapter(wrapperPath); const runtimeEvents: ProviderRuntimeEvent[] = []; const turnCompleted = yield* Deferred.make<void>(); const runtimeEventsFiber = yield* Stream.runForEach(adapter.streamEvents, (event) => Effect.sync(() => { runtimeEvents.push(event); }).pipe( Effect.andThen( event.type === "turn.completed" ? Deferred.succeed(turnCompleted, undefined)