Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 0 additions & 3 deletions apps/server/scripts/record-grok-acp-replay-fixture.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ import * as FileSystem from "effect/FileSystem";
import * as Layer from "effect/Layer";
import * as Path from "effect/Path";
import * as Schema from "effect/Schema";
import { ChildProcessSpawner } from "effect/process";
import type * as EffectAcpProtocol from "effect-acp/protocol";

import * as TestProviderHost from "@t3tools/provider-testing/TestProviderHost";
Expand Down Expand Up @@ -443,7 +442,6 @@ const recordScenario = Effect.fn("recordGrokScenario")(function* (fixtureName: s
const settings = { ...DEFAULT_GROK_SETTINGS, binaryPath: process.env.T3_GROK_BIN ?? "grok" };
const layerRegistry = ProviderAdapterRegistry.layerFromAdaptersEffect(
Effect.gen(function* () {
const childProcessSpawner = yield* ChildProcessSpawner.ChildProcessSpawner;
const environment = yield* HostProcessEnvironment;
const adapter = yield* makeGrokAdapterV2({
instanceId: GROK_DEFAULT_INSTANCE_ID,
Expand All @@ -460,7 +458,6 @@ const recordScenario = Effect.fn("recordGrokScenario")(function* (fixtureName: s
interruptPromptOnCancel: input.interruptPromptOnCancel ?? false,
grokSettings: settings,
environment,
childProcessSpawner,
runtimeMode: grokLaunchRuntimeMode(runtimePolicy),
}),
});
Expand Down
38 changes: 23 additions & 15 deletions apps/server/src/orchestration-v2/Adapters/AcpAdapterV2.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ import * as Sink from "effect/Sink";
import * as Stream from "effect/Stream";
import * as Scope from "effect/Scope";
import * as TestClock from "effect/testing/TestClock";
import { ChildProcessSpawner } from "effect/process";
import * as ChildProcessSpawner from "effect/process/ChildProcessSpawner";
import * as EffectAcpErrors from "effect-acp/errors";
import type * as EffectAcpProtocol from "effect-acp/protocol";
import type * as EffectAcpSchema from "effect-acp/compat";
Expand Down Expand Up @@ -1150,7 +1150,7 @@ describe("AcpAdapterV2", () => {
const adapter = yield* makeAcpAdapterV2({
instanceId,
// Production Devin runs commands through client terminals.
clientTerminals: { childProcessSpawner, shellCommands: true },
clientTerminals: { shellCommands: true },
selfInvocation: yield* resolveSelfInvocation(),
flavor: {
driver: ACP_TEST_DRIVER,
Expand Down Expand Up @@ -2190,7 +2190,7 @@ describe("AcpAdapterV2", () => {
makeRuntime,
},
selfInvocation,
clientTerminals: { childProcessSpawner },
clientTerminals: {},
});
const sourceThreadId = ThreadId.make("thread-acp-native-fork-source");
const targetThreadId = ThreadId.make("thread-acp-native-fork-target");
Expand Down Expand Up @@ -2476,7 +2476,7 @@ describe("AcpAdapterV2", () => {
}).pipe(Effect.andThen(makeRuntime(input))),
},
selfInvocation,
clientTerminals: { childProcessSpawner },
clientTerminals: {},
});
const threadId = ThreadId.make("thread-acp-unknown-permission-grant");
const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
Expand Down Expand Up @@ -12045,9 +12045,11 @@ describe("AcpAdapterV2", () => {
Effect.gen(function* () {
const commands: Array<{ readonly command: string; readonly args: ReadonlyArray<string> }> =
[];
yield* AcpSessionRuntime.terminateWindowsProcessTreeWithTaskkill(
makeTaskkillSpawner({ exitCode: 0, commands }),
4321,
yield* AcpSessionRuntime.terminateWindowsProcessTreeWithTaskkill(4321).pipe(
Effect.provideService(
ChildProcessSpawner.ChildProcessSpawner,
makeTaskkillSpawner({ exitCode: 0, commands }),
),
);
assert.deepEqual(commands, [{ command: "taskkill", args: ["/PID", "4321", "/T", "/F"] }]);

Expand All @@ -12058,10 +12060,13 @@ describe("AcpAdapterV2", () => {
{ exitCode: 128, output: "ERROR: Access is denied." },
{ exitCode: 1, output: "generic failure" },
]) {
const failed = yield* AcpSessionRuntime.terminateWindowsProcessTreeWithTaskkill(
makeTaskkillSpawner(fixture),
4321,
).pipe(Effect.exit);
const failed = yield* AcpSessionRuntime.terminateWindowsProcessTreeWithTaskkill(4321).pipe(
Effect.provideService(
ChildProcessSpawner.ChildProcessSpawner,
makeTaskkillSpawner(fixture),
),
Effect.exit,
);
if (Exit.isSuccess(failed)) assert.fail(`taskkill ${fixture.exitCode} must fail`);
const error = Cause.squash(failed.cause);
assert.instanceOf(error, AcpSessionRuntime.AcpProcessGroupTerminationError);
Expand All @@ -12082,10 +12087,13 @@ describe("AcpAdapterV2", () => {
{ outputFailure: true },
{ exitFailure: true },
]) {
const failed = yield* AcpSessionRuntime.terminateWindowsProcessTreeWithTaskkill(
makeTaskkillSpawner(fixture),
4321,
).pipe(Effect.exit);
const failed = yield* AcpSessionRuntime.terminateWindowsProcessTreeWithTaskkill(4321).pipe(
Effect.provideService(
ChildProcessSpawner.ChildProcessSpawner,
makeTaskkillSpawner(fixture),
),
Effect.exit,
);
if (Exit.isSuccess(failed)) assert.fail("taskkill infrastructure failure must fail");
const error = Cause.squash(failed.cause);
assert.instanceOf(error, AcpSessionRuntime.AcpProcessGroupTerminationError);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import * as Layer from "effect/Layer";
import * as Schema from "effect/Schema";
import type * as Scope from "effect/Scope";
import * as Stream from "effect/Stream";
import { ChildProcessSpawner } from "effect/process";
import * as ChildProcessSpawner from "effect/process/ChildProcessSpawner";
import * as EffectAcpErrors from "effect-acp/errors";

import * as AcpSessionRuntime from "@t3tools/provider-acp/server/AcpSessionRuntime";
Expand Down
122 changes: 64 additions & 58 deletions apps/server/src/orchestration-v2/Adapters/AcpRegistryAdapterV2.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,9 @@ import * as Layer from "effect/Layer";
import * as Option from "effect/Option";
import * as Path from "effect/Path";
import * as Schema from "effect/Schema";
import { HttpClient, HttpClientResponse } from "effect/http";
import { ChildProcessSpawner } from "effect/process";
import * as HttpClient from "effect/http/HttpClient";
import * as HttpClientResponse from "effect/http/HttpClientResponse";
import * as ChildProcessSpawner from "effect/process/ChildProcessSpawner";
import * as EffectAcpErrors from "effect-acp/errors";

import * as ProviderHost from "@t3tools/provider-core/server/ProviderHost";
Expand All @@ -22,6 +23,7 @@ import type {
AcpRegistryLiveConfiguration,
} from "@t3tools/provider-acp-registry/testing";
import * as AcpRegistrySupport from "@t3tools/provider-acp-registry/server/AcpRegistrySupport";
import * as AcpRegistryRuntimeCoordinator from "@t3tools/provider-acp-registry/server/AcpRegistryRuntimeCoordinator";
import { ACP_SESSION_MODE_OPTION_ID } from "@t3tools/provider-acp/server/sessionConfig";
import * as AcpSessionRuntime from "@t3tools/provider-acp/server/AcpSessionRuntime";
import * as IdAllocator from "@t3tools/provider-core/server/IdAllocator";
Expand Down Expand Up @@ -82,6 +84,11 @@ const layerRegistry = Layer.succeed(
),
);

// Adapters given an explicit runtime never resolve through the catalog.
const layerInjectedRuntimeCatalog = Layer.mock(AcpRegistrySupport.AcpRegistryCatalog)({
resolve: () => Effect.die("the runtime is injected"),
});

const layerTest = Layer.mergeAll(
NodeServices.layer,
IdAllocator.layer,
Expand Down Expand Up @@ -197,8 +204,6 @@ describe("AcpRegistryAdapterV2", () => {
authMethodId: "test",
}),
environment: {},
childProcessSpawner,
resolver: { resolve: () => Effect.die("the runtime is injected") },
makeRuntime: makeAcpReplayRuntime({
transcript,
statusPath,
Expand All @@ -208,7 +213,7 @@ describe("AcpRegistryAdapterV2", () => {
childProcessSpawner,
fileSystem,
}),
});
}).pipe(Effect.provide(layerInjectedRuntimeCatalog));
yield* adapter
.openSession({
threadId: ThreadId.make("thread-acp-registry-mode-pick"),
Expand Down Expand Up @@ -279,8 +284,6 @@ describe("AcpRegistryAdapterV2", () => {
instanceId,
settings: yield* decodeAcpRegistryAdapterSettings({ agentId, authMethodId: "test" }),
environment: {},
childProcessSpawner,
resolver: { resolve: () => Effect.die("the runtime is injected") },
makeRuntime: (input) =>
Effect.gen(function* () {
clientCapabilities = input.clientCapabilities;
Expand All @@ -305,7 +308,7 @@ describe("AcpRegistryAdapterV2", () => {
Effect.provide(context),
);
}),
});
}).pipe(Effect.provide(layerInjectedRuntimeCatalog));
const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
runtimeMode: "full-access",
interactionMode: "default",
Expand Down Expand Up @@ -333,7 +336,6 @@ describe("AcpRegistryAdapterV2", () => {

it.effect("opens a real ACP child process resolved from registry configuration", () =>
Effect.gen(function* () {
const childProcessSpawner = yield* ChildProcessSpawner.ChildProcessSpawner;
const path = yield* Path.Path;
const host = yield* ProviderHost.ProviderHost;
const mockAgentPath = yield* path.fromFileUrl(
Expand Down Expand Up @@ -365,55 +367,59 @@ describe("AcpRegistryAdapterV2", () => {
T3_ACP_SESSION_LIFECYCLE: "1",
T3_ACP_COMMAND_ADVERTISEMENT_DELAY_MS: "750",
},
childProcessSpawner,
runtimeCoordinator: {
withForegroundStartup: (agentId, effect) =>
Effect.acquireUseRelease(
Effect.sync(() => {
assert.equal(agentId, "fixture-agent");
startupActive = true;
startupCount += 1;
}),
() => effect,
() =>
Effect.sync(() => {
startupActive = false;
}),
),
runBackgroundProbe: (_agentId, effect) => effect.pipe(Effect.map(Option.some)),
withSessionMutation: (effect) => effect,
clearAvailableCommands: () => Effect.void,
publishAvailableCommands: (publishedInstanceId, commands) =>
Deferred.succeed(commandsPublished, {
instanceId: publishedInstanceId,
commands,
}).pipe(Effect.asVoid),
getAvailableCommands: () => Effect.succeed(Option.none()),
watchAvailableCommands: () => Effect.never,
clearLiveConfiguration: () => Effect.void,
publishLiveConfiguration: (_publishedInstanceId, configuration) =>
Deferred.succeed(configurationPublished, configuration).pipe(Effect.asVoid),
getLiveConfiguration: () => Effect.succeed(Option.none()),
watchLiveConfiguration: () => Effect.never,
requestUrlAuthentication: () => Effect.succeed(false),
acceptUrlAuthentication: () => Effect.succeed(false),
getUrlAuthAction: () => Effect.succeed(Option.none()),
watchUrlAuthAction: () => Effect.never,
},
resolver: {
resolve: (configuredSettings, cwd, environment) =>
Effect.sync(() => assert.isTrue(startupActive)).pipe(
Effect.andThen(resolver.resolve(configuredSettings, cwd, environment)),
Effect.map((resolved) => ({
...resolved,
spawn: {
...resolved.spawn,
args: [mockAgentPath],
},
})),
),
},
});
}).pipe(
Effect.provide(
Layer.mergeAll(
Layer.mock(AcpRegistryRuntimeCoordinator.AcpRegistryRuntimeCoordinator)({
withForegroundStartup: (agentId, effect) =>
Effect.acquireUseRelease(
Effect.sync(() => {
assert.equal(agentId, "fixture-agent");
startupActive = true;
startupCount += 1;
}),
() => effect,
() =>
Effect.sync(() => {
startupActive = false;
}),
),
runBackgroundProbe: (_agentId, effect) => effect.pipe(Effect.map(Option.some)),
withSessionMutation: (effect) => effect,
clearAvailableCommands: () => Effect.void,
publishAvailableCommands: (publishedInstanceId, commands) =>
Deferred.succeed(commandsPublished, {
instanceId: publishedInstanceId,
commands,
}).pipe(Effect.asVoid),
getAvailableCommands: () => Effect.succeed(Option.none()),
watchAvailableCommands: () => Effect.never,
clearLiveConfiguration: () => Effect.void,
publishLiveConfiguration: (_publishedInstanceId, configuration) =>
Deferred.succeed(configurationPublished, configuration).pipe(Effect.asVoid),
getLiveConfiguration: () => Effect.succeed(Option.none()),
watchLiveConfiguration: () => Effect.never,
requestUrlAuthentication: () => Effect.succeed(false),
acceptUrlAuthentication: () => Effect.succeed(false),
getUrlAuthAction: () => Effect.succeed(Option.none()),
watchUrlAuthAction: () => Effect.never,
}),
Layer.mock(AcpRegistrySupport.AcpRegistryCatalog)({
resolve: (configuredSettings, cwd, environment) =>
Effect.sync(() => assert.isTrue(startupActive)).pipe(
Effect.andThen(resolver.resolve(configuredSettings, cwd, environment)),
Effect.map((resolved) => ({
...resolved,
spawn: {
...resolved.spawn,
args: [mockAgentPath],
},
})),
),
}),
),
),
);
const threadId = ThreadId.make("thread-acp-registry-fixture");
const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
runtimeMode: "full-access",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import * as FileSystem from "effect/FileSystem";
import * as Layer from "effect/Layer";
import * as Path from "effect/Path";
import * as Schema from "effect/Schema";
import { ChildProcessSpawner } from "effect/process";
import * as ChildProcessSpawner from "effect/process/ChildProcessSpawner";

import * as TestProviderHost from "@t3tools/provider-testing/TestProviderHost";
import * as IdAllocator from "@t3tools/provider-core/server/IdAllocator";
Expand All @@ -25,6 +25,7 @@ import {
ACP_REGISTRY_PROVIDER,
makeAcpRegistryAdapterV2,
} from "@t3tools/provider-acp-registry/testing";
import * as AcpRegistrySupport from "@t3tools/provider-acp-registry/server/AcpRegistrySupport";

const REPLAY_SETTINGS = Schema.decodeUnknownSync(AcpRegistrySettings)({
agentId: "replay-agent",
Expand Down Expand Up @@ -56,10 +57,6 @@ function layerAcpRegistryProviderAdapterRegistryReplay(
instanceId: ACP_REGISTRY_DEFAULT_INSTANCE_ID,
settings: REPLAY_SETTINGS,
environment: {},
childProcessSpawner,
resolver: {
resolve: () => Effect.die("ACP registry resolver must not run during replay"),
},
selfInvocation: yield* resolveSelfInvocation(),
makeRuntime: makeAcpReplayRuntime({
transcript,
Expand All @@ -74,7 +71,16 @@ function layerAcpRegistryProviderAdapterRegistryReplay(
return [adapter];
}),
).pipe(
Layer.provide(Layer.mergeAll(layerHost, NodeServices.layer, IdAllocator.layer)),
Layer.provide(
Layer.mergeAll(
layerHost,
NodeServices.layer,
IdAllocator.layer,
Layer.mock(AcpRegistrySupport.AcpRegistryCatalog)({
resolve: () => Effect.die("ACP registry resolver must not run during replay"),
}),
),
),
// Held inbound lines must not outlive the scenario and wedge teardown.
Layer.merge(
Layer.effectDiscard(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import * as Effect from "effect/Effect";
import * as Layer from "effect/Layer";
import * as PlatformError from "effect/PlatformError";
import * as Schema from "effect/Schema";
import { ChildProcessSpawner } from "effect/process";
import * as ChildProcessSpawner from "effect/process/ChildProcessSpawner";
import type * as EffectAcpSchema from "effect-acp/compat";

import * as TestProviderHost from "@t3tools/provider-testing/TestProviderHost";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import * as FileSystem from "effect/FileSystem";
import * as Layer from "effect/Layer";
import * as Path from "effect/Path";
import * as Schema from "effect/Schema";
import { ChildProcessSpawner } from "effect/process";
import * as ChildProcessSpawner from "effect/process/ChildProcessSpawner";
import { HostProcessPlatform } from "@t3tools/shared/hostProcess";
import { resolveSelfInvocation } from "@t3tools/shared/nodeRuntime";

Expand Down
7 changes: 4 additions & 3 deletions apps/server/src/terminal/Manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2253,13 +2253,14 @@ export const makeWithOptions = Effect.fn("TerminalManager.makeWithOptions")(func
options.managedBinaryToolsDir !== undefined
) {
const managedDirectories = yield* acpRegistryManagedBinaryDirectories({
fileSystem,
path,
cacheDir: options.managedBinaryCacheDir,
toolsDir: options.managedBinaryToolsDir,
platform,
architecture,
});
}).pipe(
Effect.provideService(FileSystem.FileSystem, fileSystem),
Effect.provideService(Path.Path, path),
);
if (managedDirectories.length > 0) {
const delimiter = platform === "win32" ? ";" : ":";
const pathKey =
Expand Down
Loading
Loading