Files
PaperClipAI/server/src/services/heartbeat/external-provider.test.ts
T
DottaandPaperclip 2cc7992369 Share the external Runner lifecycle and add strict Muse contracts (#16444)
Add the external Runner foundation with distinct Dot/Grok V6, canonical home-mode V7 and Muse V8 contracts.

Keep generated schemas and profiles synchronized. Bound default question-answer command IDs in a disjoint namespace while preserving existing IDs and durable replay. Preserve canonical migration history and add normally generated, replay-safe Muse schema milestones with literal preview-history fixtures.

Refs #16444.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-16 13:46:50 -05:00

45 lines
3.4 KiB
TypeScript

import { randomUUID } from "node:crypto";
import { describe, expect, it } from "vitest";
import { buildNativeExecutionInput } from "../native-runtime/native-execution-input.js";
import { nativeRuntimeContextFixture } from "../native-runtime/runtime-context.test-fixture.js";
import { heartbeatExternalProvider, nativeRuntimeHasInstructionFilesystem, nativeExecutionUsesTaskWorkspace } from "./external-provider.js";
function execution(provider: "muse" | "openai_dot" | "grok_bot" | "codex") {
const companyId = randomUUID(), agentId = randomUUID();
const binding = { companyId, agentId, bindingId: randomUUID(), bindingGeneration: 1,
acceptByUnixMs: Date.now() + 60_000, expiresAtUnixMs: Date.now() + 600_000 };
return buildNativeExecutionInput({ companyId, agentId, runId: randomUUID(), provider,
...(provider === "muse" ? { museBinding: binding } : provider === "openai_dot" || provider === "grok_bot" ? { dotBinding: binding } : {}),
issue: { id: randomUUID(), identifier: "EXT-1", title: "Bound task", description: null, workMode: "standard" },
taskPrompt: "Complete the task", normalizedSessionId: randomUUID(),
workspace: { id: randomUUID(), cwd: "/controller", repoUrl: null, repoRef: null, branchName: null },
completionContract: { id: randomUUID(), sha256: `sha256:${"a".repeat(64)}`, schemaVersion: "paperclip.run-result.v1",
contract: { revision: "1", objective: "Complete the task", criteria: [{ id: "objective", requirement: "Complete the task" }] } },
runtimeContext: nativeRuntimeContextFixture(),
});
}
describe("shared external heartbeat classification", () => {
it.each(["muse", "openai_dot", "grok_bot"] as const)("keeps %s external on fresh admission and after a configured provider change", provider => {
const agent = { adapterType: "paperclip_runner", adapterConfig: { provider } };
expect(heartbeatExternalProvider({ agent, run: { runtimeMode: "legacy", runnerProfileJson: null } })).toBe(provider);
const admitted = execution(provider);
expect(admitted.schema).toBe(provider === "muse" ? "paperclip.native-execution-input.v8" : "paperclip.native-execution-input.v6");
expect(admitted.provider.kind).toBe(provider);
expect(admitted.session.driverKind).toBe(provider === "muse" ? "muse_external" : provider === "grok_bot" ? "grok_bot_mcp" : "openai_dot_mcp");
expect(heartbeatExternalProvider({ agent: { ...agent, adapterConfig: { provider: "codex" } },
run: { runtimeMode: "native", runnerProfileJson: { nativeExecutionInput: admitted } } })).toBe(provider);
expect(nativeRuntimeHasInstructionFilesystem(admitted.session.driverKind)).toBe(false);
expect(nativeExecutionUsesTaskWorkspace(admitted)).toBe(false);
});
it("does not turn an admitted local provider into an external bridge after configuration changes", () => {
expect(heartbeatExternalProvider({ agent: { adapterType: "paperclip_runner", adapterConfig: { provider: "muse" } },
run: { runtimeMode: "native", runnerProfileJson: { nativeExecutionInput: execution("codex") } } })).toBeNull();
expect(nativeRuntimeHasInstructionFilesystem("codex_app_server")).toBe(true);
expect(nativeExecutionUsesTaskWorkspace(execution("codex"))).toBe(true);
expect(heartbeatExternalProvider({ agent: { adapterType: "process", adapterConfig: { provider: "muse" } },
run: { runtimeMode: "legacy", runnerProfileJson: null } })).toBeNull();
});
});