From caeff5d86e2aea9edef0603b63d87d543a6df493 Mon Sep 17 00:00:00 2001 From: Taras Mankovski <74687+taras@users.noreply.github.com> Date: Sun, 30 Aug 2026 22:04:14 -0400 Subject: [PATCH 1/2] =?UTF-8?q?=F0=9F=90=9B=20Run=20and=20plan=20through?= =?UTF-8?q?=20this=20build's=20own=20ACP=20adapters=20(#672)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `xmd plan` failed with both providers on a machine where Codex and Claude are installed and authenticated. Its provider was built without an agent registry, so ACPX's own table answered: `codex` spawned an `npx` of the published adapter that build pins, which runs its own bundled agent, and `claude` ran a stock adapter that never publishes the session-materialization signal a first turn waits for. `xmd run` assembled its provider the same way and had the same gap. Both now resolve agents through the overlay the workflow attachment uses — ACPX's registry with this build's two patched snapshots over the top — and materialize one at the availability probe, under `~/.xmd/adapters`. An agent this build carries no snapshot for resolves exactly as it did. The registry and the preparation are stated together by `embeddedAdapterDependencies`, so a host cannot take one without the other, and the adapters are part of the settled Agent stack: the assistant that writes a Plan and the run of the approved Plan launch from the same snapshot. Claude-Session: https://claude.ai/code/session_01TNJwcFmnt3kYSn9gGsx9u7 --- packages/acp/embedded-adapters.ts | 10 +- packages/acp/src/adapter-snapshots.ts | 34 +++++ packages/acp/vendor/adapters/PROVENANCE.md | 3 +- packages/cli/src/agent-stack.ts | 58 +++++-- packages/cli/src/authorship-profile.ts | 16 +- packages/cli/src/workflow-agent.ts | 26 +--- packages/cli/tests/agent-adapters.test.ts | 166 +++++++++++++++++++++ packages/cli/tests/plan-cli.test.ts | 2 + packages/cli/tests/plan.test.ts | 2 + packages/cli/tests/support/plan-harness.ts | 14 ++ specs/acp-client-spec.md | 18 ++- 11 files changed, 308 insertions(+), 41 deletions(-) create mode 100644 packages/cli/tests/agent-adapters.test.ts diff --git a/packages/acp/embedded-adapters.ts b/packages/acp/embedded-adapters.ts index 5278dd4e..2242f1f4 100644 --- a/packages/acp/embedded-adapters.ts +++ b/packages/acp/embedded-adapters.ts @@ -5,21 +5,23 @@ * * Its own entrypoint, and deliberately not part of `@executablemd/acp`. This is * a temporary arrangement: no published Codex or Claude release names the turn - * a Prompt completed, so a workflow run executes the snapshots under - * `vendor/adapters` instead. Issue #636 removes them, one provider at a time, - * as qualifying releases appear. + * a Prompt completed, so every profile that runs one of those two agents + * executes the snapshots under `vendor/adapters` instead. Issue #636 removes + * them, one provider at a time, as qualifying releases appear. * * Anything on the package root is a stable contract somebody may build on, and * withdrawing one is a compatibility break. A workaround should not be able to * earn that, so it lives here and goes away with the thing it exists for. * - * The workflow Agent profile is the only caller. + * The CLI's three Agent profiles — the workflow attachment, `xmd run` and the + * `xmd plan` authorship ceiling — are the callers. */ export { AdapterSnapshotError, carriesEmbeddedAdapter, createEmbeddedAdapters, + embeddedAdapterDependencies, embeddedAdapterIdentities, overlaidAdapterRegistry, } from "./src/adapter-snapshots.ts"; diff --git a/packages/acp/src/adapter-snapshots.ts b/packages/acp/src/adapter-snapshots.ts index df658941..67a88bcd 100644 --- a/packages/acp/src/adapter-snapshots.ts +++ b/packages/acp/src/adapter-snapshots.ts @@ -81,6 +81,7 @@ import { readdir, readFile, rename as renamePath, rm as rmPath, writeFile } from import { exec, useQuietProcessOutput } from "@executablemd/runtime"; import { createAgentRegistry } from "./acpx-runtime.ts"; import type { AcpAgentRegistry } from "./acpx-runtime.ts"; +import type { AcpxProviderDependencies } from "./provider.ts"; import { ensure, type Operation, scoped, sleep } from "effection"; import { Buffer } from "node:buffer"; import { createHash, randomUUID } from "node:crypto"; @@ -722,6 +723,39 @@ export function carriesEmbeddedAdapter(adapters: EmbeddedAdapters, agentName: st return adapters.providers.includes(agentName); } +/** + * What a host that carries its own adapters states to a provider. + * + * Both clauses together, because neither is an arrangement on its own: the + * registry says Codex and Claude resolve to this build's snapshot, and the + * preparation is what puts that snapshot on disk. A host that stated only the + * registry would name a file nothing had written, and one that stated only the + * preparation would materialize an adapter and then run whatever `npx` resolved. + * + * Preparation happens at the first point the provider would run that command, + * which is its availability probe — earlier than a `` placement, and + * earlier than any turn. A snapshot that cannot prove itself refuses the agent + * there rather than surfacing later as an adapter that would not start. + * + * It is asked only about an agent this build actually carries. An agent ACPX + * resolves is already a command on this machine, so there is nothing to put on + * disk for it, and reaching into the snapshots to find that out would make every + * run pay for a mechanism that has nothing to say about it. + */ +export function embeddedAdapterDependencies( + adapters: EmbeddedAdapters, +): Pick { + return { + agentRegistry: overlaidAdapterRegistry(adapters), + *prepareAgent(agentName: string): Operation { + if (!carriesEmbeddedAdapter(adapters, agentName)) { + return; + } + yield* adapters.materialize(agentName); + }, + }; +} + /** Every embedded snapshot's identity, for provenance checks and diagnostics. */ export function embeddedAdapterIdentities(): readonly EmbeddedAdapterSnapshot[] { return EMBEDDED_ADAPTER_SNAPSHOTS; diff --git a/packages/acp/vendor/adapters/PROVENANCE.md b/packages/acp/vendor/adapters/PROVENANCE.md index 81d1ebe9..241167a3 100644 --- a/packages/acp/vendor/adapters/PROVENANCE.md +++ b/packages/acp/vendor/adapters/PROVENANCE.md @@ -1,6 +1,7 @@ # Embedded ACP adapter provenance -This directory carries one npm tarball per provider, and a workflow run executes +This directory carries one npm tarball per provider, and every command that runs +one of those providers — `xmd workflow`, `xmd run` and `xmd plan` — executes those instead of the adapter `npx` would resolve. | Provider | Package | Version | Contracts | diff --git a/packages/cli/src/agent-stack.ts b/packages/cli/src/agent-stack.ts index 353ec094..aabc2153 100644 --- a/packages/cli/src/agent-stack.ts +++ b/packages/cli/src/agent-stack.ts @@ -24,13 +24,32 @@ import type { AgentProviderFactory, PermissionMode } from "@executablemd/core"; import { installForegroundLauncher, env as readEnv } from "@executablemd/runtime"; import { createAcpxProvider, DEFAULT_AGENT_NAME } from "@executablemd/acp"; import type { AcpxProviderDependencies } from "@executablemd/acp"; +// A separate entrypoint because the embedded adapters are temporary (#636) and +// must not become part of the package's stable surface. +import { + createEmbeddedAdapters, + embeddedAdapterDependencies, +} from "@executablemd/acp/embedded-adapters"; +import type { EmbeddedAdapters } from "@executablemd/acp/embedded-adapters"; import { Err, Ok } from "effection"; import type { Operation, Result } from "effection"; +import { homedir } from "node:os"; +import { join } from "node:path"; import { resolveAgentConfig } from "./agent-config.ts"; import type { AgentFlags } from "./agent-config.ts"; import type { MachineSessionAssembly } from "./session-coordinator.ts"; +/** + * Where a command that is not a workflow run materializes its adapters. + * + * One root for this machine, content-addressed beneath it: two invocations + * asking for the same adapter name the same directory, so the second one runs + * what the first installed, and a build carrying a different snapshot names a + * different directory instead of deciding whether this one is current. + */ +export const DEFAULT_ADAPTER_ROOT: string = join(homedir(), ".xmd", "adapters"); + /** Everything one invocation settled about agents, resolved exactly once. */ export interface AgentStack { /** The provider name the caller selected, already known to be registered. */ @@ -38,6 +57,14 @@ export interface AgentStack { /** The agent every consumer defaults to, environment fallback applied. */ defaultAgent: string; permissionMode: PermissionMode; + /** + * The ACP adapters this build carries, and where this host puts them. + * + * Part of the one settled answer because both consumers resolve agents + * through it: the assistant that writes a Plan and the run of the approved + * Plan are the same Codex or Claude, launched from the same snapshot. + */ + adapters: EmbeddedAdapters; /** What this host states about machine-wide agent sessions, if anything. */ sessions?: MachineSessionAssembly; } @@ -67,25 +94,32 @@ export function* resolveAgentStack( provider: flags.agentProvider, defaultAgent, permissionMode: config.permissionMode, + adapters: createEmbeddedAdapters(DEFAULT_ADAPTER_ROOT), ...(sessions === undefined ? {} : { sessions }), }); } /** - * What this host built, if it built anything. + * What this host carries and what it built, stated to the provider. + * + * The adapters are first, and unconditional: an `xmd run` or an `xmd plan` that + * asked for Codex or Claude and got ACPX's own registry would run whatever + * `npx` resolved from that build's pins — an adapter that names no turn, or one + * carrying an agent release this machine does not have (#672). * - * Each piece reaches the provider directly rather than through a context: who - * owns a session and which build it belongs to are security decisions, and ones - * a document could replace are not ones. The two advertised sets are stated by - * the host, not inherited. + * Each of the rest reaches the provider directly rather than through a context: + * who owns a session and which build it belongs to are security decisions, and + * ones a document could replace are not ones. The two advertised sets are stated + * by the host, not inherited. */ -export function hostAcpDependencies( - sessions: MachineSessionAssembly | undefined, -): AcpxProviderDependencies { +export function hostAcpDependencies(stack: AgentStack): AcpxProviderDependencies { + const { sessions } = stack; + const adapters = embeddedAdapterDependencies(stack.adapters); if (sessions === undefined) { - return {}; + return adapters; } return { + ...adapters, ...(sessions.coordinator ? { coordinator: sessions.coordinator } : {}), ...(sessions.routeStore ? { routeStore: sessions.routeStore } : {}), ...(sessions.executableObserver ? { executableObserver: sessions.executableObserver } : {}), @@ -99,10 +133,12 @@ export function hostAcpDependencies( * components with the resolved root provider, the permission mode, and the * terminal this command has to give away. * - * Nothing starts an agent — the provider validates availability on first use. + * Nothing starts an agent — the provider validates availability on first use, + * and an embedded adapter reaches the disk at that same point. A document that + * asks for no agent installs no adapter. */ export function* installRunAgentStack(stack: AgentStack): Operation { - const acpx = createAcpxProvider(hostAcpDependencies(stack.sessions)); + const acpx = createAcpxProvider(hostAcpDependencies(stack)); yield* registerAgentProvider("acpx", acpx); // The trusted host selects its own root provider by name. Document-level diff --git a/packages/cli/src/authorship-profile.ts b/packages/cli/src/authorship-profile.ts index 7ff44649..1fe2266d 100644 --- a/packages/cli/src/authorship-profile.ts +++ b/packages/cli/src/authorship-profile.ts @@ -256,10 +256,22 @@ function validator(profile: AuthorshipProfile): IdentityComponent { * The host's own assembly is passed through, then overridden: a coordinator or * a route store says who owns a session, which this profile still has to respect, * while nothing a caller wrote may reach the four fields below. + * + * That assembly is also where this build's own ACP adapters enter, so the + * assistant that writes a Plan is launched from the same snapshot as the run of + * the approved Plan. A ceiling built without them resolved Codex and Claude + * through ACPX's published pins and could reach neither (#672). + * + * Exported for the suite that pins exactly that: what a provider is built from + * is not observable through a provider, and a case that could only watch a turn + * fail would be reading a live agent's machine rather than this host's decision. */ -function authorshipCeiling(profile: AuthorshipProfile, workdir: string): AcpxProviderDependencies { +export function authorshipCeiling( + profile: AuthorshipProfile, + workdir: string, +): AcpxProviderDependencies { return { - ...hostAcpDependencies(profile.stack.sessions), + ...hostAcpDependencies(profile.stack), ...profile.acp, // deno-lint-ignore require-yield *agentCwd() { diff --git a/packages/cli/src/workflow-agent.ts b/packages/cli/src/workflow-agent.ts index f638e1bf..0df4e77e 100644 --- a/packages/cli/src/workflow-agent.ts +++ b/packages/cli/src/workflow-agent.ts @@ -93,7 +93,7 @@ import type { import { carriesEmbeddedAdapter, createEmbeddedAdapters, - overlaidAdapterRegistry, + embeddedAdapterDependencies, } from "@executablemd/acp/embedded-adapters"; import type { EmbeddedAdapters } from "@executablemd/acp/embedded-adapters"; import { @@ -421,25 +421,11 @@ export function* useWorkflowAgentProfile(options: WorkflowAgentProfileOptions): const factory: AgentProviderFactory = createAcpxProvider({ sessionStore: store, - // ACPX's own registry with this build's two patched snapshots over the top. - // Codex and Claude resolve to the adapter that names its turns; every other - // agent resolves to the command it always did. - agentRegistry: overlaidAdapterRegistry(adapters), - // At the first point the provider would run that command, which is its - // availability probe — earlier than a `` placement, and earlier - // than any turn. A snapshot that cannot prove itself refuses the agent here - // rather than surfacing later as an adapter that would not start. - // - // Asked only about an agent this build actually carries. An agent ACPX - // resolves is already a command on this machine, so there is nothing to put - // on disk for it, and reaching into the snapshots to find that out would - // make every run pay for a mechanism that has nothing to say about it. - *prepareAgent(agentName): Operation { - if (!carriesEmbeddedAdapter(adapters, agentName)) { - return; - } - yield* adapters.materialize(agentName); - }, + // ACPX's own registry with this build's two patched snapshots over the top, + // and the preparation that puts one on disk. Codex and Claude resolve to the + // adapter that names its turns; every other agent resolves to the command it + // always did. + ...embeddedAdapterDependencies(adapters), ...(options.createRuntime === undefined ? {} : { createRuntime: options.createRuntime }), // ACP-only, stated rather than inherited. A workflow session belongs to a // run, not to this machine: it is named by a row in the run's own database, diff --git a/packages/cli/tests/agent-adapters.test.ts b/packages/cli/tests/agent-adapters.test.ts new file mode 100644 index 00000000..861d83a7 --- /dev/null +++ b/packages/cli/tests/agent-adapters.test.ts @@ -0,0 +1,166 @@ +/** + * Tier AE — embedded adapters on the run and plan paths + * (specs/acp-client-spec.md §Command-line configuration, §The `xmd plan` + * authorship profile). + * + * What a provider was built from is not observable through a provider: an agent + * command reaches the disk only when something spawns it, and a case that + * watched a turn fail would be reading whichever Codex or Claude the machine + * running the suite happens to have. So these read the dependencies each path + * hands `createAcpxProvider`, which is the decision this host makes and the one + * that was missing (#672). + * + * Nothing here materializes anything. Every root is a temporary path, and the + * resolutions asked for are settled by the bytes this build carries. + */ + +import { describe, it } from "@executablemd/test-support/bdd"; +import { expect } from "@executablemd/test-support/expect"; +import { exists } from "@effectionx/fs"; +import { randomUUID } from "node:crypto"; +import { tmpdir } from "node:os"; +import { join } from "node:path"; +import { API } from "@executablemd/runtime"; +import { createEmbeddedAdapters } from "@executablemd/acp/embedded-adapters"; +import type { EmbeddedAdapters } from "@executablemd/acp/embedded-adapters"; +import type { Operation } from "effection"; + +import { + DEFAULT_ADAPTER_ROOT, + hostAcpDependencies, + resolveAgentStack, +} from "../src/agent-stack.ts"; +import type { AgentStack } from "../src/agent-stack.ts"; +import { authorshipCeiling } from "../src/authorship-profile.ts"; +import type { AuthorshipProfile, CandidateAssessment } from "../src/authorship-profile.ts"; + +/** The two agents this build carries a patched snapshot for. */ +const EMBEDDED = ["codex", "claude"] as const; + +/** A root under this machine's temporary tree, never written to. */ +function adapterRoot(): string { + return join(tmpdir(), `xmd-adapters-${randomUUID()}`); +} + +function stackWith(adapters: EmbeddedAdapters): AgentStack { + return { provider: "acpx", defaultAgent: "codex", permissionMode: "deny-all", adapters }; +} + +/** The profile `xmd plan` builds its ceiling from, with nothing else supplied. */ +function profileWith(stack: AgentStack): AuthorshipProfile { + return { + request: "write a greeting", + syntax: "", + session: "xmd-plan:case", + explicitSession: false, + root: adapterRoot(), + stack, + // deno-lint-ignore require-yield + *installElicitation(): Operation {}, + // deno-lint-ignore require-yield + *assess(): Operation { + return { valid: true, diagnostics: {} }; + }, + }; +} + +describe("Tier AE — embedded adapters on the run and plan paths", () => { + it("AE1: the run path resolves an embedded agent to this build's own adapter", function* () { + const root = adapterRoot(); + const adapters = createEmbeddedAdapters(root); + const registry = hostAcpDependencies(stackWith(adapters)).agentRegistry; + if (registry === undefined) { + throw new Error("the run path handed its provider no agent registry"); + } + + for (const agent of EMBEDDED) { + const command = registry.resolve(agent); + expect(command).toBe(adapters.command(agent)); + // The failure this replaces: ACPX's own table resolves both of these to + // an `npx` of a published adapter, pinned by the version range that build + // recorded rather than by the snapshot this one carries. + expect(command).not.toContain("npx"); + expect(command).toContain(root); + } + }); + + it("AE2: an agent this build carries no snapshot for resolves as it always did", function* () { + const root = adapterRoot(); + const registry = hostAcpDependencies(stackWith(createEmbeddedAdapters(root))).agentRegistry; + if (registry === undefined) { + throw new Error("the run path handed its provider no agent registry"); + } + + // An overlay, not a replacement: carrying a Codex adapter is no reason to + // stop a run from using Gemini, and ACPX's command for it is still the one + // this host runs. + expect(registry.resolve("gemini")).not.toContain(root); + expect(registry.list()).toContain("gemini"); + for (const agent of EMBEDDED) { + expect(registry.list()).toContain(agent); + } + }); + + it("AE3: the plan path's ceiling hands its provider the same registry", function* () { + const root = adapterRoot(); + const adapters = createEmbeddedAdapters(root); + const stack = stackWith(adapters); + const ceiling = authorshipCeiling(profileWith(stack), join(root, "workdir")); + const registry = ceiling.agentRegistry; + if (registry === undefined) { + throw new Error("the plan path handed its provider no agent registry"); + } + + for (const agent of EMBEDDED) { + expect(registry.resolve(agent)).toBe(adapters.command(agent)); + expect(registry.resolve(agent)).not.toContain("npx"); + } + // Beside the clauses that make it a ceiling, not instead of them. + expect(ceiling.mcpServers).toEqual([]); + expect(ceiling.permissions).toBe("strict"); + expect(ceiling.newSessionOptions?.allowedTools).toEqual([]); + }); + + it("AE4: preparing an agent this build carries nothing for writes nothing", function* () { + const root = adapterRoot(); + const prepare = hostAcpDependencies(stackWith(createEmbeddedAdapters(root))).prepareAgent; + if (prepare === undefined) { + throw new Error("the run path handed its provider no preparation"); + } + + yield* prepare("gemini"); + + // Materialization is for an agent this build carries a snapshot for. Every + // other name is already a command on this machine, and asking about one + // creates no directory to install into. + expect(yield* exists(root)).toBe(false); + }); + + it("AE5: a settled stack carries this host's own adapter root", function* () { + yield* API.Env.around({ + // deno-lint-ignore require-yield + *env(): Operation { + return undefined; + }, + }); + const settled = yield* resolveAgentStack( + { + agentProvider: "acpx", + defaultAgent: undefined, + approveAll: false, + approveReads: false, + denyAll: false, + }, + undefined, + ); + if (!settled.ok) { + throw settled.error; + } + + // The resolution supplies them, so no consumer of the settled answer has a + // registry to fall back to. + for (const agent of EMBEDDED) { + expect(settled.value.adapters.executablePath(agent)).toContain(DEFAULT_ADAPTER_ROOT); + } + }); +}); diff --git a/packages/cli/tests/plan-cli.test.ts b/packages/cli/tests/plan-cli.test.ts index 40ffd25c..7291cecf 100644 --- a/packages/cli/tests/plan-cli.test.ts +++ b/packages/cli/tests/plan-cli.test.ts @@ -34,6 +34,7 @@ import { scanPlanArgs, } from "../src/plan-args.ts"; import { + ADAPTERS, AGENT, createPlanHarness, timesRead, @@ -111,6 +112,7 @@ const STACK: AgentStack = { provider: "acpx", defaultAgent: AGENT, permissionMode: "deny-all", + adapters: ADAPTERS, }; /** diff --git a/packages/cli/tests/plan.test.ts b/packages/cli/tests/plan.test.ts index 12b07f16..1e4a96c7 100644 --- a/packages/cli/tests/plan.test.ts +++ b/packages/cli/tests/plan.test.ts @@ -37,6 +37,7 @@ import { import { scanPlanArgs } from "../src/plan-args.ts"; import type { AgentStack } from "../src/agent-stack.ts"; import { + ADAPTERS, AGENT, createPlanHarness, useAuthorshipRoot, @@ -131,6 +132,7 @@ const STACK: AgentStack = { provider: "acpx", defaultAgent: AGENT, permissionMode: "deny-all", + adapters: ADAPTERS, }; /** diff --git a/packages/cli/tests/support/plan-harness.ts b/packages/cli/tests/support/plan-harness.ts index e6a9416e..b13e5a56 100644 --- a/packages/cli/tests/support/plan-harness.ts +++ b/packages/cli/tests/support/plan-harness.ts @@ -22,6 +22,8 @@ import { randomUUID } from "node:crypto"; import { tmpdir } from "node:os"; import { join } from "node:path"; import { API, useHostFiles } from "@executablemd/runtime"; +import { createEmbeddedAdapters } from "@executablemd/acp/embedded-adapters"; +import type { EmbeddedAdapters } from "@executablemd/acp/embedded-adapters"; import { syntaxCatalog } from "../../src/syntax.ts"; import type { PlanDependencies, PlanExecution } from "../../src/plan.ts"; import { createFakeAcp, makeRegistry, makeStore } from "./fake-acp.ts"; @@ -30,6 +32,18 @@ import type { FakeAcp, FakeStore } from "./fake-acp.ts"; /** The agent every plan case drives, and the command it resolves to. */ export const AGENT = "scripted-agent"; +/** + * The embedded adapters a case's Agent stack carries. + * + * Nothing is ever written beneath this root: materialization happens only for an + * agent this build carries a snapshot for, and every case drives + * {@link AGENT}. Naming a temporary root anyway is what keeps a case that came + * to resolve `codex` from installing an adapter under the developer's own home. + */ +export const ADAPTERS: EmbeddedAdapters = createEmbeddedAdapters( + join(tmpdir(), `xmd-plan-adapters-${randomUUID()}`), +); + /** * One review answer, scripted. * diff --git a/specs/acp-client-spec.md b/specs/acp-client-spec.md index 3d31cea5..ab9e1a47 100644 --- a/specs/acp-client-spec.md +++ b/specs/acp-client-spec.md @@ -531,6 +531,15 @@ The default agent resolves in order, each entry overriding the ones above it: The installed provider belongs to the run's `DocumentExecution` scope and closes during its teardown. +Both commands resolve `codex` and `claude` to the ACP adapter this build carries, +the way the workflow attachment does, and every other agent to the command ACPX +resolves for it. The snapshot is installed under `~/.xmd/adapters/` at the +provider's availability probe — the first point either command would run an agent +— so an invocation that asks for no agent installs nothing, and a second one runs +what the first put there. An embedded agent never falls through to the published +adapter ACPX's own table pins: a snapshot that cannot be verified or materialized +refuses that agent. + ### The `xmd plan` authorship profile `xmd plan` resolves that configuration once and uses it for the plan command @@ -546,9 +555,11 @@ consumers, not the flags that produced it: `DEFAULT_AGENT_NAME` is read once per invocation, and authorship and a run of the Plan cannot reach different conclusions from one command line. -The authorship profile takes the provider name and the default agent from that -answer, and nothing else. Its ceiling is the host's, assembled for that one -document and not readable from the command line: +The authorship profile takes the provider name, the default agent and the +adapters this build carries from that answer, and nothing else — so the +assistant that writes a Plan is launched from the same snapshot as the run of +the approved Plan. Its ceiling is the host's, assembled for that one document +and not readable from the command line: | The profile's provider gets | Stated as | | --- | --- | @@ -556,6 +567,7 @@ document and not readable from the command line: | no MCP servers | `mcpServers: []`, an empty set rather than an omission | | no native tools on a fresh session | `newSessionOptions.allowedTools: []` | | a private refusal of every native permission request | `permissions: "strict"` | +| this build's own ACP adapters | `agentRegistry`, ACPX's table with the embedded snapshots over it, and the `prepareAgent` that installs one | `"strict"` answers the request inside the provider: the request is denied, the turn it belongs to fails, and no public Agent handler is consulted or can From f744b30963b66a5de2b85338d0c2db579db3ecb1 Mon Sep 17 00:00:00 2001 From: Taras Mankovski <74687+taras@users.noreply.github.com> Date: Sun, 30 Aug 2026 22:04:24 -0400 Subject: [PATCH 2/2] =?UTF-8?q?=F0=9F=94=96=20Bump=20workspace=20packages?= =?UTF-8?q?=20to=200.10.1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A patch bump: this release fixes `xmd plan` and `xmd run` resolving Codex and Claude through ACPX's stock adapter pins instead of the adapters this build carries (#672). No surface changes. Stamps every publishable manifest and restamps the matching workspace entries in bun.lock, which the bump task deliberately leaves alone. Only the ten @executablemd members move. Claude-Session: https://claude.ai/code/session_01TNJwcFmnt3kYSn9gGsx9u7 --- bun.lock | 20 ++++++++++---------- packages/acp/deno.json | 2 +- packages/acp/package.json | 2 +- packages/cli/deno.json | 2 +- packages/cli/package.json | 2 +- packages/code-review-agent/deno.json | 2 +- packages/code-review-agent/package.json | 2 +- packages/core/deno.json | 2 +- packages/core/package.json | 2 +- packages/durable-streams/deno.json | 2 +- packages/durable-streams/package.json | 2 +- packages/runtime/deno.json | 2 +- packages/runtime/package.json | 2 +- packages/test-agent/deno.json | 2 +- packages/test-agent/package.json | 2 +- packages/testing/deno.json | 2 +- packages/testing/package.json | 2 +- packages/web/deno.json | 2 +- packages/web/package.json | 2 +- packages/workflow/deno.json | 2 +- packages/workflow/package.json | 2 +- 21 files changed, 30 insertions(+), 30 deletions(-) diff --git a/bun.lock b/bun.lock index 9af80342..dbb61f55 100644 --- a/bun.lock +++ b/bun.lock @@ -51,7 +51,7 @@ }, "packages/acp": { "name": "@executablemd/acp", - "version": "0.10.0", + "version": "0.10.1", "dependencies": { "@executablemd/core": "workspace:*", "@executablemd/runtime": "workspace:*", @@ -61,7 +61,7 @@ }, "packages/cli": { "name": "@executablemd/cli", - "version": "0.10.0", + "version": "0.10.1", "bin": { "xmd": "./src/node.ts", }, @@ -83,11 +83,11 @@ }, "packages/code-review-agent": { "name": "@executablemd/code-review-agent", - "version": "0.10.0", + "version": "0.10.1", }, "packages/core": { "name": "@executablemd/core", - "version": "0.10.0", + "version": "0.10.1", "dependencies": { "@effectionx/context-api": "0.6.0", "@effectionx/converge": "0.1.4", @@ -119,7 +119,7 @@ }, "packages/durable-streams": { "name": "@executablemd/durable-streams", - "version": "0.10.0", + "version": "0.10.1", "dependencies": { "@durable-streams/client": "^0.2.2", "effection": "4.1.0", @@ -127,7 +127,7 @@ }, "packages/runtime": { "name": "@executablemd/runtime", - "version": "0.10.0", + "version": "0.10.1", "dependencies": { "@effectionx/context-api": "0.6.0", "@effectionx/fetch": "0.2.1", @@ -139,7 +139,7 @@ }, "packages/test-agent": { "name": "@executablemd/test-agent", - "version": "0.10.0", + "version": "0.10.1", "dependencies": { "@agentclientprotocol/sdk": "1.3.0", "@effectionx/node": "0.2.4", @@ -169,7 +169,7 @@ }, "packages/testing": { "name": "@executablemd/testing", - "version": "0.10.0", + "version": "0.10.1", "dependencies": { "@effectionx/context-api": "0.6.0", "@effectionx/scope-eval": "0.1.3", @@ -181,7 +181,7 @@ }, "packages/web": { "name": "@executablemd/web", - "version": "0.10.0", + "version": "0.10.1", "dependencies": { "@effectionx/node": "0.2.4", "@executablemd/core": "workspace:*", @@ -208,7 +208,7 @@ }, "packages/workflow": { "name": "@executablemd/workflow", - "version": "0.10.0", + "version": "0.10.1", "dependencies": { "@effectionx/context-api": "0.6.0", "@effectionx/fs": "0.3.0", diff --git a/packages/acp/deno.json b/packages/acp/deno.json index 00d650d5..1828f82e 100644 --- a/packages/acp/deno.json +++ b/packages/acp/deno.json @@ -1,6 +1,6 @@ { "name": "@executablemd/acp", - "version": "0.10.0", + "version": "0.10.1", "license": "MIT", "exports": { ".": "./mod.ts", diff --git a/packages/acp/package.json b/packages/acp/package.json index 5a701ed2..8275e13f 100644 --- a/packages/acp/package.json +++ b/packages/acp/package.json @@ -1,6 +1,6 @@ { "name": "@executablemd/acp", - "version": "0.10.0", + "version": "0.10.1", "description": "ACPX agent provider for executable.md documents: drives coding agents over the Agent Client Protocol.", "type": "module", "exports": { diff --git a/packages/cli/deno.json b/packages/cli/deno.json index b2a8841e..752e6411 100644 --- a/packages/cli/deno.json +++ b/packages/cli/deno.json @@ -1,6 +1,6 @@ { "name": "@executablemd/cli", - "version": "0.10.0", + "version": "0.10.1", "license": "MIT", "exports": "./src/deno.ts", "imports": { diff --git a/packages/cli/package.json b/packages/cli/package.json index 4bb82fdf..178ad072 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,6 +1,6 @@ { "name": "@executablemd/cli", - "version": "0.10.0", + "version": "0.10.1", "description": "The xmd command-line interface for executable.md.", "bin": { "xmd": "./src/node.ts" diff --git a/packages/code-review-agent/deno.json b/packages/code-review-agent/deno.json index d9ebaee5..44509dd7 100644 --- a/packages/code-review-agent/deno.json +++ b/packages/code-review-agent/deno.json @@ -1,6 +1,6 @@ { "name": "@executablemd/code-review-agent", - "version": "0.10.0", + "version": "0.10.1", "exports": { ".": "./mod.ts" } diff --git a/packages/code-review-agent/package.json b/packages/code-review-agent/package.json index afe4bc8a..002061f7 100644 --- a/packages/code-review-agent/package.json +++ b/packages/code-review-agent/package.json @@ -1,6 +1,6 @@ { "name": "@executablemd/code-review-agent", - "version": "0.10.0", + "version": "0.10.1", "description": "Parsers that turn git diff and Oxlint output into typed structures for executable.md reviews.", "type": "module", "exports": "./mod.ts" diff --git a/packages/core/deno.json b/packages/core/deno.json index e60e0c47..808eb8b3 100644 --- a/packages/core/deno.json +++ b/packages/core/deno.json @@ -1,6 +1,6 @@ { "name": "@executablemd/core", - "version": "0.10.0", + "version": "0.10.1", "exports": { ".": "./mod.ts", "./host": "./host.ts" diff --git a/packages/core/package.json b/packages/core/package.json index 6414af7b..6a5bf234 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -1,6 +1,6 @@ { "name": "@executablemd/core", - "version": "0.10.0", + "version": "0.10.1", "description": "Core engine that evaluates executable.md documents.", "type": "module", "exports": { diff --git a/packages/durable-streams/deno.json b/packages/durable-streams/deno.json index d0ca1f56..8aeabd81 100644 --- a/packages/durable-streams/deno.json +++ b/packages/durable-streams/deno.json @@ -1,5 +1,5 @@ { "name": "@executablemd/durable-streams", - "version": "0.10.0", + "version": "0.10.1", "exports": "./mod.ts" } diff --git a/packages/durable-streams/package.json b/packages/durable-streams/package.json index ddfe7b79..c29fdca5 100644 --- a/packages/durable-streams/package.json +++ b/packages/durable-streams/package.json @@ -1,6 +1,6 @@ { "name": "@executablemd/durable-streams", - "version": "0.10.0", + "version": "0.10.1", "description": "Durable, replayable event streams for executable.md.", "type": "module", "exports": "./mod.ts", diff --git a/packages/runtime/deno.json b/packages/runtime/deno.json index f4009dc2..de5c654c 100644 --- a/packages/runtime/deno.json +++ b/packages/runtime/deno.json @@ -1,6 +1,6 @@ { "name": "@executablemd/runtime", - "version": "0.10.0", + "version": "0.10.1", "exports": { ".": "./mod.ts", "./files": "./files.ts", diff --git a/packages/runtime/package.json b/packages/runtime/package.json index 695d72c2..172f88d4 100644 --- a/packages/runtime/package.json +++ b/packages/runtime/package.json @@ -1,6 +1,6 @@ { "name": "@executablemd/runtime", - "version": "0.10.0", + "version": "0.10.1", "description": "Runtime host APIs for executable.md documents.", "type": "module", "exports": { diff --git a/packages/test-agent/deno.json b/packages/test-agent/deno.json index 63982aff..051aa7c1 100644 --- a/packages/test-agent/deno.json +++ b/packages/test-agent/deno.json @@ -1,6 +1,6 @@ { "name": "@executablemd/test-agent", - "version": "0.10.0", + "version": "0.10.1", "exports": { ".": "./mod.ts" }, diff --git a/packages/test-agent/package.json b/packages/test-agent/package.json index 4b1ae856..37e1de79 100644 --- a/packages/test-agent/package.json +++ b/packages/test-agent/package.json @@ -1,6 +1,6 @@ { "name": "@executablemd/test-agent", - "version": "0.10.0", + "version": "0.10.1", "description": "Build reliable ACP integration tests with deterministic, document-driven agent behavior.", "type": "module", "exports": { diff --git a/packages/testing/deno.json b/packages/testing/deno.json index 696b444b..c54e27bc 100644 --- a/packages/testing/deno.json +++ b/packages/testing/deno.json @@ -1,6 +1,6 @@ { "name": "@executablemd/testing", - "version": "0.10.0", + "version": "0.10.1", "exports": { ".": "./mod.ts" } diff --git a/packages/testing/package.json b/packages/testing/package.json index cb98bcbb..4f0d6e34 100644 --- a/packages/testing/package.json +++ b/packages/testing/package.json @@ -1,6 +1,6 @@ { "name": "@executablemd/testing", - "version": "0.10.0", + "version": "0.10.1", "description": "Testing components for executable.md documents: , , and assertion components.", "type": "module", "exports": { diff --git a/packages/web/deno.json b/packages/web/deno.json index 1be09067..4f0b0d22 100644 --- a/packages/web/deno.json +++ b/packages/web/deno.json @@ -1,6 +1,6 @@ { "name": "@executablemd/web", - "version": "0.10.0", + "version": "0.10.1", "exports": "./mod.ts", "compilerOptions": { "lib": ["dom", "dom.iterable", "esnext"] diff --git a/packages/web/package.json b/packages/web/package.json index 5e5ac8d9..1e67cf62 100644 --- a/packages/web/package.json +++ b/packages/web/package.json @@ -1,6 +1,6 @@ { "name": "@executablemd/web", - "version": "0.10.0", + "version": "0.10.1", "description": "Schema-backed local web form input for executable.md documents.", "type": "module", "exports": { diff --git a/packages/workflow/deno.json b/packages/workflow/deno.json index a59bbd04..a5ceb312 100644 --- a/packages/workflow/deno.json +++ b/packages/workflow/deno.json @@ -1,6 +1,6 @@ { "name": "@executablemd/workflow", - "version": "0.10.0", + "version": "0.10.1", "license": "MIT", "exports": { ".": "./mod.ts", diff --git a/packages/workflow/package.json b/packages/workflow/package.json index c2cd8fc6..0120c841 100644 --- a/packages/workflow/package.json +++ b/packages/workflow/package.json @@ -1,6 +1,6 @@ { "name": "@executablemd/workflow", - "version": "0.10.0", + "version": "0.10.1", "description": "Workflow runs for executable.md: one pinned definition per run, retained durably so another process can find it.", "type": "module", "exports": {