Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
15 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ jobs:
--exclude-unused-npm \
--allow-all \
--include packages/code-review-agent \
--include packages/cli/src/documents/prompt-command.md \
--target ${{ matrix.target }} \
--output dist/${{ matrix.artifact }} \
packages/cli/src/compiled.ts
Expand Down
5 changes: 5 additions & 0 deletions architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,10 @@ Existing documents and code get aligned to this section retroactively.
| Repository base | the optional Git revision from which one named Workspace Repository initializes its primary checkout |
| pinned commit | the commit obtained by resolving a base once; it remains the workflow run's starting repository state even as the run creates descendant commits |
| document target | an addressable static heading in a root document's own Markdown flow, named by the canonical path of heading labels that reaches it; selecting one executes the preamble, each ancestor's own content, and that heading's complete subtree |
| Prompt | a person's original request, in ordinary natural language. `xmd prompt` takes exactly one |
| Plan | the executable program produced from a Prompt: an Executable Markdown document combining readable prose that expresses the Prompt's intent with the components that carry it out, each placed beside the prose describing what it does. It begins with one descriptive level-one heading. A Plan is what `xmd prompt` approves and then delivers: printed to stdout by default, written to an `--output` file, and run only under `--run`. It is not a synonym for a workflow, a policy document or any executable Markdown file |
| prompt command document | the one exact checked-in first-party Markdown value root `xmd prompt` executes to convert a Prompt into a Plan. It implements that conversion and its review workflow — the Prompt wording, the draft and repair loops, the `<CheckDraft>` branches, human review, revision, approval, stopping, exhaustion and the final explanation turn — and returns the exact approved Plan source. Every Plan-producing turn in it states the complete Plan requirements for itself, so a replacement may add or correct a title rather than only carry one forward. It is not itself a Plan. Internal: no command-line option selects another one, and no repository component search can answer for it |
| prompt profile | the trusted-host assembly used only for the prompt command document: its fixed inputs, a constrained Agent provider, Elicitation, the fixed first-party components and the host-declared `<CheckDraft>`. It uses no repository component search and exposes no custom root, and the ceiling it establishes is not readable from the command line. Its working directory is one host-owned directory dedicated to the logical prompt session, keyed by the digest of that name, created empty and required to be empty on the way in. An explicitly named session's directory is durable, because continuation derives the same session identity from it; an invocation-unique default session's is scope-owned, claimed before it is created, and exactly one cleanup is attempted after profile teardown and before admission on every ending — the leaf removed non-recursively when it is still the empty directory that was handed over, and left as found with the command failing terminally when it has gained content or vanished. Where those directories live is a host dependency no caller or document selects |
| expansion | one logical evaluation of an authored executable element within a document execution |
| expansion ID | a deterministic identifier for one logical expansion; restoring or retrying that expansion preserves the ID, while a distinct evaluation requested by the document receives another |
| Git capability | the contextual interface through which workflow infrastructure queries the Git repository associated with the current working directory |
Expand Down Expand Up @@ -3541,6 +3545,7 @@ Status is measured against main.
| --- | --- | --- |
| `xmd syntax` | describes every structural construct and every selected component the production `run` profile would let a document write in the contextual working directory, as deterministic Markdown or as version-1 JSON, from one catalog. Inspection only: it registers the run profile's declarations in a bounded scope and reads the filesystem for which files exist and, for a selected Markdown component, that file's frontmatter. It runs no body, imports no repository TypeScript module, installs no provider, mints no authority and writes no journal. An include it cannot enumerate — a selection-relevant symbolic link to a directory beneath it included — fails the whole request rather than printing a healthy subset | built on the #632 stack |
| document validation | validates one supplied root projection and the recursive Markdown source closure normal component selection discovers, returning deterministic version-1 document diagnostics and `valid`, `invalid` or `not-statically-checkable` invocation outcomes without evaluating document code or installing operational host behavior | built on the #654 stack |
| `xmd prompt` | turns one Prompt into a Plan and delivers it, by executing one root document — and, only under `--run`, a second — with a complete scope boundary between them. First the packaged prompt command document, under the internal `<prompt-command>` identity, inside the prompt profile: one enclosing Session, a host ceiling of one host-owned directory dedicated to that logical session — keyed by the digest of the name, never the name, created empty and required to be empty before the provider exists or a session is materialized, refused rather than cleaned when it is not, durable when the caller named the session and handed back non-recursively after teardown when it did not — with no additional directories, no MCP servers, no native tools and a private strict denial no permission flag widens, no Files, command, service or network capability for that document, no repository component search, and one host-declared `<CheckDraft>` whose closed assessment answers `valid: false` for a defect the draft authored and raises for a defect the command line authored. Its instructions require every Plan to begin with one descriptive level-one title and to keep the Prompt's outcomes as readable steps with each component beside the step it performs, through repairs and revisions alike; that is an authorship and human-review requirement, and `<CheckDraft>` never enforces it. A tenth draft that still has problems may be stopped or explained: the explanation is one more ordinary turn in the same Session carrying only the final diagnostics, is inert text, reopens no draft limit, and ends the command. The host's instruction layer states only that an answer belongs to the message that asked for it, so which shape a turn wants stays in the document. Authorship sits outside durability: it runs on an invocation-owned in-memory stream that is never journaled, persisted, reused or replayed. Then, only after that execution and every provider, Prompt task and Elicitation resource inside it has torn down, the host validates the returned Plan again, resolves props for exactly those bytes, and then delivers the approved Plan where the caller asked: to stdout byte for byte by default, to an exclusively created `--output` path, and — only under `--run` — through the ordinary supplied-source path under the `<prompt>` identity, one ordinary document with its own Agent provider, its own journal and ordinary run output, result and failure behavior. A journal exists only when `--run` begins, and the flags that configure only a run are refused in preflight without it | built on the #659 stack |
| `<PrintErrors>` / `printErrors(fn)` | prints failures | built on main |
| `<Fail message>` | stops authored work with the sentence its author wrote, raised where it is written. An ordinary overridable core default — never structural, never reserved, so a repository `Fail.md` is chosen ahead of it — with a closed schema of one required non-empty `message` and **self-closing only**: a paired spelling never enters its body, and `as` is refused by the body itself because there is nothing to bind. Every refusal reports the invocation and happens before the authored message, so a document that never reached its decision is never reported as having made one. A valid invocation is the ordinary failure of a function component: an `Error` carrying the exact authored message, positioned at the opening tag, rendering nothing and binding nothing. It carries no `printErrors()` declaration, which is what leaves recovery to an authored `<PrintErrors>` region under ordinary text-root modes; a value body's `throw` is not replaced there, so the authored failure settles the body ahead of missing-`<Return>` settlement. No authority, context, provider, resource, module state or durable operation of its own: replay of a completed root restores the recorded outcome without re-expanding the body | built on the #659 stack |
| `<Let as="name">` | binds one name in the current environment from exactly one source: the content it renders, or the exact value `value` names, bound by reference and never through the JSON boundary component props cross — the scanner resolves no JSON for that one prop, and expansion projects none. Which source it has is read from what the author wrote, before either one runs, so a construct naming both expands no child and evaluates no expression. It opens no scope, owns no resource, adds no middleware boundary and writes no journal record — replay reconstructs both sources through ordinary expansion | built on the #527 stack |
Expand Down
2 changes: 1 addition & 1 deletion deno.json
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@
"verify:clean": "deno run --allow-all --node-modules-dir=none --cached-only --frozen scripts/verify-clean.ts",
"deps": "deno run --allow-all scripts/deps.ts",
"deps:target": "deno run --allow-all scripts/deps-target.ts",
"build": "deno task build:web && deno compile --node-modules-dir=none --cached-only --frozen --exclude-unused-npm --allow-all --include packages/code-review-agent --output dist/xmd packages/cli/src/compiled.ts",
"build": "deno task build:web && deno compile --node-modules-dir=none --cached-only --frozen --exclude-unused-npm --allow-all --include packages/code-review-agent --include packages/cli/src/documents/prompt-command.md --output dist/xmd packages/cli/src/compiled.ts",
"build:web": "deno run --allow-all --node-modules-dir=none --cached-only --frozen scripts/preflight.ts scripts/build-web-client.ts",
"gen:publish-workflow": "deno run --allow-all packages/cli/src/deno.ts run scripts/gen-publish-workflow.md",
"bump": "deno run -A scripts/bump-version.ts",
Expand Down
124 changes: 124 additions & 0 deletions packages/cli/src/agent-stack.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
/**
* The one Agent configuration an invocation resolves, and the two things a
* host does with it (specs/acp-client-spec.md §Command-line configuration).
*
* `xmd run` and `xmd prompt` take the same Agent, permission and provider
* options, so they resolve them once, here, rather than each reading the flags
* again. What they do with the result differs, and deliberately: a run installs
* the registered provider into the Agent Api so a document may reach it, while
* `xmd prompt` hands the same answer to two consumers — the prompt profile, which
* takes the provider name and the default agent and nothing else, and the
* approved document, which runs the ordinary run stack.
*
* `agent-config.ts` stays the pure flag-to-permission mapping. This module is
* where the environment, the provider registry and the host's own machine
* session assembly enter.
*/

