123 lines
4.2 KiB
TypeScript
123 lines
4.2 KiB
TypeScript
/* eslint-disable max-classes-per-file -- the AgentOS service and its tagged error form one adapter contract. */
|
|
import codex from "@agentos-software/codex-cli";
|
|
import { agentOS as createAgentOsActor } from "@rivet-dev/agentos";
|
|
import type { AgentOSConfigInput } from "@rivet-dev/agentos";
|
|
import { Context, Effect, Layer, Schema } from "effect";
|
|
|
|
import { getExecutableGitSoftware } from "./agent-os-git-software";
|
|
|
|
const withGitSoftware = (
|
|
config: AgentOSConfigInput<undefined>
|
|
): AgentOSConfigInput<undefined> => ({
|
|
...config,
|
|
software: [getExecutableGitSoftware(), ...(config.software ?? [])],
|
|
});
|
|
|
|
export class AgentOsError extends Schema.TaggedErrorClass<AgentOsError>()(
|
|
"AgentOsError",
|
|
{
|
|
cause: Schema.Defect(),
|
|
}
|
|
) {}
|
|
|
|
export interface AgentOsOptions {
|
|
readonly config?: AgentOSConfigInput<undefined>;
|
|
}
|
|
|
|
export class AgentOs extends Context.Service<
|
|
AgentOs,
|
|
{
|
|
readonly createActor: (
|
|
config?: AgentOSConfigInput<undefined>
|
|
) => Effect.Effect<
|
|
ReturnType<typeof createAgentOsActor<undefined>>,
|
|
AgentOsError
|
|
>;
|
|
}
|
|
>()("@code/primitives/agent-os/AgentOs") {
|
|
static readonly layer = Layer.succeed(
|
|
AgentOs,
|
|
AgentOs.of({
|
|
createActor: Effect.fn("AgentOs.createActor")(function* createActor(
|
|
config: AgentOSConfigInput<undefined> = {}
|
|
) {
|
|
return yield* Effect.try({
|
|
catch: (cause) => new AgentOsError({ cause }),
|
|
try: () => createAgentOsActor<undefined>(withGitSoftware(config)),
|
|
});
|
|
}),
|
|
})
|
|
);
|
|
}
|
|
|
|
export const makeAgentOsLayer = (options: AgentOsOptions = {}) =>
|
|
Layer.succeed(
|
|
AgentOs,
|
|
AgentOs.of({
|
|
createActor: Effect.fn("AgentOs.createActor")(function* createActor(
|
|
config: AgentOSConfigInput<undefined> = options.config ?? {}
|
|
) {
|
|
return yield* Effect.try({
|
|
catch: (cause) => new AgentOsError({ cause }),
|
|
try: () => createAgentOsActor<undefined>(withGitSoftware(config)),
|
|
});
|
|
}),
|
|
})
|
|
);
|
|
|
|
export const createAgentOsActorEffect = Effect.fn("createAgentOsActorEffect")(
|
|
function* createAgentOsActorEffect(config?: AgentOSConfigInput<undefined>) {
|
|
const agentOs = yield* AgentOs;
|
|
return yield* agentOs.createActor(config);
|
|
}
|
|
);
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Codex harness configuration
|
|
//
|
|
// The canonical execution registry runs exactly one AgentOS actor for the
|
|
// puter/zopu-code repo, booted with the Codex CLI harness + git. The model
|
|
// provider is injected as VM env (OpenAI-compatible base URL + key) so Codex
|
|
// authenticates against the configured gateway without a second credentials
|
|
// path. This is a pure config builder; the RivetKit registry/server that hosts
|
|
// the actor lives in the agents package.
|
|
// ---------------------------------------------------------------------------
|
|
|
|
/** Software bundle for a Codex-capable VM: the Codex harness plus git. */
|
|
export const codexSoftware = [codex, getExecutableGitSoftware()] as const;
|
|
|
|
/** Model-provider env that Codex reads inside the VM. Pass this to the session's
|
|
* `env` when opening a Codex session (`agent: "codex"`). */
|
|
export interface CodexModelEnv {
|
|
readonly apiKey: string;
|
|
readonly baseUrl: string;
|
|
readonly model: string;
|
|
}
|
|
|
|
/** Builds the VM env record Codex reads to authenticate against the model gateway. */
|
|
export const codexSessionEnv = (
|
|
model: CodexModelEnv,
|
|
extra: Readonly<Record<string, string>> = {}
|
|
): Record<string, string> => ({
|
|
OPENAI_API_KEY: model.apiKey,
|
|
OPENAI_BASE_URL: model.baseUrl,
|
|
...extra,
|
|
});
|
|
|
|
export interface CodexAgentOsConfigInput {
|
|
/** Extra software merged after the Codex+git bundle. */
|
|
readonly software?: NonNullable<AgentOSConfigInput<undefined>["software"]>;
|
|
}
|
|
|
|
/**
|
|
* Builds an AgentOS actor config for a Codex-backed VM. The Codex CLI and git
|
|
* are always present; software the caller passes is appended, never replacing
|
|
* the harness or git. Model-provider env is supplied per session via
|
|
* `codexSessionEnv`, not here — the actor config has no env field.
|
|
*/
|
|
export const makeCodexAgentOsConfig = (
|
|
input: CodexAgentOsConfigInput = {}
|
|
): AgentOSConfigInput<undefined> => ({
|
|
software: [...codexSoftware, ...(input.software ?? [])],
|
|
});
|