From 33dac3623608065b5a653e79dacdcd9a16c10320 Mon Sep 17 00:00:00 2001 From: Christian Klotz Date: Sun, 16 Aug 2026 22:53:32 +0300 Subject: [PATCH] feat(coding-agent): move experimental runtime outside CLI Keep command parsing under cli/experimental and adapt missing model identities to the current Harness shape. --- packages/coding-agent/src/bun/cli.ts | 4 +-- packages/coding-agent/src/cli.ts | 6 ++-- .../src/{cli => }/experimental/client.ts | 2 +- .../src/{cli => }/experimental/coordinator.ts | 0 .../experimental/harness-wire-adapter.ts | 7 +++-- .../src/{cli => }/experimental/process.ts | 2 +- .../src/{cli => }/experimental/server.ts | 4 +-- .../experimental/session-worker-manager.ts | 0 .../{cli => }/experimental/session-worker.ts | 2 +- packages/coding-agent/src/main.ts | 4 +-- .../experimental-harness-wire-adapter.test.ts | 31 +++++++++++++------ .../experimental-internal-process.test.ts | 2 +- .../test/experimental-remote-runtime.test.ts | 4 +-- .../experimental-server-lifecycle.test.ts | 2 +- .../test/experimental-server-profile.test.ts | 2 +- .../experimental-server-replacement.test.ts | 2 +- .../experimental-session-directory.test.ts | 2 +- ...erimental-session-worker-lifecycle.test.ts | 2 +- ...xperimental-session-worker-manager.test.ts | 4 +-- 19 files changed, 49 insertions(+), 33 deletions(-) rename packages/coding-agent/src/{cli => }/experimental/client.ts (97%) rename packages/coding-agent/src/{cli => }/experimental/coordinator.ts (100%) rename packages/coding-agent/src/{cli => }/experimental/harness-wire-adapter.ts (98%) rename packages/coding-agent/src/{cli => }/experimental/process.ts (98%) rename packages/coding-agent/src/{cli => }/experimental/server.ts (99%) rename packages/coding-agent/src/{cli => }/experimental/session-worker-manager.ts (100%) rename packages/coding-agent/src/{cli => }/experimental/session-worker.ts (99%) diff --git a/packages/coding-agent/src/bun/cli.ts b/packages/coding-agent/src/bun/cli.ts index 9a023f6fd..ae72d6e52 100644 --- a/packages/coding-agent/src/bun/cli.ts +++ b/packages/coding-agent/src/bun/cli.ts @@ -1,6 +1,6 @@ #!/usr/bin/env node -import { runCoordinatorProcess } from "../cli/experimental/coordinator.ts"; -import { consumeInternalProcessRole, getInternalProcessRole } from "../cli/experimental/process.ts"; +import { runCoordinatorProcess } from "../experimental/coordinator.ts"; +import { consumeInternalProcessRole, getInternalProcessRole } from "../experimental/process.ts"; import { restoreSandboxEnv } from "./restore-sandbox-env.ts"; restoreSandboxEnv(); diff --git a/packages/coding-agent/src/cli.ts b/packages/coding-agent/src/cli.ts index e3d969ead..3108a13b1 100644 --- a/packages/coding-agent/src/cli.ts +++ b/packages/coding-agent/src/cli.ts @@ -1,9 +1,9 @@ #!/usr/bin/env node -import { consumeInternalProcessRole } from "./cli/experimental/process.ts"; -import { runServerProcess } from "./cli/experimental/server.ts"; -import { runSessionWorkerProcess } from "./cli/experimental/session-worker.ts"; import { APP_NAME } from "./config.ts"; import { configureHttpDispatcher } from "./core/http-dispatcher.ts"; +import { consumeInternalProcessRole } from "./experimental/process.ts"; +import { runServerProcess } from "./experimental/server.ts"; +import { runSessionWorkerProcess } from "./experimental/session-worker.ts"; import { main } from "./main.ts"; const internalProcessRole = consumeInternalProcessRole(); diff --git a/packages/coding-agent/src/cli/experimental/client.ts b/packages/coding-agent/src/experimental/client.ts similarity index 97% rename from packages/coding-agent/src/cli/experimental/client.ts rename to packages/coding-agent/src/experimental/client.ts index 34566cef0..63ddfe956 100644 --- a/packages/coding-agent/src/cli/experimental/client.ts +++ b/packages/coding-agent/src/experimental/client.ts @@ -2,7 +2,7 @@ import { basename } from "node:path"; import { PiClient } from "@earendil-works/pi-client"; import { createUnixTransportFactory, discoverUnixServers, type UnixServerRoute } from "@earendil-works/pi-client/unix"; import { isServerId } from "@earendil-works/pi-protocol"; -import type { ClientCommand } from "./commands/client.ts"; +import type { ClientCommand } from "../cli/experimental/commands/client.ts"; import { activateServer, ENV_SERVER_ID, resolveServerDirectory, resolveSessionDirectory } from "./server.ts"; export type ClientResult = diff --git a/packages/coding-agent/src/cli/experimental/coordinator.ts b/packages/coding-agent/src/experimental/coordinator.ts similarity index 100% rename from packages/coding-agent/src/cli/experimental/coordinator.ts rename to packages/coding-agent/src/experimental/coordinator.ts diff --git a/packages/coding-agent/src/cli/experimental/harness-wire-adapter.ts b/packages/coding-agent/src/experimental/harness-wire-adapter.ts similarity index 98% rename from packages/coding-agent/src/cli/experimental/harness-wire-adapter.ts rename to packages/coding-agent/src/experimental/harness-wire-adapter.ts index 5c9f9c2a3..b7b106174 100644 --- a/packages/coding-agent/src/cli/experimental/harness-wire-adapter.ts +++ b/packages/coding-agent/src/experimental/harness-wire-adapter.ts @@ -294,7 +294,10 @@ function toWireRunValue(value: Extract["value"]) reason: "missing_identities", runId: value.runId, leafId: value.leafId, - missing: { tools: [...value.missing.tools], models: [...value.missing.models] }, + missing: { + tools: [...value.missing.tools], + models: value.missing.model === undefined ? [] : [value.missing.model], + }, }; default: return assertNever(value); @@ -316,7 +319,7 @@ function toWireRunError(error: Extract["error"] _tag: "MissingIdentities", lane: error.lane, tools: [...error.tools], - models: [...error.models], + models: error.model === undefined ? [] : [error.model], message: error.message, }; case "InvalidMessage": diff --git a/packages/coding-agent/src/cli/experimental/process.ts b/packages/coding-agent/src/experimental/process.ts similarity index 98% rename from packages/coding-agent/src/cli/experimental/process.ts rename to packages/coding-agent/src/experimental/process.ts index 6220f61a1..21e50dc3e 100644 --- a/packages/coding-agent/src/cli/experimental/process.ts +++ b/packages/coding-agent/src/experimental/process.ts @@ -1,6 +1,6 @@ import { type ChildProcess, spawn } from "node:child_process"; import { fileURLToPath } from "node:url"; -import { isBunBinary } from "../../config.ts"; +import { isBunBinary } from "../config.ts"; export const INTERNAL_PROCESS_ENV = "__PI_INTERNAL_SPAWN"; diff --git a/packages/coding-agent/src/cli/experimental/server.ts b/packages/coding-agent/src/experimental/server.ts similarity index 99% rename from packages/coding-agent/src/cli/experimental/server.ts rename to packages/coding-agent/src/experimental/server.ts index 000f16de4..d253093fd 100644 --- a/packages/coding-agent/src/cli/experimental/server.ts +++ b/packages/coding-agent/src/experimental/server.ts @@ -11,8 +11,8 @@ import { isServerId, type ServerId } from "@earendil-works/pi-protocol"; import type { PiServer, PiServerHost } from "@earendil-works/pi-server"; import { createUnixServer, getUnixSocketPath } from "@earendil-works/pi-server/unix"; import lockfile from "proper-lockfile"; -import { getAgentDir } from "../../config.ts"; -import { resolvePath } from "../../utils/paths.ts"; +import { getAgentDir } from "../config.ts"; +import { resolvePath } from "../utils/paths.ts"; import { CoordinatorConnection, type CoordinatorStartupLease, ensureCoordinator } from "./coordinator.ts"; import { consumeInternalProcessRole, spawnInternalProcess, terminateInternalProcess } from "./process.ts"; import { SessionWorkerManager } from "./session-worker-manager.ts"; diff --git a/packages/coding-agent/src/cli/experimental/session-worker-manager.ts b/packages/coding-agent/src/experimental/session-worker-manager.ts similarity index 100% rename from packages/coding-agent/src/cli/experimental/session-worker-manager.ts rename to packages/coding-agent/src/experimental/session-worker-manager.ts diff --git a/packages/coding-agent/src/cli/experimental/session-worker.ts b/packages/coding-agent/src/experimental/session-worker.ts similarity index 99% rename from packages/coding-agent/src/cli/experimental/session-worker.ts rename to packages/coding-agent/src/experimental/session-worker.ts index e748ecb37..e7ce4f2e5 100644 --- a/packages/coding-agent/src/cli/experimental/session-worker.ts +++ b/packages/coding-agent/src/experimental/session-worker.ts @@ -22,7 +22,7 @@ import { import lockfile from "proper-lockfile"; import Type, { type Static } from "typebox"; import { Check } from "typebox/value"; -import { ModelRuntime } from "../../core/model-runtime.ts"; +import { ModelRuntime } from "../core/model-runtime.ts"; import { toHarnessPromptArguments, toWireRunResult } from "./harness-wire-adapter.ts"; import { consumeInternalProcessRole, encodeControlLine, MAX_CONTROL_LINE_BYTES } from "./process.ts"; diff --git a/packages/coding-agent/src/main.ts b/packages/coding-agent/src/main.ts index e6c06ff95..2e0cf2060 100644 --- a/packages/coding-agent/src/main.ts +++ b/packages/coding-agent/src/main.ts @@ -27,10 +27,8 @@ import { } from "./cli/auth-command.ts"; import { resolveCredentialForPrint } from "./cli/credential-print.ts"; import { cli as experimentalCli } from "./cli/experimental/cli.ts"; -import { runClient } from "./cli/experimental/client.ts"; import type { ClientCommand } from "./cli/experimental/commands/client.ts"; import type { ServerCommand } from "./cli/experimental/commands/server.ts"; -import { startForegroundServer } from "./cli/experimental/server.ts"; import { processFileArguments } from "./cli/file-processor.ts"; import { buildInitialMessage } from "./cli/initial-message.ts"; import { listModels } from "./cli/list-models.ts"; @@ -65,6 +63,8 @@ import { assertValidSessionId, SessionManager } from "./core/session-manager.ts" import { SettingsManager } from "./core/settings-manager.ts"; import { printTimings, resetTimings, time } from "./core/timings.ts"; import { hasTrustRequiringProjectResources, ProjectTrustStore } from "./core/trust-manager.ts"; +import { runClient } from "./experimental/client.ts"; +import { startForegroundServer } from "./experimental/server.ts"; import { builtInExtensions } from "./extensions/index.ts"; import { runMigrations, showDeprecationWarnings } from "./migrations.ts"; import { InteractiveMode, runPrintMode, runRpcMode } from "./modes/index.ts"; diff --git a/packages/coding-agent/test/experimental-harness-wire-adapter.test.ts b/packages/coding-agent/test/experimental-harness-wire-adapter.test.ts index 22689e52d..66db759ce 100644 --- a/packages/coding-agent/test/experimental-harness-wire-adapter.test.ts +++ b/packages/coding-agent/test/experimental-harness-wire-adapter.test.ts @@ -11,7 +11,7 @@ import type { AssistantMessage } from "@earendil-works/pi-ai"; import { type PromptArguments, type PromptMessage, RunResultSchema } from "@earendil-works/pi-protocol"; import { Check } from "typebox/value"; import { describe, expect, test } from "vitest"; -import { toHarnessPromptArguments, toWireRunResult } from "../src/cli/experimental/harness-wire-adapter.ts"; +import { toHarnessPromptArguments, toWireRunResult } from "../src/experimental/harness-wire-adapter.ts"; const usage = { input: 1, @@ -165,20 +165,33 @@ describe("Harness wire adapter", () => { deferred: { provider: "test", modelId: "model", api: "test", id: "deferred-1", data: { row: 1 } }, }, }, - { + ] satisfies HarnessRunResult[])("projects every Run outcome %#", (result) => { + const wire = toWireRunResult(result); + expect(Check(RunResultSchema, wire)).toBe(true); + expect(wire).toEqual(result); + }); + + test("projects singular Harness model identities to the wire list", () => { + const wire = toWireRunResult({ ok: true, value: { kind: "suspended", reason: "missing_identities", runId: "run-1", leafId: "leaf-1", - missing: { tools: ["read"], models: [] }, + missing: { tools: ["read"], model: "missing-model" }, }, - }, - ] satisfies HarnessRunResult[])("projects every Run outcome %#", (result) => { - const wire = toWireRunResult(result); - expect(Check(RunResultSchema, wire)).toBe(true); - expect(wire).toEqual(result); + }); + expect(wire).toEqual({ + ok: true, + value: { + kind: "suspended", + reason: "missing_identities", + runId: "run-1", + leafId: "leaf-1", + missing: { tools: ["read"], models: ["missing-model"] }, + }, + }); }); test.each([ @@ -188,7 +201,7 @@ describe("Harness wire adapter", () => { operationKind: "run", message: "busy", }), - new MissingIdentities({ lane: "main", tools: ["read"], models: [], message: "missing" }), + new MissingIdentities({ lane: "main", tools: ["read"], model: "missing-model", message: "missing" }), new InvalidMessage({ lane: "main", reason: "invalid", message: "invalid" }), new UnknownSkill({ name: "skill", message: "unknown" }), new UnknownTemplate({ name: "template", message: "unknown" }), diff --git a/packages/coding-agent/test/experimental-internal-process.test.ts b/packages/coding-agent/test/experimental-internal-process.test.ts index 7e74c91ad..dc1f37d34 100644 --- a/packages/coding-agent/test/experimental-internal-process.test.ts +++ b/packages/coding-agent/test/experimental-internal-process.test.ts @@ -5,7 +5,7 @@ import { createConnection } from "node:net"; import { tmpdir } from "node:os"; import { join } from "node:path"; import { afterEach, describe, expect, test } from "vitest"; -import { spawnInternalProcess, terminateInternalProcess } from "../src/cli/experimental/process.ts"; +import { spawnInternalProcess, terminateInternalProcess } from "../src/experimental/process.ts"; const children = new Set(); const directories = new Set(); diff --git a/packages/coding-agent/test/experimental-remote-runtime.test.ts b/packages/coding-agent/test/experimental-remote-runtime.test.ts index 3c4296832..d2ab1bd72 100644 --- a/packages/coding-agent/test/experimental-remote-runtime.test.ts +++ b/packages/coding-agent/test/experimental-remote-runtime.test.ts @@ -3,8 +3,8 @@ import { join, resolve } from "node:path"; import { PiClient } from "@earendil-works/pi-client"; import { createUnixTransportFactory } from "@earendil-works/pi-client/unix"; import { afterEach, beforeEach, describe, expect, test, vi } from "vitest"; -import { runClient } from "../src/cli/experimental/client.ts"; -import { type RunningServer, startServer } from "../src/cli/experimental/server.ts"; +import { runClient } from "../src/experimental/client.ts"; +import { type RunningServer, startServer } from "../src/experimental/server.ts"; import { configureExperimentalWorkerModel, createExperimentalSessions } from "./experimental-session-support.ts"; const servers = new Set(); diff --git a/packages/coding-agent/test/experimental-server-lifecycle.test.ts b/packages/coding-agent/test/experimental-server-lifecycle.test.ts index 2afe09224..b4eadc461 100644 --- a/packages/coding-agent/test/experimental-server-lifecycle.test.ts +++ b/packages/coding-agent/test/experimental-server-lifecycle.test.ts @@ -1,5 +1,5 @@ import { afterEach, describe, expect, test, vi } from "vitest"; -import { ServerLifetime } from "../src/cli/experimental/server.ts"; +import { ServerLifetime } from "../src/experimental/server.ts"; afterEach(() => { vi.useRealTimers(); diff --git a/packages/coding-agent/test/experimental-server-profile.test.ts b/packages/coding-agent/test/experimental-server-profile.test.ts index 2c318d4f6..ce53103ef 100644 --- a/packages/coding-agent/test/experimental-server-profile.test.ts +++ b/packages/coding-agent/test/experimental-server-profile.test.ts @@ -2,7 +2,7 @@ import { mkdtemp, readdir, readFile, rm, writeFile } from "node:fs/promises"; import { tmpdir } from "node:os"; import { join } from "node:path"; import { afterEach, describe, expect, test } from "vitest"; -import { acquireServerProfile } from "../src/cli/experimental/server.ts"; +import { acquireServerProfile } from "../src/experimental/server.ts"; const directories = new Set(); const FIRST_SERVER_ID = "00000000-0000-4000-8000-000000000001"; diff --git a/packages/coding-agent/test/experimental-server-replacement.test.ts b/packages/coding-agent/test/experimental-server-replacement.test.ts index 05ddeb735..c20fe854c 100644 --- a/packages/coding-agent/test/experimental-server-replacement.test.ts +++ b/packages/coding-agent/test/experimental-server-replacement.test.ts @@ -4,7 +4,7 @@ import { lstat, mkdir, mkdtemp, rm } from "node:fs/promises"; import { join } from "node:path"; import { fileURLToPath } from "node:url"; import { afterEach, describe, expect, test } from "vitest"; -import { runClient } from "../src/cli/experimental/client.ts"; +import { runClient } from "../src/experimental/client.ts"; import { configureExperimentalWorkerModel, createExperimentalSessions } from "./experimental-session-support.ts"; const cliPath = fileURLToPath(new URL("../src/cli.ts", import.meta.url)); diff --git a/packages/coding-agent/test/experimental-session-directory.test.ts b/packages/coding-agent/test/experimental-session-directory.test.ts index 4b4257067..416032679 100644 --- a/packages/coding-agent/test/experimental-session-directory.test.ts +++ b/packages/coding-agent/test/experimental-session-directory.test.ts @@ -1,7 +1,7 @@ import { homedir } from "node:os"; import { resolve } from "node:path"; import { afterEach, describe, expect, test, vi } from "vitest"; -import { resolveSessionDirectory } from "../src/cli/experimental/server.ts"; +import { resolveSessionDirectory } from "../src/experimental/server.ts"; afterEach(() => vi.unstubAllEnvs()); diff --git a/packages/coding-agent/test/experimental-session-worker-lifecycle.test.ts b/packages/coding-agent/test/experimental-session-worker-lifecycle.test.ts index c3dc2bce9..26376f424 100644 --- a/packages/coding-agent/test/experimental-session-worker-lifecycle.test.ts +++ b/packages/coding-agent/test/experimental-session-worker-lifecycle.test.ts @@ -1,5 +1,5 @@ import { afterEach, describe, expect, test, vi } from "vitest"; -import { WorkerLifecycle } from "../src/cli/experimental/session-worker.ts"; +import { WorkerLifecycle } from "../src/experimental/session-worker.ts"; const GENERATION = "generation-1"; diff --git a/packages/coding-agent/test/experimental-session-worker-manager.test.ts b/packages/coding-agent/test/experimental-session-worker-manager.test.ts index 7f30df686..62b95507d 100644 --- a/packages/coding-agent/test/experimental-session-worker-manager.test.ts +++ b/packages/coding-agent/test/experimental-session-worker-manager.test.ts @@ -1,7 +1,7 @@ import type { JsonlSessionMetadata } from "@earendil-works/pi-agent-core"; import { describe, expect, test } from "vitest"; -import type { CoordinatorConnectionEvent } from "../src/cli/experimental/coordinator.ts"; -import { SessionWorkerManager } from "../src/cli/experimental/session-worker-manager.ts"; +import type { CoordinatorConnectionEvent } from "../src/experimental/coordinator.ts"; +import { SessionWorkerManager } from "../src/experimental/session-worker-manager.ts"; const metadata: JsonlSessionMetadata = { id: "session-1",