Files
zopu-code/packages/agents/src/orb/domain.test.ts
T
-Puter 22ebb89ff0 feat(agents/orb): harden Docker sandbox lifecycle, proof staging, and tests
Fix detached process startup in docker-sandbox.ts: capture the real process
group PID via setsid, redirect stdout/stderr to durable log files, refresh
status and exit code after natural completion, make getProcessLogs return real
logs, and implement bounded TERM/KILL cleanup with no leftover containers.
Switch the adapter to node:child_process (supported by Bun) so tests execute
under vitest, add -i to docker exec so staged files reach cat, and assert the
launcher is nonempty before launch.

Ensure Orb creation disposes the VM before its sandbox on partial failure
(AgentOS creation or OpenCode linking), and dispose the VM before its sandbox
in OrbHandle.dispose. Fix the Effect state callbacks (tapError/sync/asVoid) and
prepareRepository branch/base shell quoting.

Split normalizeSessionEvent to stay under the complexity limit without
suppressing the rule. Raw outbound prompts stay unchanged; only persisted and
logged event copies are redacted.

Rewrite scripts/orb-proof.ts to emit explicit stable stage markers for Docker,
AgentOS/OpenCode, and the model turn; ORB_GATEWAY_* fall back safely to
AGENT_MODEL_* without printing secrets; an unreachable gateway is BLOCKED not
passed; container removal is verified after disposal.

Strengthen tests: drop the any-failure createOrb test and add focused Docker
lifecycle coverage (PID, logs, status, exit code, stop/kill, removal) guarded
only when Docker is unavailable.
2026-07-24 22:03:21 +05:30

133 lines
4.1 KiB
TypeScript

import { Effect } from "effect";
import { describe, expect, it } from "vitest";
import {
orbActorKey,
OrbStateError,
transitionOrbState,
transitionRunState,
} from "./domain";
const runTransition = (from: string, to: string) =>
Effect.runSync(
Effect.flip(transitionOrbState({ from: from as never, to: to as never }))
);
const runTransitionOk = (from: string, to: string) =>
Effect.runSync(transitionOrbState({ from: from as never, to: to as never }));
const runRunState = (from: string, to: string) =>
Effect.runSync(
Effect.flip(transitionRunState({ from: from as never, to: to as never }))
);
const runRunStateOk = (from: string, to: string) =>
Effect.runSync(transitionRunState({ from: from as never, to: to as never }));
describe("orbActorKey", () => {
it("produces a stable key from the identity triple", () => {
const key = orbActorKey({
projectId: "prj-1",
runId: "run-1",
workUnitId: "wrk-1",
});
expect(key).toBe("project:prj-1:work:wrk-1:run:run-1");
});
it("preserves order across different identities", () => {
expect(orbActorKey({ projectId: "a", runId: "b", workUnitId: "c" })).toBe(
"project:a:work:c:run:b"
);
});
});
describe("transitionOrbState", () => {
it("allows creating -> prepared", () => {
expect(runTransitionOk("creating", "prepared")).toBe("prepared");
});
it("allows prepared -> running", () => {
expect(runTransitionOk("prepared", "running")).toBe("running");
});
it("allows running -> needs-input", () => {
expect(runTransitionOk("running", "needs-input")).toBe("needs-input");
});
it("allows running -> completed", () => {
expect(runTransitionOk("running", "completed")).toBe("completed");
});
it("allows completed -> disposed", () => {
expect(runTransitionOk("completed", "disposed")).toBe("disposed");
});
it("allows needs-input -> running (resume)", () => {
expect(runTransitionOk("needs-input", "running")).toBe("running");
});
it("rejects disposed -> running", () => {
const error = runTransition("disposed", "running");
expect(error).toBeInstanceOf(OrbStateError);
expect(error.reason).toBe("AlreadyDisposed");
});
it("rejects completed -> creating", () => {
const error = runTransition("completed", "creating");
expect(error).toBeInstanceOf(OrbStateError);
expect(error.reason).toBe("InvalidTransition");
});
it("rejects prepared -> needs-input (skipping running)", () => {
const error = runTransition("prepared", "needs-input");
expect(error).toBeInstanceOf(OrbStateError);
expect(error.reason).toBe("InvalidTransition");
});
it("rejects cancelled -> running", () => {
const error = runTransition("cancelled", "running");
expect(error).toBeInstanceOf(OrbStateError);
expect(error.reason).toBe("InvalidTransition");
});
});
describe("transitionRunState", () => {
it("allows queued -> provisioning", () => {
expect(runRunStateOk("queued", "provisioning")).toBe("provisioning");
});
it("allows provisioning -> preparing", () => {
expect(runRunStateOk("provisioning", "preparing")).toBe("preparing");
});
it("allows running -> verifying", () => {
expect(runRunStateOk("running", "verifying")).toBe("verifying");
});
it("allows verifying -> succeeded", () => {
expect(runRunStateOk("verifying", "succeeded")).toBe("succeeded");
});
it("allows running -> cancelled", () => {
expect(runRunStateOk("running", "cancelled")).toBe("cancelled");
});
it("rejects succeeded -> running (terminal)", () => {
const error = runRunState("succeeded", "running");
expect(error).toBeInstanceOf(OrbStateError);
expect(error.reason).toBe("InvalidTransition");
});
it("rejects failed -> running (terminal)", () => {
const error = runRunState("failed", "running");
expect(error).toBeInstanceOf(OrbStateError);
expect(error.reason).toBe("InvalidTransition");
});
it("rejects queued -> succeeded (skipping steps)", () => {
const error = runRunState("queued", "succeeded");
expect(error).toBeInstanceOf(OrbStateError);
expect(error.reason).toBe("InvalidTransition");
});
});