import {
installAgentComponents,
installPermissionMode,
registerAgentProvider,
} from "@executablemd/core";
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";
import { Err, Ok } from "effection";
import type { Operation, Result } from "effection";

import { resolveAgentConfig } from "./agent-config.ts";
import type { AgentFlags } from "./agent-config.ts";
import type { MachineSessionAssembly } from "./session-coordinator.ts";

/** Everything one invocation settled about agents, resolved exactly once. */
export interface AgentStack {
/** The provider name the caller selected, already known to be registered. */
provider: string;
/** The agent every consumer defaults to, environment fallback applied. */
defaultAgent: string;
permissionMode: PermissionMode;
/** What this host states about machine-wide agent sessions, if anything. */
sessions?: MachineSessionAssembly;
}

/**
* Read the command line, the environment and the host's assembly into one
* configuration.
*
* A failure comes back as a `Result` rather than as a printed line and an exit,
* so the same resolution serves a command that runs a document and one that
* generates one first.
*/
export function* resolveAgentStack(
flags: AgentFlags,
sessions: MachineSessionAssembly | undefined,
): Operation<Result<AgentStack>> {
const config = resolveAgentConfig(flags);
if ("error" in config) {
return Err(new Error(config.error));
}
if (flags.agentProvider !== "acpx") {
return Err(new Error(`Unknown agent provider "${flags.agentProvider}"`));
}
const defaultAgent =
config.defaultAgent ?? (yield* readEnv("DEFAULT_AGENT_NAME")) ?? DEFAULT_AGENT_NAME;
return Ok({
provider: flags.agentProvider,
defaultAgent,
permissionMode: config.permissionMode,
...(sessions === undefined ? {} : { sessions }),
});
}

/**
* What this host built, if it built anything.
*
* 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.
*/
export function hostAcpDependencies(
sessions: MachineSessionAssembly | undefined,
): AcpxProviderDependencies {
if (sessions === undefined) {
return {};
}
return {
...(sessions.coordinator ? { coordinator: sessions.coordinator } : {}),
...(sessions.routeStore ? { routeStore: sessions.routeStore } : {}),
...(sessions.executableObserver ? { executableObserver: sessions.executableObserver } : {}),
advertiseNativeLaunch: sessions.advertiseNativeLaunch,
advertiseClientNativeAttachment: sessions.advertiseClientNativeAttachment,
};
}

/**
* Install the agent stack a document runs under: the registration, the
* 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.
*/
export function* installRunAgentStack(stack: AgentStack): Operation<void> {
const acpx = createAcpxProvider(hostAcpDependencies(stack.sessions));
yield* registerAgentProvider("acpx", acpx);

// The trusted host selects its own root provider by name. Document-level
// selection goes through the installation protocol; this is the host saying
// what it configured, which no document is composing around.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Redundant comment — restates what the code does.

Suggested change
// what it configured, which no document is composing around.

const providers: Record<string, AgentProviderFactory> = { acpx };
const factory = providers[stack.provider];
const { defaultAgent, permissionMode } = stack;
yield* installAgentComponents({
defaultAgent,
permissionMode,
rootProvider: { factory, options: { defaultAgent, permissionMode } },
});
yield* installPermissionMode(permissionMode);
// `xmd run` is the one command that has a terminal to give away. Help,
// document inspection and `xmd test` install no launcher, so a document that
// reaches <Session.Launch> under any of them refuses instead of spawning.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Redundant comment — restates what the code does.

Suggested change
// reaches <Session.Launch> under any of them refuses instead of spawning.

yield* installForegroundLauncher();
}
Loading
Loading