Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
40 changes: 30 additions & 10 deletions architecture.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -2847,15 +2847,35 @@ request to act. This preserves provider composition without letting a document
or replacement context mint terminal ownership.

A pane claim grants one interactive terminal at that ordinal, not an Agent
session. Core installs a pane-scoped native launcher that closes over the claim.
`<Session.Launch>` in that pane consequently reserves, flushes, and launches on
the pane terminal instead of competing for the root lease. Launches in
different panes may run concurrently; two interactive launches in one pane
cannot. Sequential launches in one paired pane remain ordinary composition.
The session coordinator is unchanged and independently authoritative, so two
panes attempting to own the same logical Agent session still contend and one
is refused. The provider starts a self-closing pane's host-configured default
shell under the same kind of pane claim.
session. Core installs a pane-scoped native launcher that closes over the claim,
the composite, and the authored ordinal. `<Session.Launch>` in that pane
consequently reserves and flushes the pane, then terminates native-launch
routing at this required provider-neutral composite operation:

```ts
launch(
ordinal: number,
request: NativeLaunchRequest,
spawned: () => void,
): Operation<NativeLaunchOutcome>;
```

The ordinal exists only in core's live closure and never enters the native or
Agent request. Once nearer native-launch middleware delegates, the pane launcher
calls the composite operation instead of the root foreground launcher. Nearer
middleware may still observe, wrap, refuse, or short-circuit the request. A
composite that cannot execute the pane request refuses explicitly; falling
through to the root would put the child on the wrong physical terminal. Root
`<Session.Launch>` retains its existing foreground-launch route unchanged.

Launches in different panes may run concurrently; two interactive launches in
one pane cannot. Sequential launches in one paired pane remain ordinary
composition. The session coordinator is unchanged and independently
authoritative, so two panes attempting to own the same logical Agent session
still contend and one is refused. The composite's separate `shell()` operation
starts a self-closing pane's host-configured default shell under the same kind
of pane claim; it remains separate because its executable is live host policy,
not an authored or Agent-provided native launch request.

Each claim also closes over one host-owned readiness latch. The pane-scoped
native launcher acknowledges it from the runtime's successful child-spawn event
Expand DownExpand Up@@ -4137,7 +4157,7 @@ Status is measured against main.
| testing harness (`<Execution>`) | runs another document as a real root under a production host profile, authorized by canonical `<Test>` alone: declarations installed before the root import, child output displayed progressively and collected only when asked, journal retention selected independently of observation, and the outcome published by the invocation's own terminal through a request public middleware composes around but cannot answer | built on the #454 stack for `host="run"`; the workflow profile and `<WorkflowRun>` are unbuilt, and a host that offers no workflow profile refuses them |
| nested run-profile Agent and elicitation declarations | lets one `<Execution host="run">` declare one child-scoped `<TestAgent>` scenario set and one non-delegating `<Answers>` matcher set; only frozen test data crosses the harness request, the trusted host constructs both providers inside the isolated child, siblings share no session or provider state, ordinary component shadowing remains in force, and the child journal retains only the selected Prompt and Elicit components' ordinary results | built on the #641 stack |
| `Config` run deadline / exec default / Fetch default / verbosity | three independently owned contextual timeouts, absent unless configured, each read by exactly one consumer, and contextual verbosity — a boolean that is false unless configured, seeded by the command line and overridable for a lexical subtree, bounding nothing and owning no authority | built on this stack |
| terminal grid (`<Terminal.Grid>` / `<Terminal>`) | replaces the root foreground terminal with one provider-neutral composite whose statically declared direct panes begin concurrently, stay independently interactive, preserve their final statuses until the reader closes the composite, and tear down completely before document execution continues. A paired pane expands isolated document flow; a self-closing pane runs the host's default shell. The grid owns one foreground-terminal lease, each pane owns a separate pane-terminal lease, and a pane-scoped native launcher lets `<Session.Launch>` use that pane without weakening the independent Agent session coordinator. Core validates the complete row-major layout before provider contact, attaches only after every pane is ready, contains post-attach pane failures until close, and records the ordered provider-neutral outcomes. Completed replay contacts no terminal or Agent provider; partial replay rebuilds a fresh composite, restores completed panes as statuses, and continues incomplete pane effects under their existing durable identities. Provider commands, sockets, process topology and layout identifiers remain live-only inside the provider closure | defined for #717; #726 proves the persistent tmux pane-worker topology and its observable teardown boundary on macOS; first production provider is tmux in the Deno and compiled foreground hosts; controlled non-tmux provider proves the core contract; implementation unbuilt |
| terminal grid (`<Terminal.Grid>` / `<Terminal>`) | replaces the root foreground terminal with one provider-neutral composite whose statically declared direct panes begin concurrently, stay independently interactive, preserve their final statuses until the reader closes the composite, and tear down completely before document execution continues. A paired pane expands isolated document flow; a self-closing pane runs the host's default shell. The grid owns one foreground-terminal lease, each pane owns a separate pane-terminal lease, and a pane-scoped native launcher lets `<Session.Launch>` use that pane without weakening the independent Agent session coordinator. The launcher terminates at the composite's required provider-neutral pane-execution operation; the authored ordinal stays in core's live closure, and the native request carries no pane identity. Core validates the complete row-major layout before provider contact, attaches only after every pane is ready, contains post-attach pane failures until close, and records the ordered provider-neutral outcomes. Completed replay contacts no terminal or Agent provider; partial replay rebuilds a fresh composite, restores completed panes as statuses, and continues incomplete pane effects under their existing durable identities. Provider commands, sockets, process topology and layout identifiers remain live-only inside the provider closure | defined for #717; #726 proves the persistent tmux pane-worker topology and its observable teardown boundary on macOS; structure and layout built in #729, provider-neutral execution and durability in #730, pane claim admission and native-launch middleware in #731; the required composite pane-execution endpoint is specified and implemented in #732, which is what gives a pane's `<Session.Launch>` that pane's terminal rather than the root's; the controlled non-tmux provider remains the authority for core lifecycle semantics; the tmux provider is built in #732 for the Deno and compiled foreground hosts — one invocation-private server per grid, authenticated persistent pane workers carrying exact argv, cwd and environment outside tmux parsing, explicit row-major layout imposed by pane swaps, a required composite `launch()` that gives a pane's `<Session.Launch>` its own terminal rather than the root's, and one ordered teardown that proves worker quiescence, channel closure and server disappearance before the document continues; its evidence uses a fake tmux with real workers and real sockets, and real tmux behaviour on macOS remains #726's; Node and Bun catalog and validate the same grids and install neither the provider nor the process observer, refusing before pane start |
| native session launch (`<Session.Launch>` / `launchAgentSession()`) | prepares one durable coding-agent session from the rendered body of `<Session.Launch>` and hands the provider's native UI the terminal for that exact session, then continues the document after it exits. The body renders completely first and only what it rendered crosses as the instruction layer; the launch performs no model turn; at the root it takes the run's foreground-terminal lease before an agent is resolved, while a launch inside `<Terminal>` takes that pane's lease through its pane-scoped native launcher. A host with no applicable terminal refuses without probing for an installed CLI. A session is constructed once, by one of two mechanisms, and its create-once construction route says which. Where the provider returns the identity, the ACPX provider creates the session, installs the layer at creation, releases ACP ownership before the spawn, and marks its handle stale so a later `<Prompt>` reattaches. Where the adapter names its own sessions, it allocates the identity inside ownership before any process exists, the native process creates the session under that name from a private mode-0600 instruction file, and ACP creates nothing — the instruction text reaches neither argv nor environment, and the file is removed on success, failure and cancellation alike while ownership is still held. Neither route converts into the other, and which one governs is chosen by the first operation that consumes the placement rather than by the `<Session>` that made it: a fresh `<Session>` publishes no route and establishes nothing, so a `<Session.Launch>` nested inside one constructs the session it placed, while a first subscribed `<Prompt>` publishes ACP-first before it ensures and keeps that account even if the turn that follows is never accepted. An established route is validated eagerly by a later `<Session>`, and a launch meeting a published ACP-first route refuses before an identity exists. A `<Session>` or `<Prompt>` meeting a bound client-allocated route attaches under the route's exact identity; a legacy unbound route or an unavailable attachment capability refuses before a turn and creates no substitute conversation. Phases are retained as `agent_session_launch` records under one expansion identity — `prepared` before ownership is released, then `detached`, then `exited` — so a completed replay launches nothing, a replay holding only `prepared` proves the handoff never began and may still create under the retained identity, and one holding `detached` resumes and never falls back. The public route carries an opaque one-use launch request and answers nothing; authority to run and retain a phase is delivered to the installed provider directly, so neither a returned completion nor a rebuilt request authors a launch. Every operation that can act on an advertised session takes exclusive ownership under one natural key first, through a coordinator the host built and passed in; contention refuses instead of queueing, and an owner that never proved it stopped leaves a recovery tombstone. A host that cannot say who owns a session refuses every advertised operation, and one that cannot say how a session was constructed additionally refuses an agent that names its own — before any provider effect. Every private setup or child-creation failure is normalized to `process-creation-failed` with fixed provider-owned text, carrying no path, argv, environment or host message. No launch path discards persistent provider state. A client-allocated session is bound to one executable build: the build is observed inside ownership before an identity is allocated, the binding is published with the V2 route and retained beside the prepared record, the native child runs the exact observed path in place of the launcher name, and every later create, resume, attachment and incomplete replay reobserves and compares before a process, an ensure or a turn. A `<Session>` or `<Prompt>` meeting a bound client-native route attaches to it: it reobserves the build, requires any retained provider arrangement to assert that same conversation, calls ensure with the route identity as `resumeSessionId`, and requires the provider to report that identity before a turn — refusing on missing capability, build drift, missing history or a differing assertion without creating a substitute conversation. ACP runtimes are partitioned by resolved agent command and binding, each handle is closed by the partition that created it, and a bound partition is torn down when its last handle closes. A legacy V1 client-native route keeps exactly the released native-only behavior and never attaches | built on the #517 stack, extended by the #519 and #561 stacks; Deno and the compiled binary assemble the host — coordinator, route store and executable observer — and Node and Bun keep the same advertised names while assembling none of it, so every advertised operation refuses before provider work; `claude` is advertised for native launch after passing the client-allocated gate at Claude Code 2.1.241 on macOS arm64 (#520) and separately for client-native attachment after passing the native-to-ACP marker gate (#561), and Codex remains unadvertised because nothing has run its provider-returned claims against an installed Codex; `Agent.AddDir` is unbuilt |
| `<Fetch>` | performs one XMD-mediated HTTP read through contextual `API.Fetch`, admitting the whole request before transport, and retains the normalized request and the detached response as one `fetch` durable observation; capture decides whether a status is data or a failure, and the trusted host's destination ceiling sits below the component | built on the #456 stack; a generated fragment may name the pinned identity only for a request the trusted host stated exactly, on the #369 stack |
| `API.Files` | routes every document filesystem operation to the installed provider, with no host default and structural failure data. Its mandatory semantic operations include `ensureDirectory`, which recursively creates or adopts one directory and returns Unit; separately loaded copies compose through the stable Api name | built on the #227 stack; directory ensure added by #643 |
Expand Down
16 changes: 16 additions & 0 deletions packages/cli/src/agent-stack.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -22,6 +22,8 @@ import {
} from "@executablemd/core";
import type { AgentProviderFactory, PermissionMode } from "@executablemd/core";
import { installForegroundLauncher, env as readEnv } from "@executablemd/runtime";
import { unsupportedTerminalGrid } from "./terminal/host.ts";
import type { TerminalGridInstaller } from "./terminal/host.ts";
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
Expand DownExpand Up@@ -67,6 +69,14 @@ export interface AgentStack {
adapters: EmbeddedAdapters;
/** What this host states about machine-wide agent sessions, if anything. */
sessions?: MachineSessionAssembly;
/**
* What presents this host's terminal grids.
*
* Deno and the compiled binary supply the tmux provider; Node and Bun supply
* the one that installs none, so those runtimes describe and validate the
* same grids and open none of them.
*/
installTerminalGrid?: TerminalGridInstaller;
}

/**
Expand All@@ -80,6 +90,7 @@ export interface AgentStack {
export function* resolveAgentStack(
flags: AgentFlags,
sessions: MachineSessionAssembly | undefined,
installTerminalGrid?: TerminalGridInstaller,
): Operation<Result<AgentStack>> {
const config = resolveAgentConfig(flags);
if ("error" in config) {
Expand All@@ -96,6 +107,7 @@ export function* resolveAgentStack(
permissionMode: config.permissionMode,
adapters: createEmbeddedAdapters(DEFAULT_ADAPTER_ROOT),
...(sessions === undefined ? {} : { sessions }),
...(installTerminalGrid === undefined ? {} : { installTerminalGrid }),
});
}

Expand DownExpand Up@@ -157,4 +169,8 @@ export function* installRunAgentStack(stack: AgentStack): Operation<void> {
// document inspection and `xmd test` install no launcher, so a document that
// reaches <Session.Launch> under any of them refuses instead of spawning.
yield* installForegroundLauncher();
// And whatever presents this host's terminal grids, which on a host that
// presents none still opens the installation so a grid is validated — the
// refusal a document meets there is core's own.
yield* (stack.installTerminalGrid ?? unsupportedTerminalGrid)();
}
14 changes: 13 additions & 1 deletion packages/cli/src/cli.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -95,6 +95,8 @@ import { installWebComponents, installWebElicitation } from "@executablemd/web";
import { timebox } from "@effectionx/timebox";
import { timeout as runTimeout } from "@executablemd/runtime";
import { installRunAgentStack, resolveAgentStack } from "./agent-stack.ts";
import { unsupportedTerminalGrid } from "./terminal/host.ts";
import type { TerminalGridInstaller } from "./terminal/host.ts";
import { planComponentDeclaration } from "./plan-component.ts";
import { VERBOSE_REGISTRATION } from "./verbose-component.ts";
import type { AgentStack } from "./agent-stack.ts";
Expand DownExpand Up@@ -725,8 +727,9 @@ function* underRunDeadline(timeouts: RunTimeouts, body: () => Operation<void>):
function* settleAgentStack(
flags: AgentFlags,
sessions: MachineSessionAssembly | undefined,
installTerminalGrid: TerminalGridInstaller,
): Operation<AgentStack | undefined> {
const stack = yield* resolveAgentStack(flags, sessions);
const stack = yield* resolveAgentStack(flags, sessions, installTerminalGrid);
if (!stack.ok) {
console.error(stack.error.message);
yield* exit(1);
Expand DownExpand Up@@ -2155,6 +2158,7 @@ function* dispatch(
installRepositories: RepositoryInstaller,
workflowHost: WorkflowHost | undefined,
sessions: MachineSessionAssembly | undefined,
installTerminalGrid: TerminalGridInstaller,
): Operation<void> {
const propsPhase = yield* preparePropsPhase(helpRequest.args, evalFlags);

Expand DownExpand Up@@ -2229,6 +2233,7 @@ function* dispatch(
denyAll: config.denyAll,
},
sessions,
installTerminalGrid,
);
if (runStack === undefined) {
break;
Expand DownExpand Up@@ -2290,6 +2295,7 @@ function* dispatch(
denyAll: config.denyAll,
},
sessions,
installTerminalGrid,
);
if (planStack === undefined) {
break;
Expand DownExpand Up@@ -2565,6 +2571,10 @@ export function* runXmd(
// owns the session or which build it belongs to. A caller that names none
// gets no machine sessions at all, which is the ordinary ACP behaviour.
sessions?: MachineSessionAssembly,
// What presents a terminal grid on this host. Deno and the compiled binary
// supply the tmux provider; Node and Bun supply the one that installs none,
// so those runtimes describe and validate the same grids and open none.
installTerminalGrid: TerminalGridInstaller = unsupportedTerminalGrid,
): Operation<void> {
// Before every scanner, before command selection, and before anything reads a
// path. `prompt` names no command, and a first token that names none is a
Expand DownExpand Up@@ -2632,6 +2642,7 @@ export function* runXmd(
installRepositories,
workflowHost,
sessions,
installTerminalGrid,
);
}

Expand All@@ -2651,6 +2662,7 @@ export function* runXmd(
installRepositories,
workflowHost,
sessions,
installTerminalGrid,
),
);
}
Loading
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all \u003cpre\u003e\u003ccode\u003e blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks"); } } catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); } })(); (function(){ try { var __m = "github.com"; var __re = new RegExp('^' + "github\\.com" + '
Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
40 changes: 30 additions & 10 deletions architecture.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -2847,15 +2847,35 @@ request to act. This preserves provider composition without letting a document
or replacement context mint terminal ownership.

A pane claim grants one interactive terminal at that ordinal, not an Agent
session. Core installs a pane-scoped native launcher that closes over the claim.
`<Session.Launch>` in that pane consequently reserves, flushes, and launches on
the pane terminal instead of competing for the root lease. Launches in
different panes may run concurrently; two interactive launches in one pane
cannot. Sequential launches in one paired pane remain ordinary composition.
The session coordinator is unchanged and independently authoritative, so two
panes attempting to own the same logical Agent session still contend and one
is refused. The provider starts a self-closing pane's host-configured default
shell under the same kind of pane claim.
session. Core installs a pane-scoped native launcher that closes over the claim,
the composite, and the authored ordinal. `<Session.Launch>` in that pane
consequently reserves and flushes the pane, then terminates native-launch
routing at this required provider-neutral composite operation:

```ts
launch(
ordinal: number,
request: NativeLaunchRequest,
spawned: () => void,
): Operation<NativeLaunchOutcome>;
```

The ordinal exists only in core's live closure and never enters the native or
Agent request. Once nearer native-launch middleware delegates, the pane launcher
calls the composite operation instead of the root foreground launcher. Nearer
middleware may still observe, wrap, refuse, or short-circuit the request. A
composite that cannot execute the pane request refuses explicitly; falling
through to the root would put the child on the wrong physical terminal. Root
`<Session.Launch>` retains its existing foreground-launch route unchanged.

Launches in different panes may run concurrently; two interactive launches in
one pane cannot. Sequential launches in one paired pane remain ordinary
composition. The session coordinator is unchanged and independently
authoritative, so two panes attempting to own the same logical Agent session
still contend and one is refused. The composite's separate `shell()` operation
starts a self-closing pane's host-configured default shell under the same kind
of pane claim; it remains separate because its executable is live host policy,
not an authored or Agent-provided native launch request.

Each claim also closes over one host-owned readiness latch. The pane-scoped
native launcher acknowledges it from the runtime's successful child-spawn event
Expand DownExpand Up@@ -4137,7 +4157,7 @@ Status is measured against main.
| testing harness (`<Execution>`) | runs another document as a real root under a production host profile, authorized by canonical `<Test>` alone: declarations installed before the root import, child output displayed progressively and collected only when asked, journal retention selected independently of observation, and the outcome published by the invocation's own terminal through a request public middleware composes around but cannot answer | built on the #454 stack for `host="run"`; the workflow profile and `<WorkflowRun>` are unbuilt, and a host that offers no workflow profile refuses them |
| nested run-profile Agent and elicitation declarations | lets one `<Execution host="run">` declare one child-scoped `<TestAgent>` scenario set and one non-delegating `<Answers>` matcher set; only frozen test data crosses the harness request, the trusted host constructs both providers inside the isolated child, siblings share no session or provider state, ordinary component shadowing remains in force, and the child journal retains only the selected Prompt and Elicit components' ordinary results | built on the #641 stack |
| `Config` run deadline / exec default / Fetch default / verbosity | three independently owned contextual timeouts, absent unless configured, each read by exactly one consumer, and contextual verbosity — a boolean that is false unless configured, seeded by the command line and overridable for a lexical subtree, bounding nothing and owning no authority | built on this stack |
| terminal grid (`<Terminal.Grid>` / `<Terminal>`) | replaces the root foreground terminal with one provider-neutral composite whose statically declared direct panes begin concurrently, stay independently interactive, preserve their final statuses until the reader closes the composite, and tear down completely before document execution continues. A paired pane expands isolated document flow; a self-closing pane runs the host's default shell. The grid owns one foreground-terminal lease, each pane owns a separate pane-terminal lease, and a pane-scoped native launcher lets `<Session.Launch>` use that pane without weakening the independent Agent session coordinator. Core validates the complete row-major layout before provider contact, attaches only after every pane is ready, contains post-attach pane failures until close, and records the ordered provider-neutral outcomes. Completed replay contacts no terminal or Agent provider; partial replay rebuilds a fresh composite, restores completed panes as statuses, and continues incomplete pane effects under their existing durable identities. Provider commands, sockets, process topology and layout identifiers remain live-only inside the provider closure | defined for #717; #726 proves the persistent tmux pane-worker topology and its observable teardown boundary on macOS; first production provider is tmux in the Deno and compiled foreground hosts; controlled non-tmux provider proves the core contract; implementation unbuilt |
| terminal grid (`<Terminal.Grid>` / `<Terminal>`) | replaces the root foreground terminal with one provider-neutral composite whose statically declared direct panes begin concurrently, stay independently interactive, preserve their final statuses until the reader closes the composite, and tear down completely before document execution continues. A paired pane expands isolated document flow; a self-closing pane runs the host's default shell. The grid owns one foreground-terminal lease, each pane owns a separate pane-terminal lease, and a pane-scoped native launcher lets `<Session.Launch>` use that pane without weakening the independent Agent session coordinator. The launcher terminates at the composite's required provider-neutral pane-execution operation; the authored ordinal stays in core's live closure, and the native request carries no pane identity. Core validates the complete row-major layout before provider contact, attaches only after every pane is ready, contains post-attach pane failures until close, and records the ordered provider-neutral outcomes. Completed replay contacts no terminal or Agent provider; partial replay rebuilds a fresh composite, restores completed panes as statuses, and continues incomplete pane effects under their existing durable identities. Provider commands, sockets, process topology and layout identifiers remain live-only inside the provider closure | defined for #717; #726 proves the persistent tmux pane-worker topology and its observable teardown boundary on macOS; structure and layout built in #729, provider-neutral execution and durability in #730, pane claim admission and native-launch middleware in #731; the required composite pane-execution endpoint is specified and implemented in #732, which is what gives a pane's `<Session.Launch>` that pane's terminal rather than the root's; the controlled non-tmux provider remains the authority for core lifecycle semantics; the tmux provider is built in #732 for the Deno and compiled foreground hosts — one invocation-private server per grid, authenticated persistent pane workers carrying exact argv, cwd and environment outside tmux parsing, explicit row-major layout imposed by pane swaps, a required composite `launch()` that gives a pane's `<Session.Launch>` its own terminal rather than the root's, and one ordered teardown that proves worker quiescence, channel closure and server disappearance before the document continues; its evidence uses a fake tmux with real workers and real sockets, and real tmux behaviour on macOS remains #726's; Node and Bun catalog and validate the same grids and install neither the provider nor the process observer, refusing before pane start |
| native session launch (`<Session.Launch>` / `launchAgentSession()`) | prepares one durable coding-agent session from the rendered body of `<Session.Launch>` and hands the provider's native UI the terminal for that exact session, then continues the document after it exits. The body renders completely first and only what it rendered crosses as the instruction layer; the launch performs no model turn; at the root it takes the run's foreground-terminal lease before an agent is resolved, while a launch inside `<Terminal>` takes that pane's lease through its pane-scoped native launcher. A host with no applicable terminal refuses without probing for an installed CLI. A session is constructed once, by one of two mechanisms, and its create-once construction route says which. Where the provider returns the identity, the ACPX provider creates the session, installs the layer at creation, releases ACP ownership before the spawn, and marks its handle stale so a later `<Prompt>` reattaches. Where the adapter names its own sessions, it allocates the identity inside ownership before any process exists, the native process creates the session under that name from a private mode-0600 instruction file, and ACP creates nothing — the instruction text reaches neither argv nor environment, and the file is removed on success, failure and cancellation alike while ownership is still held. Neither route converts into the other, and which one governs is chosen by the first operation that consumes the placement rather than by the `<Session>` that made it: a fresh `<Session>` publishes no route and establishes nothing, so a `<Session.Launch>` nested inside one constructs the session it placed, while a first subscribed `<Prompt>` publishes ACP-first before it ensures and keeps that account even if the turn that follows is never accepted. An established route is validated eagerly by a later `<Session>`, and a launch meeting a published ACP-first route refuses before an identity exists. A `<Session>` or `<Prompt>` meeting a bound client-allocated route attaches under the route's exact identity; a legacy unbound route or an unavailable attachment capability refuses before a turn and creates no substitute conversation. Phases are retained as `agent_session_launch` records under one expansion identity — `prepared` before ownership is released, then `detached`, then `exited` — so a completed replay launches nothing, a replay holding only `prepared` proves the handoff never began and may still create under the retained identity, and one holding `detached` resumes and never falls back. The public route carries an opaque one-use launch request and answers nothing; authority to run and retain a phase is delivered to the installed provider directly, so neither a returned completion nor a rebuilt request authors a launch. Every operation that can act on an advertised session takes exclusive ownership under one natural key first, through a coordinator the host built and passed in; contention refuses instead of queueing, and an owner that never proved it stopped leaves a recovery tombstone. A host that cannot say who owns a session refuses every advertised operation, and one that cannot say how a session was constructed additionally refuses an agent that names its own — before any provider effect. Every private setup or child-creation failure is normalized to `process-creation-failed` with fixed provider-owned text, carrying no path, argv, environment or host message. No launch path discards persistent provider state. A client-allocated session is bound to one executable build: the build is observed inside ownership before an identity is allocated, the binding is published with the V2 route and retained beside the prepared record, the native child runs the exact observed path in place of the launcher name, and every later create, resume, attachment and incomplete replay reobserves and compares before a process, an ensure or a turn. A `<Session>` or `<Prompt>` meeting a bound client-native route attaches to it: it reobserves the build, requires any retained provider arrangement to assert that same conversation, calls ensure with the route identity as `resumeSessionId`, and requires the provider to report that identity before a turn — refusing on missing capability, build drift, missing history or a differing assertion without creating a substitute conversation. ACP runtimes are partitioned by resolved agent command and binding, each handle is closed by the partition that created it, and a bound partition is torn down when its last handle closes. A legacy V1 client-native route keeps exactly the released native-only behavior and never attaches | built on the #517 stack, extended by the #519 and #561 stacks; Deno and the compiled binary assemble the host — coordinator, route store and executable observer — and Node and Bun keep the same advertised names while assembling none of it, so every advertised operation refuses before provider work; `claude` is advertised for native launch after passing the client-allocated gate at Claude Code 2.1.241 on macOS arm64 (#520) and separately for client-native attachment after passing the native-to-ACP marker gate (#561), and Codex remains unadvertised because nothing has run its provider-returned claims against an installed Codex; `Agent.AddDir` is unbuilt |
| `<Fetch>` | performs one XMD-mediated HTTP read through contextual `API.Fetch`, admitting the whole request before transport, and retains the normalized request and the detached response as one `fetch` durable observation; capture decides whether a status is data or a failure, and the trusted host's destination ceiling sits below the component | built on the #456 stack; a generated fragment may name the pinned identity only for a request the trusted host stated exactly, on the #369 stack |
| `API.Files` | routes every document filesystem operation to the installed provider, with no host default and structural failure data. Its mandatory semantic operations include `ensureDirectory`, which recursively creates or adopts one directory and returns Unit; separately loaded copies compose through the stable Api name | built on the #227 stack; directory ensure added by #643 |
Expand Down
16 changes: 16 additions & 0 deletions packages/cli/src/agent-stack.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -22,6 +22,8 @@ import {
} from "@executablemd/core";
import type { AgentProviderFactory, PermissionMode } from "@executablemd/core";
import { installForegroundLauncher, env as readEnv } from "@executablemd/runtime";
import { unsupportedTerminalGrid } from "./terminal/host.ts";
import type { TerminalGridInstaller } from "./terminal/host.ts";
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
Expand DownExpand Up@@ -67,6 +69,14 @@ export interface AgentStack {
adapters: EmbeddedAdapters;
/** What this host states about machine-wide agent sessions, if anything. */
sessions?: MachineSessionAssembly;
/**
* What presents this host's terminal grids.
*
* Deno and the compiled binary supply the tmux provider; Node and Bun supply
* the one that installs none, so those runtimes describe and validate the
* same grids and open none of them.
*/
installTerminalGrid?: TerminalGridInstaller;
}

/**
Expand All@@ -80,6 +90,7 @@ export interface AgentStack {
export function* resolveAgentStack(
flags: AgentFlags,
sessions: MachineSessionAssembly | undefined,
installTerminalGrid?: TerminalGridInstaller,
): Operation<Result<AgentStack>> {
const config = resolveAgentConfig(flags);
if ("error" in config) {
Expand All@@ -96,6 +107,7 @@ export function* resolveAgentStack(
permissionMode: config.permissionMode,
adapters: createEmbeddedAdapters(DEFAULT_ADAPTER_ROOT),
...(sessions === undefined ? {} : { sessions }),
...(installTerminalGrid === undefined ? {} : { installTerminalGrid }),
});
}

Expand DownExpand Up@@ -157,4 +169,8 @@ export function* installRunAgentStack(stack: AgentStack): Operation<void> {
// document inspection and `xmd test` install no launcher, so a document that
// reaches <Session.Launch> under any of them refuses instead of spawning.
yield* installForegroundLauncher();
// And whatever presents this host's terminal grids, which on a host that
// presents none still opens the installation so a grid is validated — the
// refusal a document meets there is core's own.
yield* (stack.installTerminalGrid ?? unsupportedTerminalGrid)();
}
14 changes: 13 additions & 1 deletion packages/cli/src/cli.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -95,6 +95,8 @@ import { installWebComponents, installWebElicitation } from "@executablemd/web";
import { timebox } from "@effectionx/timebox";
import { timeout as runTimeout } from "@executablemd/runtime";
import { installRunAgentStack, resolveAgentStack } from "./agent-stack.ts";
import { unsupportedTerminalGrid } from "./terminal/host.ts";
import type { TerminalGridInstaller } from "./terminal/host.ts";
import { planComponentDeclaration } from "./plan-component.ts";
import { VERBOSE_REGISTRATION } from "./verbose-component.ts";
import type { AgentStack } from "./agent-stack.ts";
Expand DownExpand Up@@ -725,8 +727,9 @@ function* underRunDeadline(timeouts: RunTimeouts, body: () => Operation<void>):
function* settleAgentStack(
flags: AgentFlags,
sessions: MachineSessionAssembly | undefined,
installTerminalGrid: TerminalGridInstaller,
): Operation<AgentStack | undefined> {
const stack = yield* resolveAgentStack(flags, sessions);
const stack = yield* resolveAgentStack(flags, sessions, installTerminalGrid);
if (!stack.ok) {
console.error(stack.error.message);
yield* exit(1);
Expand DownExpand Up@@ -2155,6 +2158,7 @@ function* dispatch(
installRepositories: RepositoryInstaller,
workflowHost: WorkflowHost | undefined,
sessions: MachineSessionAssembly | undefined,
installTerminalGrid: TerminalGridInstaller,
): Operation<void> {
const propsPhase = yield* preparePropsPhase(helpRequest.args, evalFlags);

Expand DownExpand Up@@ -2229,6 +2233,7 @@ function* dispatch(
denyAll: config.denyAll,
},
sessions,
installTerminalGrid,
);
if (runStack === undefined) {
break;
Expand DownExpand Up@@ -2290,6 +2295,7 @@ function* dispatch(
denyAll: config.denyAll,
},
sessions,
installTerminalGrid,
);
if (planStack === undefined) {
break;
Expand DownExpand Up@@ -2565,6 +2571,10 @@ export function* runXmd(
// owns the session or which build it belongs to. A caller that names none
// gets no machine sessions at all, which is the ordinary ACP behaviour.
sessions?: MachineSessionAssembly,
// What presents a terminal grid on this host. Deno and the compiled binary
// supply the tmux provider; Node and Bun supply the one that installs none,
// so those runtimes describe and validate the same grids and open none.
installTerminalGrid: TerminalGridInstaller = unsupportedTerminalGrid,
): Operation<void> {
// Before every scanner, before command selection, and before anything reads a
// path. `prompt` names no command, and a first token that names none is a
Expand DownExpand Up@@ -2632,6 +2642,7 @@ export function* runXmd(
installRepositories,
workflowHost,
sessions,
installTerminalGrid,
);
}

Expand All@@ -2651,6 +2662,7 @@ export function* runXmd(
installRepositories,
workflowHost,
sessions,
installTerminalGrid,
),
);
}
Loading
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
40 changes: 30 additions & 10 deletions architecture.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -2847,15 +2847,35 @@ request to act. This preserves provider composition without letting a document
or replacement context mint terminal ownership.

A pane claim grants one interactive terminal at that ordinal, not an Agent
session. Core installs a pane-scoped native launcher that closes over the claim.
`<Session.Launch>` in that pane consequently reserves, flushes, and launches on
the pane terminal instead of competing for the root lease. Launches in
different panes may run concurrently; two interactive launches in one pane
cannot. Sequential launches in one paired pane remain ordinary composition.
The session coordinator is unchanged and independently authoritative, so two
panes attempting to own the same logical Agent session still contend and one
is refused. The provider starts a self-closing pane's host-configured default
shell under the same kind of pane claim.
session. Core installs a pane-scoped native launcher that closes over the claim,
the composite, and the authored ordinal. `<Session.Launch>` in that pane
consequently reserves and flushes the pane, then terminates native-launch
routing at this required provider-neutral composite operation:

```ts
launch(
ordinal: number,
request: NativeLaunchRequest,
spawned: () => void,
): Operation<NativeLaunchOutcome>;
```

The ordinal exists only in core's live closure and never enters the native or
Agent request. Once nearer native-launch middleware delegates, the pane launcher
calls the composite operation instead of the root foreground launcher. Nearer
middleware may still observe, wrap, refuse, or short-circuit the request. A
composite that cannot execute the pane request refuses explicitly; falling
through to the root would put the child on the wrong physical terminal. Root
`<Session.Launch>` retains its existing foreground-launch route unchanged.

Launches in different panes may run concurrently; two interactive launches in
one pane cannot. Sequential launches in one paired pane remain ordinary
composition. The session coordinator is unchanged and independently
authoritative, so two panes attempting to own the same logical Agent session
still contend and one is refused. The composite's separate `shell()` operation
starts a self-closing pane's host-configured default shell under the same kind
of pane claim; it remains separate because its executable is live host policy,
not an authored or Agent-provided native launch request.

Each claim also closes over one host-owned readiness latch. The pane-scoped
native launcher acknowledges it from the runtime's successful child-spawn event
Expand DownExpand Up@@ -4137,7 +4157,7 @@ Status is measured against main.
| testing harness (`<Execution>`) | runs another document as a real root under a production host profile, authorized by canonical `<Test>` alone: declarations installed before the root import, child output displayed progressively and collected only when asked, journal retention selected independently of observation, and the outcome published by the invocation's own terminal through a request public middleware composes around but cannot answer | built on the #454 stack for `host="run"`; the workflow profile and `<WorkflowRun>` are unbuilt, and a host that offers no workflow profile refuses them |
| nested run-profile Agent and elicitation declarations | lets one `<Execution host="run">` declare one child-scoped `<TestAgent>` scenario set and one non-delegating `<Answers>` matcher set; only frozen test data crosses the harness request, the trusted host constructs both providers inside the isolated child, siblings share no session or provider state, ordinary component shadowing remains in force, and the child journal retains only the selected Prompt and Elicit components' ordinary results | built on the #641 stack |
| `Config` run deadline / exec default / Fetch default / verbosity | three independently owned contextual timeouts, absent unless configured, each read by exactly one consumer, and contextual verbosity — a boolean that is false unless configured, seeded by the command line and overridable for a lexical subtree, bounding nothing and owning no authority | built on this stack |
| terminal grid (`<Terminal.Grid>` / `<Terminal>`) | replaces the root foreground terminal with one provider-neutral composite whose statically declared direct panes begin concurrently, stay independently interactive, preserve their final statuses until the reader closes the composite, and tear down completely before document execution continues. A paired pane expands isolated document flow; a self-closing pane runs the host's default shell. The grid owns one foreground-terminal lease, each pane owns a separate pane-terminal lease, and a pane-scoped native launcher lets `<Session.Launch>` use that pane without weakening the independent Agent session coordinator. Core validates the complete row-major layout before provider contact, attaches only after every pane is ready, contains post-attach pane failures until close, and records the ordered provider-neutral outcomes. Completed replay contacts no terminal or Agent provider; partial replay rebuilds a fresh composite, restores completed panes as statuses, and continues incomplete pane effects under their existing durable identities. Provider commands, sockets, process topology and layout identifiers remain live-only inside the provider closure | defined for #717; #726 proves the persistent tmux pane-worker topology and its observable teardown boundary on macOS; first production provider is tmux in the Deno and compiled foreground hosts; controlled non-tmux provider proves the core contract; implementation unbuilt |
| terminal grid (`<Terminal.Grid>` / `<Terminal>`) | replaces the root foreground terminal with one provider-neutral composite whose statically declared direct panes begin concurrently, stay independently interactive, preserve their final statuses until the reader closes the composite, and tear down completely before document execution continues. A paired pane expands isolated document flow; a self-closing pane runs the host's default shell. The grid owns one foreground-terminal lease, each pane owns a separate pane-terminal lease, and a pane-scoped native launcher lets `<Session.Launch>` use that pane without weakening the independent Agent session coordinator. The launcher terminates at the composite's required provider-neutral pane-execution operation; the authored ordinal stays in core's live closure, and the native request carries no pane identity. Core validates the complete row-major layout before provider contact, attaches only after every pane is ready, contains post-attach pane failures until close, and records the ordered provider-neutral outcomes. Completed replay contacts no terminal or Agent provider; partial replay rebuilds a fresh composite, restores completed panes as statuses, and continues incomplete pane effects under their existing durable identities. Provider commands, sockets, process topology and layout identifiers remain live-only inside the provider closure | defined for #717; #726 proves the persistent tmux pane-worker topology and its observable teardown boundary on macOS; structure and layout built in #729, provider-neutral execution and durability in #730, pane claim admission and native-launch middleware in #731; the required composite pane-execution endpoint is specified and implemented in #732, which is what gives a pane's `<Session.Launch>` that pane's terminal rather than the root's; the controlled non-tmux provider remains the authority for core lifecycle semantics; the tmux provider is built in #732 for the Deno and compiled foreground hosts — one invocation-private server per grid, authenticated persistent pane workers carrying exact argv, cwd and environment outside tmux parsing, explicit row-major layout imposed by pane swaps, a required composite `launch()` that gives a pane's `<Session.Launch>` its own terminal rather than the root's, and one ordered teardown that proves worker quiescence, channel closure and server disappearance before the document continues; its evidence uses a fake tmux with real workers and real sockets, and real tmux behaviour on macOS remains #726's; Node and Bun catalog and validate the same grids and install neither the provider nor the process observer, refusing before pane start |
| native session launch (`<Session.Launch>` / `launchAgentSession()`) | prepares one durable coding-agent session from the rendered body of `<Session.Launch>` and hands the provider's native UI the terminal for that exact session, then continues the document after it exits. The body renders completely first and only what it rendered crosses as the instruction layer; the launch performs no model turn; at the root it takes the run's foreground-terminal lease before an agent is resolved, while a launch inside `<Terminal>` takes that pane's lease through its pane-scoped native launcher. A host with no applicable terminal refuses without probing for an installed CLI. A session is constructed once, by one of two mechanisms, and its create-once construction route says which. Where the provider returns the identity, the ACPX provider creates the session, installs the layer at creation, releases ACP ownership before the spawn, and marks its handle stale so a later `<Prompt>` reattaches. Where the adapter names its own sessions, it allocates the identity inside ownership before any process exists, the native process creates the session under that name from a private mode-0600 instruction file, and ACP creates nothing — the instruction text reaches neither argv nor environment, and the file is removed on success, failure and cancellation alike while ownership is still held. Neither route converts into the other, and which one governs is chosen by the first operation that consumes the placement rather than by the `<Session>` that made it: a fresh `<Session>` publishes no route and establishes nothing, so a `<Session.Launch>` nested inside one constructs the session it placed, while a first subscribed `<Prompt>` publishes ACP-first before it ensures and keeps that account even if the turn that follows is never accepted. An established route is validated eagerly by a later `<Session>`, and a launch meeting a published ACP-first route refuses before an identity exists. A `<Session>` or `<Prompt>` meeting a bound client-allocated route attaches under the route's exact identity; a legacy unbound route or an unavailable attachment capability refuses before a turn and creates no substitute conversation. Phases are retained as `agent_session_launch` records under one expansion identity — `prepared` before ownership is released, then `detached`, then `exited` — so a completed replay launches nothing, a replay holding only `prepared` proves the handoff never began and may still create under the retained identity, and one holding `detached` resumes and never falls back. The public route carries an opaque one-use launch request and answers nothing; authority to run and retain a phase is delivered to the installed provider directly, so neither a returned completion nor a rebuilt request authors a launch. Every operation that can act on an advertised session takes exclusive ownership under one natural key first, through a coordinator the host built and passed in; contention refuses instead of queueing, and an owner that never proved it stopped leaves a recovery tombstone. A host that cannot say who owns a session refuses every advertised operation, and one that cannot say how a session was constructed additionally refuses an agent that names its own — before any provider effect. Every private setup or child-creation failure is normalized to `process-creation-failed` with fixed provider-owned text, carrying no path, argv, environment or host message. No launch path discards persistent provider state. A client-allocated session is bound to one executable build: the build is observed inside ownership before an identity is allocated, the binding is published with the V2 route and retained beside the prepared record, the native child runs the exact observed path in place of the launcher name, and every later create, resume, attachment and incomplete replay reobserves and compares before a process, an ensure or a turn. A `<Session>` or `<Prompt>` meeting a bound client-native route attaches to it: it reobserves the build, requires any retained provider arrangement to assert that same conversation, calls ensure with the route identity as `resumeSessionId`, and requires the provider to report that identity before a turn — refusing on missing capability, build drift, missing history or a differing assertion without creating a substitute conversation. ACP runtimes are partitioned by resolved agent command and binding, each handle is closed by the partition that created it, and a bound partition is torn down when its last handle closes. A legacy V1 client-native route keeps exactly the released native-only behavior and never attaches | built on the #517 stack, extended by the #519 and #561 stacks; Deno and the compiled binary assemble the host — coordinator, route store and executable observer — and Node and Bun keep the same advertised names while assembling none of it, so every advertised operation refuses before provider work; `claude` is advertised for native launch after passing the client-allocated gate at Claude Code 2.1.241 on macOS arm64 (#520) and separately for client-native attachment after passing the native-to-ACP marker gate (#561), and Codex remains unadvertised because nothing has run its provider-returned claims against an installed Codex; `Agent.AddDir` is unbuilt |
| `<Fetch>` | performs one XMD-mediated HTTP read through contextual `API.Fetch`, admitting the whole request before transport, and retains the normalized request and the detached response as one `fetch` durable observation; capture decides whether a status is data or a failure, and the trusted host's destination ceiling sits below the component | built on the #456 stack; a generated fragment may name the pinned identity only for a request the trusted host stated exactly, on the #369 stack |
| `API.Files` | routes every document filesystem operation to the installed provider, with no host default and structural failure data. Its mandatory semantic operations include `ensureDirectory`, which recursively creates or adopts one directory and returns Unit; separately loaded copies compose through the stable Api name | built on the #227 stack; directory ensure added by #643 |
Expand Down
16 changes: 16 additions & 0 deletions packages/cli/src/agent-stack.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -22,6 +22,8 @@ import {
} from "@executablemd/core";
import type { AgentProviderFactory, PermissionMode } from "@executablemd/core";
import { installForegroundLauncher, env as readEnv } from "@executablemd/runtime";
import { unsupportedTerminalGrid } from "./terminal/host.ts";
import type { TerminalGridInstaller } from "./terminal/host.ts";
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
Expand DownExpand Up@@ -67,6 +69,14 @@ export interface AgentStack {
adapters: EmbeddedAdapters;
/** What this host states about machine-wide agent sessions, if anything. */
sessions?: MachineSessionAssembly;
/**
* What presents this host's terminal grids.
*
* Deno and the compiled binary supply the tmux provider; Node and Bun supply
* the one that installs none, so those runtimes describe and validate the
* same grids and open none of them.
*/
installTerminalGrid?: TerminalGridInstaller;
}

/**
Expand All@@ -80,6 +90,7 @@ export interface AgentStack {
export function* resolveAgentStack(
flags: AgentFlags,
sessions: MachineSessionAssembly | undefined,
installTerminalGrid?: TerminalGridInstaller,
): Operation<Result<AgentStack>> {
const config = resolveAgentConfig(flags);
if ("error" in config) {
Expand All@@ -96,6 +107,7 @@ export function* resolveAgentStack(
permissionMode: config.permissionMode,
adapters: createEmbeddedAdapters(DEFAULT_ADAPTER_ROOT),
...(sessions === undefined ? {} : { sessions }),
...(installTerminalGrid === undefined ? {} : { installTerminalGrid }),
});
}

Expand DownExpand Up@@ -157,4 +169,8 @@ export function* installRunAgentStack(stack: AgentStack): Operation<void> {
// document inspection and `xmd test` install no launcher, so a document that
// reaches <Session.Launch> under any of them refuses instead of spawning.
yield* installForegroundLauncher();
// And whatever presents this host's terminal grids, which on a host that
// presents none still opens the installation so a grid is validated — the
// refusal a document meets there is core's own.
yield* (stack.installTerminalGrid ?? unsupportedTerminalGrid)();
}
14 changes: 13 additions & 1 deletion packages/cli/src/cli.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -95,6 +95,8 @@ import { installWebComponents, installWebElicitation } from "@executablemd/web";
import { timebox } from "@effectionx/timebox";
import { timeout as runTimeout } from "@executablemd/runtime";
import { installRunAgentStack, resolveAgentStack } from "./agent-stack.ts";
import { unsupportedTerminalGrid } from "./terminal/host.ts";
import type { TerminalGridInstaller } from "./terminal/host.ts";
import { planComponentDeclaration } from "./plan-component.ts";
import { VERBOSE_REGISTRATION } from "./verbose-component.ts";
import type { AgentStack } from "./agent-stack.ts";
Expand DownExpand Up@@ -725,8 +727,9 @@ function* underRunDeadline(timeouts: RunTimeouts, body: () => Operation<void>):
function* settleAgentStack(
flags: AgentFlags,
sessions: MachineSessionAssembly | undefined,
installTerminalGrid: TerminalGridInstaller,
): Operation<AgentStack | undefined> {
const stack = yield* resolveAgentStack(flags, sessions);
const stack = yield* resolveAgentStack(flags, sessions, installTerminalGrid);
if (!stack.ok) {
console.error(stack.error.message);
yield* exit(1);
Expand DownExpand Up@@ -2155,6 +2158,7 @@ function* dispatch(
installRepositories: RepositoryInstaller,
workflowHost: WorkflowHost | undefined,
sessions: MachineSessionAssembly | undefined,
installTerminalGrid: TerminalGridInstaller,
): Operation<void> {
const propsPhase = yield* preparePropsPhase(helpRequest.args, evalFlags);

Expand DownExpand Up@@ -2229,6 +2233,7 @@ function* dispatch(
denyAll: config.denyAll,
},
sessions,
installTerminalGrid,
);
if (runStack === undefined) {
break;
Expand DownExpand Up@@ -2290,6 +2295,7 @@ function* dispatch(
denyAll: config.denyAll,
},
sessions,
installTerminalGrid,
);
if (planStack === undefined) {
break;
Expand DownExpand Up@@ -2565,6 +2571,10 @@ export function* runXmd(
// owns the session or which build it belongs to. A caller that names none
// gets no machine sessions at all, which is the ordinary ACP behaviour.
sessions?: MachineSessionAssembly,
// What presents a terminal grid on this host. Deno and the compiled binary
// supply the tmux provider; Node and Bun supply the one that installs none,
// so those runtimes describe and validate the same grids and open none.
installTerminalGrid: TerminalGridInstaller = unsupportedTerminalGrid,
): Operation<void> {
// Before every scanner, before command selection, and before anything reads a
// path. `prompt` names no command, and a first token that names none is a
Expand DownExpand Up@@ -2632,6 +2642,7 @@ export function* runXmd(
installRepositories,
workflowHost,
sessions,
installTerminalGrid,
);
}

Expand All@@ -2651,6 +2662,7 @@ export function* runXmd(
installRepositories,
workflowHost,
sessions,
installTerminalGrid,
),
);
}
Loading
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length \u003e 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
40 changes: 30 additions & 10 deletions architecture.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -2847,15 +2847,35 @@ request to act. This preserves provider composition without letting a document
or replacement context mint terminal ownership.

A pane claim grants one interactive terminal at that ordinal, not an Agent
session. Core installs a pane-scoped native launcher that closes over the claim.
`<Session.Launch>` in that pane consequently reserves, flushes, and launches on
the pane terminal instead of competing for the root lease. Launches in
different panes may run concurrently; two interactive launches in one pane
cannot. Sequential launches in one paired pane remain ordinary composition.
The session coordinator is unchanged and independently authoritative, so two
panes attempting to own the same logical Agent session still contend and one
is refused. The provider starts a self-closing pane's host-configured default
shell under the same kind of pane claim.
session. Core installs a pane-scoped native launcher that closes over the claim,
the composite, and the authored ordinal. `<Session.Launch>` in that pane
consequently reserves and flushes the pane, then terminates native-launch
routing at this required provider-neutral composite operation:

```ts
launch(
ordinal: number,
request: NativeLaunchRequest,
spawned: () => void,
): Operation<NativeLaunchOutcome>;
```

The ordinal exists only in core's live closure and never enters the native or
Agent request. Once nearer native-launch middleware delegates, the pane launcher
calls the composite operation instead of the root foreground launcher. Nearer
middleware may still observe, wrap, refuse, or short-circuit the request. A
composite that cannot execute the pane request refuses explicitly; falling
through to the root would put the child on the wrong physical terminal. Root
`<Session.Launch>` retains its existing foreground-launch route unchanged.

Launches in different panes may run concurrently; two interactive launches in
one pane cannot. Sequential launches in one paired pane remain ordinary
composition. The session coordinator is unchanged and independently
authoritative, so two panes attempting to own the same logical Agent session
still contend and one is refused. The composite's separate `shell()` operation
starts a self-closing pane's host-configured default shell under the same kind
of pane claim; it remains separate because its executable is live host policy,
not an authored or Agent-provided native launch request.

Each claim also closes over one host-owned readiness latch. The pane-scoped
native launcher acknowledges it from the runtime's successful child-spawn event
Expand DownExpand Up@@ -4137,7 +4157,7 @@ Status is measured against main.
| testing harness (`<Execution>`) | runs another document as a real root under a production host profile, authorized by canonical `<Test>` alone: declarations installed before the root import, child output displayed progressively and collected only when asked, journal retention selected independently of observation, and the outcome published by the invocation's own terminal through a request public middleware composes around but cannot answer | built on the #454 stack for `host="run"`; the workflow profile and `<WorkflowRun>` are unbuilt, and a host that offers no workflow profile refuses them |
| nested run-profile Agent and elicitation declarations | lets one `<Execution host="run">` declare one child-scoped `<TestAgent>` scenario set and one non-delegating `<Answers>` matcher set; only frozen test data crosses the harness request, the trusted host constructs both providers inside the isolated child, siblings share no session or provider state, ordinary component shadowing remains in force, and the child journal retains only the selected Prompt and Elicit components' ordinary results | built on the #641 stack |
| `Config` run deadline / exec default / Fetch default / verbosity | three independently owned contextual timeouts, absent unless configured, each read by exactly one consumer, and contextual verbosity — a boolean that is false unless configured, seeded by the command line and overridable for a lexical subtree, bounding nothing and owning no authority | built on this stack |
| terminal grid (`<Terminal.Grid>` / `<Terminal>`) | replaces the root foreground terminal with one provider-neutral composite whose statically declared direct panes begin concurrently, stay independently interactive, preserve their final statuses until the reader closes the composite, and tear down completely before document execution continues. A paired pane expands isolated document flow; a self-closing pane runs the host's default shell. The grid owns one foreground-terminal lease, each pane owns a separate pane-terminal lease, and a pane-scoped native launcher lets `<Session.Launch>` use that pane without weakening the independent Agent session coordinator. Core validates the complete row-major layout before provider contact, attaches only after every pane is ready, contains post-attach pane failures until close, and records the ordered provider-neutral outcomes. Completed replay contacts no terminal or Agent provider; partial replay rebuilds a fresh composite, restores completed panes as statuses, and continues incomplete pane effects under their existing durable identities. Provider commands, sockets, process topology and layout identifiers remain live-only inside the provider closure | defined for #717; #726 proves the persistent tmux pane-worker topology and its observable teardown boundary on macOS; first production provider is tmux in the Deno and compiled foreground hosts; controlled non-tmux provider proves the core contract; implementation unbuilt |
| terminal grid (`<Terminal.Grid>` / `<Terminal>`) | replaces the root foreground terminal with one provider-neutral composite whose statically declared direct panes begin concurrently, stay independently interactive, preserve their final statuses until the reader closes the composite, and tear down completely before document execution continues. A paired pane expands isolated document flow; a self-closing pane runs the host's default shell. The grid owns one foreground-terminal lease, each pane owns a separate pane-terminal lease, and a pane-scoped native launcher lets `<Session.Launch>` use that pane without weakening the independent Agent session coordinator. The launcher terminates at the composite's required provider-neutral pane-execution operation; the authored ordinal stays in core's live closure, and the native request carries no pane identity. Core validates the complete row-major layout before provider contact, attaches only after every pane is ready, contains post-attach pane failures until close, and records the ordered provider-neutral outcomes. Completed replay contacts no terminal or Agent provider; partial replay rebuilds a fresh composite, restores completed panes as statuses, and continues incomplete pane effects under their existing durable identities. Provider commands, sockets, process topology and layout identifiers remain live-only inside the provider closure | defined for #717; #726 proves the persistent tmux pane-worker topology and its observable teardown boundary on macOS; structure and layout built in #729, provider-neutral execution and durability in #730, pane claim admission and native-launch middleware in #731; the required composite pane-execution endpoint is specified and implemented in #732, which is what gives a pane's `<Session.Launch>` that pane's terminal rather than the root's; the controlled non-tmux provider remains the authority for core lifecycle semantics; the tmux provider is built in #732 for the Deno and compiled foreground hosts — one invocation-private server per grid, authenticated persistent pane workers carrying exact argv, cwd and environment outside tmux parsing, explicit row-major layout imposed by pane swaps, a required composite `launch()` that gives a pane's `<Session.Launch>` its own terminal rather than the root's, and one ordered teardown that proves worker quiescence, channel closure and server disappearance before the document continues; its evidence uses a fake tmux with real workers and real sockets, and real tmux behaviour on macOS remains #726's; Node and Bun catalog and validate the same grids and install neither the provider nor the process observer, refusing before pane start |
| native session launch (`<Session.Launch>` / `launchAgentSession()`) | prepares one durable coding-agent session from the rendered body of `<Session.Launch>` and hands the provider's native UI the terminal for that exact session, then continues the document after it exits. The body renders completely first and only what it rendered crosses as the instruction layer; the launch performs no model turn; at the root it takes the run's foreground-terminal lease before an agent is resolved, while a launch inside `<Terminal>` takes that pane's lease through its pane-scoped native launcher. A host with no applicable terminal refuses without probing for an installed CLI. A session is constructed once, by one of two mechanisms, and its create-once construction route says which. Where the provider returns the identity, the ACPX provider creates the session, installs the layer at creation, releases ACP ownership before the spawn, and marks its handle stale so a later `<Prompt>` reattaches. Where the adapter names its own sessions, it allocates the identity inside ownership before any process exists, the native process creates the session under that name from a private mode-0600 instruction file, and ACP creates nothing — the instruction text reaches neither argv nor environment, and the file is removed on success, failure and cancellation alike while ownership is still held. Neither route converts into the other, and which one governs is chosen by the first operation that consumes the placement rather than by the `<Session>` that made it: a fresh `<Session>` publishes no route and establishes nothing, so a `<Session.Launch>` nested inside one constructs the session it placed, while a first subscribed `<Prompt>` publishes ACP-first before it ensures and keeps that account even if the turn that follows is never accepted. An established route is validated eagerly by a later `<Session>`, and a launch meeting a published ACP-first route refuses before an identity exists. A `<Session>` or `<Prompt>` meeting a bound client-allocated route attaches under the route's exact identity; a legacy unbound route or an unavailable attachment capability refuses before a turn and creates no substitute conversation. Phases are retained as `agent_session_launch` records under one expansion identity — `prepared` before ownership is released, then `detached`, then `exited` — so a completed replay launches nothing, a replay holding only `prepared` proves the handoff never began and may still create under the retained identity, and one holding `detached` resumes and never falls back. The public route carries an opaque one-use launch request and answers nothing; authority to run and retain a phase is delivered to the installed provider directly, so neither a returned completion nor a rebuilt request authors a launch. Every operation that can act on an advertised session takes exclusive ownership under one natural key first, through a coordinator the host built and passed in; contention refuses instead of queueing, and an owner that never proved it stopped leaves a recovery tombstone. A host that cannot say who owns a session refuses every advertised operation, and one that cannot say how a session was constructed additionally refuses an agent that names its own — before any provider effect. Every private setup or child-creation failure is normalized to `process-creation-failed` with fixed provider-owned text, carrying no path, argv, environment or host message. No launch path discards persistent provider state. A client-allocated session is bound to one executable build: the build is observed inside ownership before an identity is allocated, the binding is published with the V2 route and retained beside the prepared record, the native child runs the exact observed path in place of the launcher name, and every later create, resume, attachment and incomplete replay reobserves and compares before a process, an ensure or a turn. A `<Session>` or `<Prompt>` meeting a bound client-native route attaches to it: it reobserves the build, requires any retained provider arrangement to assert that same conversation, calls ensure with the route identity as `resumeSessionId`, and requires the provider to report that identity before a turn — refusing on missing capability, build drift, missing history or a differing assertion without creating a substitute conversation. ACP runtimes are partitioned by resolved agent command and binding, each handle is closed by the partition that created it, and a bound partition is torn down when its last handle closes. A legacy V1 client-native route keeps exactly the released native-only behavior and never attaches | built on the #517 stack, extended by the #519 and #561 stacks; Deno and the compiled binary assemble the host — coordinator, route store and executable observer — and Node and Bun keep the same advertised names while assembling none of it, so every advertised operation refuses before provider work; `claude` is advertised for native launch after passing the client-allocated gate at Claude Code 2.1.241 on macOS arm64 (#520) and separately for client-native attachment after passing the native-to-ACP marker gate (#561), and Codex remains unadvertised because nothing has run its provider-returned claims against an installed Codex; `Agent.AddDir` is unbuilt |
| `<Fetch>` | performs one XMD-mediated HTTP read through contextual `API.Fetch`, admitting the whole request before transport, and retains the normalized request and the detached response as one `fetch` durable observation; capture decides whether a status is data or a failure, and the trusted host's destination ceiling sits below the component | built on the #456 stack; a generated fragment may name the pinned identity only for a request the trusted host stated exactly, on the #369 stack |
| `API.Files` | routes every document filesystem operation to the installed provider, with no host default and structural failure data. Its mandatory semantic operations include `ensureDirectory`, which recursively creates or adopts one directory and returns Unit; separately loaded copies compose through the stable Api name | built on the #227 stack; directory ensure added by #643 |
Expand Down
16 changes: 16 additions & 0 deletions packages/cli/src/agent-stack.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -22,6 +22,8 @@ import {
} from "@executablemd/core";
import type { AgentProviderFactory, PermissionMode } from "@executablemd/core";
import { installForegroundLauncher, env as readEnv } from "@executablemd/runtime";
import { unsupportedTerminalGrid } from "./terminal/host.ts";
import type { TerminalGridInstaller } from "./terminal/host.ts";
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
Expand DownExpand Up@@ -67,6 +69,14 @@ export interface AgentStack {
adapters: EmbeddedAdapters;
/** What this host states about machine-wide agent sessions, if anything. */
sessions?: MachineSessionAssembly;
/**
* What presents this host's terminal grids.
*
* Deno and the compiled binary supply the tmux provider; Node and Bun supply
* the one that installs none, so those runtimes describe and validate the
* same grids and open none of them.
*/
installTerminalGrid?: TerminalGridInstaller;
}

/**
Expand All@@ -80,6 +90,7 @@ export interface AgentStack {
export function* resolveAgentStack(
flags: AgentFlags,
sessions: MachineSessionAssembly | undefined,
installTerminalGrid?: TerminalGridInstaller,
): Operation<Result<AgentStack>> {
const config = resolveAgentConfig(flags);
if ("error" in config) {
Expand All@@ -96,6 +107,7 @@ export function* resolveAgentStack(
permissionMode: config.permissionMode,
adapters: createEmbeddedAdapters(DEFAULT_ADAPTER_ROOT),
...(sessions === undefined ? {} : { sessions }),
...(installTerminalGrid === undefined ? {} : { installTerminalGrid }),
});
}

Expand DownExpand Up@@ -157,4 +169,8 @@ export function* installRunAgentStack(stack: AgentStack): Operation<void> {
// document inspection and `xmd test` install no launcher, so a document that
// reaches <Session.Launch> under any of them refuses instead of spawning.
yield* installForegroundLauncher();
// And whatever presents this host's terminal grids, which on a host that
// presents none still opens the installation so a grid is validated — the
// refusal a document meets there is core's own.
yield* (stack.installTerminalGrid ?? unsupportedTerminalGrid)();
}
14 changes: 13 additions & 1 deletion packages/cli/src/cli.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -95,6 +95,8 @@ import { installWebComponents, installWebElicitation } from "@executablemd/web";
import { timebox } from "@effectionx/timebox";
import { timeout as runTimeout } from "@executablemd/runtime";
import { installRunAgentStack, resolveAgentStack } from "./agent-stack.ts";
import { unsupportedTerminalGrid } from "./terminal/host.ts";
import type { TerminalGridInstaller } from "./terminal/host.ts";
import { planComponentDeclaration } from "./plan-component.ts";
import { VERBOSE_REGISTRATION } from "./verbose-component.ts";
import type { AgentStack } from "./agent-stack.ts";
Expand DownExpand Up@@ -725,8 +727,9 @@ function* underRunDeadline(timeouts: RunTimeouts, body: () => Operation<void>):
function* settleAgentStack(
flags: AgentFlags,
sessions: MachineSessionAssembly | undefined,
installTerminalGrid: TerminalGridInstaller,
): Operation<AgentStack | undefined> {
const stack = yield* resolveAgentStack(flags, sessions);
const stack = yield* resolveAgentStack(flags, sessions, installTerminalGrid);
if (!stack.ok) {
console.error(stack.error.message);
yield* exit(1);
Expand DownExpand Up@@ -2155,6 +2158,7 @@ function* dispatch(
installRepositories: RepositoryInstaller,
workflowHost: WorkflowHost | undefined,
sessions: MachineSessionAssembly | undefined,
installTerminalGrid: TerminalGridInstaller,
): Operation<void> {
const propsPhase = yield* preparePropsPhase(helpRequest.args, evalFlags);

Expand DownExpand Up@@ -2229,6 +2233,7 @@ function* dispatch(
denyAll: config.denyAll,
},
sessions,
installTerminalGrid,
);
if (runStack === undefined) {
break;
Expand DownExpand Up@@ -2290,6 +2295,7 @@ function* dispatch(
denyAll: config.denyAll,
},
sessions,
installTerminalGrid,
);
if (planStack === undefined) {
break;
Expand DownExpand Up@@ -2565,6 +2571,10 @@ export function* runXmd(
// owns the session or which build it belongs to. A caller that names none
// gets no machine sessions at all, which is the ordinary ACP behaviour.
sessions?: MachineSessionAssembly,
// What presents a terminal grid on this host. Deno and the compiled binary
// supply the tmux provider; Node and Bun supply the one that installs none,
// so those runtimes describe and validate the same grids and open none.
installTerminalGrid: TerminalGridInstaller = unsupportedTerminalGrid,
): Operation<void> {
// Before every scanner, before command selection, and before anything reads a
// path. `prompt` names no command, and a first token that names none is a
Expand DownExpand Up@@ -2632,6 +2642,7 @@ export function* runXmd(
installRepositories,
workflowHost,
sessions,
installTerminalGrid,
);
}

Expand All@@ -2651,6 +2662,7 @@ export function* runXmd(
installRepositories,
workflowHost,
sessions,
installTerminalGrid,
),
);
}
Loading
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
40 changes: 30 additions & 10 deletions architecture.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -2847,15 +2847,35 @@ request to act. This preserves provider composition without letting a document
or replacement context mint terminal ownership.

A pane claim grants one interactive terminal at that ordinal, not an Agent
session. Core installs a pane-scoped native launcher that closes over the claim.
`<Session.Launch>` in that pane consequently reserves, flushes, and launches on
the pane terminal instead of competing for the root lease. Launches in
different panes may run concurrently; two interactive launches in one pane
cannot. Sequential launches in one paired pane remain ordinary composition.
The session coordinator is unchanged and independently authoritative, so two
panes attempting to own the same logical Agent session still contend and one
is refused. The provider starts a self-closing pane's host-configured default
shell under the same kind of pane claim.
session. Core installs a pane-scoped native launcher that closes over the claim,
the composite, and the authored ordinal. `<Session.Launch>` in that pane
consequently reserves and flushes the pane, then terminates native-launch
routing at this required provider-neutral composite operation:

```ts
launch(
ordinal: number,
request: NativeLaunchRequest,
spawned: () => void,
): Operation<NativeLaunchOutcome>;
```

The ordinal exists only in core's live closure and never enters the native or
Agent request. Once nearer native-launch middleware delegates, the pane launcher
calls the composite operation instead of the root foreground launcher. Nearer
middleware may still observe, wrap, refuse, or short-circuit the request. A
composite that cannot execute the pane request refuses explicitly; falling
through to the root would put the child on the wrong physical terminal. Root
`<Session.Launch>` retains its existing foreground-launch route unchanged.

Launches in different panes may run concurrently; two interactive launches in
one pane cannot. Sequential launches in one paired pane remain ordinary
composition. The session coordinator is unchanged and independently
authoritative, so two panes attempting to own the same logical Agent session
still contend and one is refused. The composite's separate `shell()` operation
starts a self-closing pane's host-configured default shell under the same kind
of pane claim; it remains separate because its executable is live host policy,
not an authored or Agent-provided native launch request.

Each claim also closes over one host-owned readiness latch. The pane-scoped
native launcher acknowledges it from the runtime's successful child-spawn event
Expand DownExpand Up@@ -4137,7 +4157,7 @@ Status is measured against main.
| testing harness (`<Execution>`) | runs another document as a real root under a production host profile, authorized by canonical `<Test>` alone: declarations installed before the root import, child output displayed progressively and collected only when asked, journal retention selected independently of observation, and the outcome published by the invocation's own terminal through a request public middleware composes around but cannot answer | built on the #454 stack for `host="run"`; the workflow profile and `<WorkflowRun>` are unbuilt, and a host that offers no workflow profile refuses them |
| nested run-profile Agent and elicitation declarations | lets one `<Execution host="run">` declare one child-scoped `<TestAgent>` scenario set and one non-delegating `<Answers>` matcher set; only frozen test data crosses the harness request, the trusted host constructs both providers inside the isolated child, siblings share no session or provider state, ordinary component shadowing remains in force, and the child journal retains only the selected Prompt and Elicit components' ordinary results | built on the #641 stack |
| `Config` run deadline / exec default / Fetch default / verbosity | three independently owned contextual timeouts, absent unless configured, each read by exactly one consumer, and contextual verbosity — a boolean that is false unless configured, seeded by the command line and overridable for a lexical subtree, bounding nothing and owning no authority | built on this stack |
| terminal grid (`<Terminal.Grid>` / `<Terminal>`) | replaces the root foreground terminal with one provider-neutral composite whose statically declared direct panes begin concurrently, stay independently interactive, preserve their final statuses until the reader closes the composite, and tear down completely before document execution continues. A paired pane expands isolated document flow; a self-closing pane runs the host's default shell. The grid owns one foreground-terminal lease, each pane owns a separate pane-terminal lease, and a pane-scoped native launcher lets `<Session.Launch>` use that pane without weakening the independent Agent session coordinator. Core validates the complete row-major layout before provider contact, attaches only after every pane is ready, contains post-attach pane failures until close, and records the ordered provider-neutral outcomes. Completed replay contacts no terminal or Agent provider; partial replay rebuilds a fresh composite, restores completed panes as statuses, and continues incomplete pane effects under their existing durable identities. Provider commands, sockets, process topology and layout identifiers remain live-only inside the provider closure | defined for #717; #726 proves the persistent tmux pane-worker topology and its observable teardown boundary on macOS; first production provider is tmux in the Deno and compiled foreground hosts; controlled non-tmux provider proves the core contract; implementation unbuilt |
| terminal grid (`<Terminal.Grid>` / `<Terminal>`) | replaces the root foreground terminal with one provider-neutral composite whose statically declared direct panes begin concurrently, stay independently interactive, preserve their final statuses until the reader closes the composite, and tear down completely before document execution continues. A paired pane expands isolated document flow; a self-closing pane runs the host's default shell. The grid owns one foreground-terminal lease, each pane owns a separate pane-terminal lease, and a pane-scoped native launcher lets `<Session.Launch>` use that pane without weakening the independent Agent session coordinator. The launcher terminates at the composite's required provider-neutral pane-execution operation; the authored ordinal stays in core's live closure, and the native request carries no pane identity. Core validates the complete row-major layout before provider contact, attaches only after every pane is ready, contains post-attach pane failures until close, and records the ordered provider-neutral outcomes. Completed replay contacts no terminal or Agent provider; partial replay rebuilds a fresh composite, restores completed panes as statuses, and continues incomplete pane effects under their existing durable identities. Provider commands, sockets, process topology and layout identifiers remain live-only inside the provider closure | defined for #717; #726 proves the persistent tmux pane-worker topology and its observable teardown boundary on macOS; structure and layout built in #729, provider-neutral execution and durability in #730, pane claim admission and native-launch middleware in #731; the required composite pane-execution endpoint is specified and implemented in #732, which is what gives a pane's `<Session.Launch>` that pane's terminal rather than the root's; the controlled non-tmux provider remains the authority for core lifecycle semantics; the tmux provider is built in #732 for the Deno and compiled foreground hosts — one invocation-private server per grid, authenticated persistent pane workers carrying exact argv, cwd and environment outside tmux parsing, explicit row-major layout imposed by pane swaps, a required composite `launch()` that gives a pane's `<Session.Launch>` its own terminal rather than the root's, and one ordered teardown that proves worker quiescence, channel closure and server disappearance before the document continues; its evidence uses a fake tmux with real workers and real sockets, and real tmux behaviour on macOS remains #726's; Node and Bun catalog and validate the same grids and install neither the provider nor the process observer, refusing before pane start |
| native session launch (`<Session.Launch>` / `launchAgentSession()`) | prepares one durable coding-agent session from the rendered body of `<Session.Launch>` and hands the provider's native UI the terminal for that exact session, then continues the document after it exits. The body renders completely first and only what it rendered crosses as the instruction layer; the launch performs no model turn; at the root it takes the run's foreground-terminal lease before an agent is resolved, while a launch inside `<Terminal>` takes that pane's lease through its pane-scoped native launcher. A host with no applicable terminal refuses without probing for an installed CLI. A session is constructed once, by one of two mechanisms, and its create-once construction route says which. Where the provider returns the identity, the ACPX provider creates the session, installs the layer at creation, releases ACP ownership before the spawn, and marks its handle stale so a later `<Prompt>` reattaches. Where the adapter names its own sessions, it allocates the identity inside ownership before any process exists, the native process creates the session under that name from a private mode-0600 instruction file, and ACP creates nothing — the instruction text reaches neither argv nor environment, and the file is removed on success, failure and cancellation alike while ownership is still held. Neither route converts into the other, and which one governs is chosen by the first operation that consumes the placement rather than by the `<Session>` that made it: a fresh `<Session>` publishes no route and establishes nothing, so a `<Session.Launch>` nested inside one constructs the session it placed, while a first subscribed `<Prompt>` publishes ACP-first before it ensures and keeps that account even if the turn that follows is never accepted. An established route is validated eagerly by a later `<Session>`, and a launch meeting a published ACP-first route refuses before an identity exists. A `<Session>` or `<Prompt>` meeting a bound client-allocated route attaches under the route's exact identity; a legacy unbound route or an unavailable attachment capability refuses before a turn and creates no substitute conversation. Phases are retained as `agent_session_launch` records under one expansion identity — `prepared` before ownership is released, then `detached`, then `exited` — so a completed replay launches nothing, a replay holding only `prepared` proves the handoff never began and may still create under the retained identity, and one holding `detached` resumes and never falls back. The public route carries an opaque one-use launch request and answers nothing; authority to run and retain a phase is delivered to the installed provider directly, so neither a returned completion nor a rebuilt request authors a launch. Every operation that can act on an advertised session takes exclusive ownership under one natural key first, through a coordinator the host built and passed in; contention refuses instead of queueing, and an owner that never proved it stopped leaves a recovery tombstone. A host that cannot say who owns a session refuses every advertised operation, and one that cannot say how a session was constructed additionally refuses an agent that names its own — before any provider effect. Every private setup or child-creation failure is normalized to `process-creation-failed` with fixed provider-owned text, carrying no path, argv, environment or host message. No launch path discards persistent provider state. A client-allocated session is bound to one executable build: the build is observed inside ownership before an identity is allocated, the binding is published with the V2 route and retained beside the prepared record, the native child runs the exact observed path in place of the launcher name, and every later create, resume, attachment and incomplete replay reobserves and compares before a process, an ensure or a turn. A `<Session>` or `<Prompt>` meeting a bound client-native route attaches to it: it reobserves the build, requires any retained provider arrangement to assert that same conversation, calls ensure with the route identity as `resumeSessionId`, and requires the provider to report that identity before a turn — refusing on missing capability, build drift, missing history or a differing assertion without creating a substitute conversation. ACP runtimes are partitioned by resolved agent command and binding, each handle is closed by the partition that created it, and a bound partition is torn down when its last handle closes. A legacy V1 client-native route keeps exactly the released native-only behavior and never attaches | built on the #517 stack, extended by the #519 and #561 stacks; Deno and the compiled binary assemble the host — coordinator, route store and executable observer — and Node and Bun keep the same advertised names while assembling none of it, so every advertised operation refuses before provider work; `claude` is advertised for native launch after passing the client-allocated gate at Claude Code 2.1.241 on macOS arm64 (#520) and separately for client-native attachment after passing the native-to-ACP marker gate (#561), and Codex remains unadvertised because nothing has run its provider-returned claims against an installed Codex; `Agent.AddDir` is unbuilt |
| `<Fetch>` | performs one XMD-mediated HTTP read through contextual `API.Fetch`, admitting the whole request before transport, and retains the normalized request and the detached response as one `fetch` durable observation; capture decides whether a status is data or a failure, and the trusted host's destination ceiling sits below the component | built on the #456 stack; a generated fragment may name the pinned identity only for a request the trusted host stated exactly, on the #369 stack |
| `API.Files` | routes every document filesystem operation to the installed provider, with no host default and structural failure data. Its mandatory semantic operations include `ensureDirectory`, which recursively creates or adopts one directory and returns Unit; separately loaded copies compose through the stable Api name | built on the #227 stack; directory ensure added by #643 |
Expand Down
16 changes: 16 additions & 0 deletions packages/cli/src/agent-stack.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -22,6 +22,8 @@ import {
} from "@executablemd/core";
import type { AgentProviderFactory, PermissionMode } from "@executablemd/core";
import { installForegroundLauncher, env as readEnv } from "@executablemd/runtime";
import { unsupportedTerminalGrid } from "./terminal/host.ts";
import type { TerminalGridInstaller } from "./terminal/host.ts";
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
Expand DownExpand Up@@ -67,6 +69,14 @@ export interface AgentStack {
adapters: EmbeddedAdapters;
/** What this host states about machine-wide agent sessions, if anything. */
sessions?: MachineSessionAssembly;
/**
* What presents this host's terminal grids.
*
* Deno and the compiled binary supply the tmux provider; Node and Bun supply
* the one that installs none, so those runtimes describe and validate the
* same grids and open none of them.
*/
installTerminalGrid?: TerminalGridInstaller;
}

/**
Expand All@@ -80,6 +90,7 @@ export interface AgentStack {
export function* resolveAgentStack(
flags: AgentFlags,
sessions: MachineSessionAssembly | undefined,
installTerminalGrid?: TerminalGridInstaller,
): Operation<Result<AgentStack>> {
const config = resolveAgentConfig(flags);
if ("error" in config) {
Expand All@@ -96,6 +107,7 @@ export function* resolveAgentStack(
permissionMode: config.permissionMode,
adapters: createEmbeddedAdapters(DEFAULT_ADAPTER_ROOT),
...(sessions === undefined ? {} : { sessions }),
...(installTerminalGrid === undefined ? {} : { installTerminalGrid }),
});
}

Expand DownExpand Up@@ -157,4 +169,8 @@ export function* installRunAgentStack(stack: AgentStack): Operation<void> {
// document inspection and `xmd test` install no launcher, so a document that
// reaches <Session.Launch> under any of them refuses instead of spawning.
yield* installForegroundLauncher();
// And whatever presents this host's terminal grids, which on a host that
// presents none still opens the installation so a grid is validated — the
// refusal a document meets there is core's own.
yield* (stack.installTerminalGrid ?? unsupportedTerminalGrid)();
}
14 changes: 13 additions & 1 deletion packages/cli/src/cli.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -95,6 +95,8 @@ import { installWebComponents, installWebElicitation } from "@executablemd/web";
import { timebox } from "@effectionx/timebox";
import { timeout as runTimeout } from "@executablemd/runtime";
import { installRunAgentStack, resolveAgentStack } from "./agent-stack.ts";
import { unsupportedTerminalGrid } from "./terminal/host.ts";
import type { TerminalGridInstaller } from "./terminal/host.ts";
import { planComponentDeclaration } from "./plan-component.ts";
import { VERBOSE_REGISTRATION } from "./verbose-component.ts";
import type { AgentStack } from "./agent-stack.ts";
Expand DownExpand Up@@ -725,8 +727,9 @@ function* underRunDeadline(timeouts: RunTimeouts, body: () => Operation<void>):
function* settleAgentStack(
flags: AgentFlags,
sessions: MachineSessionAssembly | undefined,
installTerminalGrid: TerminalGridInstaller,
): Operation<AgentStack | undefined> {
const stack = yield* resolveAgentStack(flags, sessions);
const stack = yield* resolveAgentStack(flags, sessions, installTerminalGrid);
if (!stack.ok) {
console.error(stack.error.message);
yield* exit(1);
Expand DownExpand Up@@ -2155,6 +2158,7 @@ function* dispatch(
installRepositories: RepositoryInstaller,
workflowHost: WorkflowHost | undefined,
sessions: MachineSessionAssembly | undefined,
installTerminalGrid: TerminalGridInstaller,
): Operation<void> {
const propsPhase = yield* preparePropsPhase(helpRequest.args, evalFlags);

Expand DownExpand Up@@ -2229,6 +2233,7 @@ function* dispatch(
denyAll: config.denyAll,
},
sessions,
installTerminalGrid,
);
if (runStack === undefined) {
break;
Expand DownExpand Up@@ -2290,6 +2295,7 @@ function* dispatch(
denyAll: config.denyAll,
},
sessions,
installTerminalGrid,
);
if (planStack === undefined) {
break;
Expand DownExpand Up@@ -2565,6 +2571,10 @@ export function* runXmd(
// owns the session or which build it belongs to. A caller that names none
// gets no machine sessions at all, which is the ordinary ACP behaviour.
sessions?: MachineSessionAssembly,
// What presents a terminal grid on this host. Deno and the compiled binary
// supply the tmux provider; Node and Bun supply the one that installs none,
// so those runtimes describe and validate the same grids and open none.
installTerminalGrid: TerminalGridInstaller = unsupportedTerminalGrid,
): Operation<void> {
// Before every scanner, before command selection, and before anything reads a
// path. `prompt` names no command, and a first token that names none is a
Expand DownExpand Up@@ -2632,6 +2642,7 @@ export function* runXmd(
installRepositories,
workflowHost,
sessions,
installTerminalGrid,
);
}

Expand All@@ -2651,6 +2662,7 @@ export function* runXmd(
installRepositories,
workflowHost,
sessions,
installTerminalGrid,
),
);
}
Loading
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
40 changes: 30 additions & 10 deletions architecture.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -2847,15 +2847,35 @@ request to act. This preserves provider composition without letting a document
or replacement context mint terminal ownership.

A pane claim grants one interactive terminal at that ordinal, not an Agent
session. Core installs a pane-scoped native launcher that closes over the claim.
`<Session.Launch>` in that pane consequently reserves, flushes, and launches on
the pane terminal instead of competing for the root lease. Launches in
different panes may run concurrently; two interactive launches in one pane
cannot. Sequential launches in one paired pane remain ordinary composition.
The session coordinator is unchanged and independently authoritative, so two
panes attempting to own the same logical Agent session still contend and one
is refused. The provider starts a self-closing pane's host-configured default
shell under the same kind of pane claim.
session. Core installs a pane-scoped native launcher that closes over the claim,
the composite, and the authored ordinal. `<Session.Launch>` in that pane
consequently reserves and flushes the pane, then terminates native-launch
routing at this required provider-neutral composite operation:

```ts
launch(
ordinal: number,
request: NativeLaunchRequest,
spawned: () => void,
): Operation<NativeLaunchOutcome>;
```

The ordinal exists only in core's live closure and never enters the native or
Agent request. Once nearer native-launch middleware delegates, the pane launcher
calls the composite operation instead of the root foreground launcher. Nearer
middleware may still observe, wrap, refuse, or short-circuit the request. A
composite that cannot execute the pane request refuses explicitly; falling
through to the root would put the child on the wrong physical terminal. Root
`<Session.Launch>` retains its existing foreground-launch route unchanged.

Launches in different panes may run concurrently; two interactive launches in
one pane cannot. Sequential launches in one paired pane remain ordinary
composition. The session coordinator is unchanged and independently
authoritative, so two panes attempting to own the same logical Agent session
still contend and one is refused. The composite's separate `shell()` operation
starts a self-closing pane's host-configured default shell under the same kind
of pane claim; it remains separate because its executable is live host policy,
not an authored or Agent-provided native launch request.

Each claim also closes over one host-owned readiness latch. The pane-scoped
native launcher acknowledges it from the runtime's successful child-spawn event
Expand DownExpand Up@@ -4137,7 +4157,7 @@ Status is measured against main.
| testing harness (`<Execution>`) | runs another document as a real root under a production host profile, authorized by canonical `<Test>` alone: declarations installed before the root import, child output displayed progressively and collected only when asked, journal retention selected independently of observation, and the outcome published by the invocation's own terminal through a request public middleware composes around but cannot answer | built on the #454 stack for `host="run"`; the workflow profile and `<WorkflowRun>` are unbuilt, and a host that offers no workflow profile refuses them |
| nested run-profile Agent and elicitation declarations | lets one `<Execution host="run">` declare one child-scoped `<TestAgent>` scenario set and one non-delegating `<Answers>` matcher set; only frozen test data crosses the harness request, the trusted host constructs both providers inside the isolated child, siblings share no session or provider state, ordinary component shadowing remains in force, and the child journal retains only the selected Prompt and Elicit components' ordinary results | built on the #641 stack |
| `Config` run deadline / exec default / Fetch default / verbosity | three independently owned contextual timeouts, absent unless configured, each read by exactly one consumer, and contextual verbosity — a boolean that is false unless configured, seeded by the command line and overridable for a lexical subtree, bounding nothing and owning no authority | built on this stack |
| terminal grid (`<Terminal.Grid>` / `<Terminal>`) | replaces the root foreground terminal with one provider-neutral composite whose statically declared direct panes begin concurrently, stay independently interactive, preserve their final statuses until the reader closes the composite, and tear down completely before document execution continues. A paired pane expands isolated document flow; a self-closing pane runs the host's default shell. The grid owns one foreground-terminal lease, each pane owns a separate pane-terminal lease, and a pane-scoped native launcher lets `<Session.Launch>` use that pane without weakening the independent Agent session coordinator. Core validates the complete row-major layout before provider contact, attaches only after every pane is ready, contains post-attach pane failures until close, and records the ordered provider-neutral outcomes. Completed replay contacts no terminal or Agent provider; partial replay rebuilds a fresh composite, restores completed panes as statuses, and continues incomplete pane effects under their existing durable identities. Provider commands, sockets, process topology and layout identifiers remain live-only inside the provider closure | defined for #717; #726 proves the persistent tmux pane-worker topology and its observable teardown boundary on macOS; first production provider is tmux in the Deno and compiled foreground hosts; controlled non-tmux provider proves the core contract; implementation unbuilt |
| terminal grid (`<Terminal.Grid>` / `<Terminal>`) | replaces the root foreground terminal with one provider-neutral composite whose statically declared direct panes begin concurrently, stay independently interactive, preserve their final statuses until the reader closes the composite, and tear down completely before document execution continues. A paired pane expands isolated document flow; a self-closing pane runs the host's default shell. The grid owns one foreground-terminal lease, each pane owns a separate pane-terminal lease, and a pane-scoped native launcher lets `<Session.Launch>` use that pane without weakening the independent Agent session coordinator. The launcher terminates at the composite's required provider-neutral pane-execution operation; the authored ordinal stays in core's live closure, and the native request carries no pane identity. Core validates the complete row-major layout before provider contact, attaches only after every pane is ready, contains post-attach pane failures until close, and records the ordered provider-neutral outcomes. Completed replay contacts no terminal or Agent provider; partial replay rebuilds a fresh composite, restores completed panes as statuses, and continues incomplete pane effects under their existing durable identities. Provider commands, sockets, process topology and layout identifiers remain live-only inside the provider closure | defined for #717; #726 proves the persistent tmux pane-worker topology and its observable teardown boundary on macOS; structure and layout built in #729, provider-neutral execution and durability in #730, pane claim admission and native-launch middleware in #731; the required composite pane-execution endpoint is specified and implemented in #732, which is what gives a pane's `<Session.Launch>` that pane's terminal rather than the root's; the controlled non-tmux provider remains the authority for core lifecycle semantics; the tmux provider is built in #732 for the Deno and compiled foreground hosts — one invocation-private server per grid, authenticated persistent pane workers carrying exact argv, cwd and environment outside tmux parsing, explicit row-major layout imposed by pane swaps, a required composite `launch()` that gives a pane's `<Session.Launch>` its own terminal rather than the root's, and one ordered teardown that proves worker quiescence, channel closure and server disappearance before the document continues; its evidence uses a fake tmux with real workers and real sockets, and real tmux behaviour on macOS remains #726's; Node and Bun catalog and validate the same grids and install neither the provider nor the process observer, refusing before pane start |
| native session launch (`<Session.Launch>` / `launchAgentSession()`) | prepares one durable coding-agent session from the rendered body of `<Session.Launch>` and hands the provider's native UI the terminal for that exact session, then continues the document after it exits. The body renders completely first and only what it rendered crosses as the instruction layer; the launch performs no model turn; at the root it takes the run's foreground-terminal lease before an agent is resolved, while a launch inside `<Terminal>` takes that pane's lease through its pane-scoped native launcher. A host with no applicable terminal refuses without probing for an installed CLI. A session is constructed once, by one of two mechanisms, and its create-once construction route says which. Where the provider returns the identity, the ACPX provider creates the session, installs the layer at creation, releases ACP ownership before the spawn, and marks its handle stale so a later `<Prompt>` reattaches. Where the adapter names its own sessions, it allocates the identity inside ownership before any process exists, the native process creates the session under that name from a private mode-0600 instruction file, and ACP creates nothing — the instruction text reaches neither argv nor environment, and the file is removed on success, failure and cancellation alike while ownership is still held. Neither route converts into the other, and which one governs is chosen by the first operation that consumes the placement rather than by the `<Session>` that made it: a fresh `<Session>` publishes no route and establishes nothing, so a `<Session.Launch>` nested inside one constructs the session it placed, while a first subscribed `<Prompt>` publishes ACP-first before it ensures and keeps that account even if the turn that follows is never accepted. An established route is validated eagerly by a later `<Session>`, and a launch meeting a published ACP-first route refuses before an identity exists. A `<Session>` or `<Prompt>` meeting a bound client-allocated route attaches under the route's exact identity; a legacy unbound route or an unavailable attachment capability refuses before a turn and creates no substitute conversation. Phases are retained as `agent_session_launch` records under one expansion identity — `prepared` before ownership is released, then `detached`, then `exited` — so a completed replay launches nothing, a replay holding only `prepared` proves the handoff never began and may still create under the retained identity, and one holding `detached` resumes and never falls back. The public route carries an opaque one-use launch request and answers nothing; authority to run and retain a phase is delivered to the installed provider directly, so neither a returned completion nor a rebuilt request authors a launch. Every operation that can act on an advertised session takes exclusive ownership under one natural key first, through a coordinator the host built and passed in; contention refuses instead of queueing, and an owner that never proved it stopped leaves a recovery tombstone. A host that cannot say who owns a session refuses every advertised operation, and one that cannot say how a session was constructed additionally refuses an agent that names its own — before any provider effect. Every private setup or child-creation failure is normalized to `process-creation-failed` with fixed provider-owned text, carrying no path, argv, environment or host message. No launch path discards persistent provider state. A client-allocated session is bound to one executable build: the build is observed inside ownership before an identity is allocated, the binding is published with the V2 route and retained beside the prepared record, the native child runs the exact observed path in place of the launcher name, and every later create, resume, attachment and incomplete replay reobserves and compares before a process, an ensure or a turn. A `<Session>` or `<Prompt>` meeting a bound client-native route attaches to it: it reobserves the build, requires any retained provider arrangement to assert that same conversation, calls ensure with the route identity as `resumeSessionId`, and requires the provider to report that identity before a turn — refusing on missing capability, build drift, missing history or a differing assertion without creating a substitute conversation. ACP runtimes are partitioned by resolved agent command and binding, each handle is closed by the partition that created it, and a bound partition is torn down when its last handle closes. A legacy V1 client-native route keeps exactly the released native-only behavior and never attaches | built on the #517 stack, extended by the #519 and #561 stacks; Deno and the compiled binary assemble the host — coordinator, route store and executable observer — and Node and Bun keep the same advertised names while assembling none of it, so every advertised operation refuses before provider work; `claude` is advertised for native launch after passing the client-allocated gate at Claude Code 2.1.241 on macOS arm64 (#520) and separately for client-native attachment after passing the native-to-ACP marker gate (#561), and Codex remains unadvertised because nothing has run its provider-returned claims against an installed Codex; `Agent.AddDir` is unbuilt |
| `<Fetch>` | performs one XMD-mediated HTTP read through contextual `API.Fetch`, admitting the whole request before transport, and retains the normalized request and the detached response as one `fetch` durable observation; capture decides whether a status is data or a failure, and the trusted host's destination ceiling sits below the component | built on the #456 stack; a generated fragment may name the pinned identity only for a request the trusted host stated exactly, on the #369 stack |
| `API.Files` | routes every document filesystem operation to the installed provider, with no host default and structural failure data. Its mandatory semantic operations include `ensureDirectory`, which recursively creates or adopts one directory and returns Unit; separately loaded copies compose through the stable Api name | built on the #227 stack; directory ensure added by #643 |
Expand Down
16 changes: 16 additions & 0 deletions packages/cli/src/agent-stack.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -22,6 +22,8 @@ import {
} from "@executablemd/core";
import type { AgentProviderFactory, PermissionMode } from "@executablemd/core";
import { installForegroundLauncher, env as readEnv } from "@executablemd/runtime";
import { unsupportedTerminalGrid } from "./terminal/host.ts";
import type { TerminalGridInstaller } from "./terminal/host.ts";
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
Expand DownExpand Up@@ -67,6 +69,14 @@ export interface AgentStack {
adapters: EmbeddedAdapters;
/** What this host states about machine-wide agent sessions, if anything. */
sessions?: MachineSessionAssembly;
/**
* What presents this host's terminal grids.
*
* Deno and the compiled binary supply the tmux provider; Node and Bun supply
* the one that installs none, so those runtimes describe and validate the
* same grids and open none of them.
*/
installTerminalGrid?: TerminalGridInstaller;
}

/**
Expand All@@ -80,6 +90,7 @@ export interface AgentStack {
export function* resolveAgentStack(
flags: AgentFlags,
sessions: MachineSessionAssembly | undefined,
installTerminalGrid?: TerminalGridInstaller,
): Operation<Result<AgentStack>> {
const config = resolveAgentConfig(flags);
if ("error" in config) {
Expand All@@ -96,6 +107,7 @@ export function* resolveAgentStack(
permissionMode: config.permissionMode,
adapters: createEmbeddedAdapters(DEFAULT_ADAPTER_ROOT),
...(sessions === undefined ? {} : { sessions }),
...(installTerminalGrid === undefined ? {} : { installTerminalGrid }),
});
}

Expand DownExpand Up@@ -157,4 +169,8 @@ export function* installRunAgentStack(stack: AgentStack): Operation<void> {
// document inspection and `xmd test` install no launcher, so a document that
// reaches <Session.Launch> under any of them refuses instead of spawning.
yield* installForegroundLauncher();
// And whatever presents this host's terminal grids, which on a host that
// presents none still opens the installation so a grid is validated — the
// refusal a document meets there is core's own.
yield* (stack.installTerminalGrid ?? unsupportedTerminalGrid)();
}
14 changes: 13 additions & 1 deletion packages/cli/src/cli.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -95,6 +95,8 @@ import { installWebComponents, installWebElicitation } from "@executablemd/web";
import { timebox } from "@effectionx/timebox";
import { timeout as runTimeout } from "@executablemd/runtime";
import { installRunAgentStack, resolveAgentStack } from "./agent-stack.ts";
import { unsupportedTerminalGrid } from "./terminal/host.ts";
import type { TerminalGridInstaller } from "./terminal/host.ts";
import { planComponentDeclaration } from "./plan-component.ts";
import { VERBOSE_REGISTRATION } from "./verbose-component.ts";
import type { AgentStack } from "./agent-stack.ts";
Expand DownExpand Up@@ -725,8 +727,9 @@ function* underRunDeadline(timeouts: RunTimeouts, body: () => Operation<void>):
function* settleAgentStack(
flags: AgentFlags,
sessions: MachineSessionAssembly | undefined,
installTerminalGrid: TerminalGridInstaller,
): Operation<AgentStack | undefined> {
const stack = yield* resolveAgentStack(flags, sessions);
const stack = yield* resolveAgentStack(flags, sessions, installTerminalGrid);
if (!stack.ok) {
console.error(stack.error.message);
yield* exit(1);
Expand DownExpand Up@@ -2155,6 +2158,7 @@ function* dispatch(
installRepositories: RepositoryInstaller,
workflowHost: WorkflowHost | undefined,
sessions: MachineSessionAssembly | undefined,
installTerminalGrid: TerminalGridInstaller,
): Operation<void> {
const propsPhase = yield* preparePropsPhase(helpRequest.args, evalFlags);

Expand DownExpand Up@@ -2229,6 +2233,7 @@ function* dispatch(
denyAll: config.denyAll,
},
sessions,
installTerminalGrid,
);
if (runStack === undefined) {
break;
Expand DownExpand Up@@ -2290,6 +2295,7 @@ function* dispatch(
denyAll: config.denyAll,
},
sessions,
installTerminalGrid,
);
if (planStack === undefined) {
break;
Expand DownExpand Up@@ -2565,6 +2571,10 @@ export function* runXmd(
// owns the session or which build it belongs to. A caller that names none
// gets no machine sessions at all, which is the ordinary ACP behaviour.
sessions?: MachineSessionAssembly,
// What presents a terminal grid on this host. Deno and the compiled binary
// supply the tmux provider; Node and Bun supply the one that installs none,
// so those runtimes describe and validate the same grids and open none.
installTerminalGrid: TerminalGridInstaller = unsupportedTerminalGrid,
): Operation<void> {
// Before every scanner, before command selection, and before anything reads a
// path. `prompt` names no command, and a first token that names none is a
Expand DownExpand Up@@ -2632,6 +2642,7 @@ export function* runXmd(
installRepositories,
workflowHost,
sessions,
installTerminalGrid,
);
}

Expand All@@ -2651,6 +2662,7 @@ export function* runXmd(
installRepositories,
workflowHost,
sessions,
installTerminalGrid,
),
);
}
Loading
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
40 changes: 30 additions & 10 deletions architecture.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -2847,15 +2847,35 @@ request to act. This preserves provider composition without letting a document
or replacement context mint terminal ownership.

A pane claim grants one interactive terminal at that ordinal, not an Agent
session. Core installs a pane-scoped native launcher that closes over the claim.
`<Session.Launch>` in that pane consequently reserves, flushes, and launches on
the pane terminal instead of competing for the root lease. Launches in
different panes may run concurrently; two interactive launches in one pane
cannot. Sequential launches in one paired pane remain ordinary composition.
The session coordinator is unchanged and independently authoritative, so two
panes attempting to own the same logical Agent session still contend and one
is refused. The provider starts a self-closing pane's host-configured default
shell under the same kind of pane claim.
session. Core installs a pane-scoped native launcher that closes over the claim,
the composite, and the authored ordinal. `<Session.Launch>` in that pane
consequently reserves and flushes the pane, then terminates native-launch
routing at this required provider-neutral composite operation:

```ts
launch(
ordinal: number,
request: NativeLaunchRequest,
spawned: () => void,
): Operation<NativeLaunchOutcome>;
```

The ordinal exists only in core's live closure and never enters the native or
Agent request. Once nearer native-launch middleware delegates, the pane launcher
calls the composite operation instead of the root foreground launcher. Nearer
middleware may still observe, wrap, refuse, or short-circuit the request. A
composite that cannot execute the pane request refuses explicitly; falling
through to the root would put the child on the wrong physical terminal. Root
`<Session.Launch>` retains its existing foreground-launch route unchanged.

Launches in different panes may run concurrently; two interactive launches in
one pane cannot. Sequential launches in one paired pane remain ordinary
composition. The session coordinator is unchanged and independently
authoritative, so two panes attempting to own the same logical Agent session
still contend and one is refused. The composite's separate `shell()` operation
starts a self-closing pane's host-configured default shell under the same kind
of pane claim; it remains separate because its executable is live host policy,
not an authored or Agent-provided native launch request.

Each claim also closes over one host-owned readiness latch. The pane-scoped
native launcher acknowledges it from the runtime's successful child-spawn event
Expand DownExpand Up@@ -4137,7 +4157,7 @@ Status is measured against main.
| testing harness (`<Execution>`) | runs another document as a real root under a production host profile, authorized by canonical `<Test>` alone: declarations installed before the root import, child output displayed progressively and collected only when asked, journal retention selected independently of observation, and the outcome published by the invocation's own terminal through a request public middleware composes around but cannot answer | built on the #454 stack for `host="run"`; the workflow profile and `<WorkflowRun>` are unbuilt, and a host that offers no workflow profile refuses them |
| nested run-profile Agent and elicitation declarations | lets one `<Execution host="run">` declare one child-scoped `<TestAgent>` scenario set and one non-delegating `<Answers>` matcher set; only frozen test data crosses the harness request, the trusted host constructs both providers inside the isolated child, siblings share no session or provider state, ordinary component shadowing remains in force, and the child journal retains only the selected Prompt and Elicit components' ordinary results | built on the #641 stack |
| `Config` run deadline / exec default / Fetch default / verbosity | three independently owned contextual timeouts, absent unless configured, each read by exactly one consumer, and contextual verbosity — a boolean that is false unless configured, seeded by the command line and overridable for a lexical subtree, bounding nothing and owning no authority | built on this stack |
| terminal grid (`<Terminal.Grid>` / `<Terminal>`) | replaces the root foreground terminal with one provider-neutral composite whose statically declared direct panes begin concurrently, stay independently interactive, preserve their final statuses until the reader closes the composite, and tear down completely before document execution continues. A paired pane expands isolated document flow; a self-closing pane runs the host's default shell. The grid owns one foreground-terminal lease, each pane owns a separate pane-terminal lease, and a pane-scoped native launcher lets `<Session.Launch>` use that pane without weakening the independent Agent session coordinator. Core validates the complete row-major layout before provider contact, attaches only after every pane is ready, contains post-attach pane failures until close, and records the ordered provider-neutral outcomes. Completed replay contacts no terminal or Agent provider; partial replay rebuilds a fresh composite, restores completed panes as statuses, and continues incomplete pane effects under their existing durable identities. Provider commands, sockets, process topology and layout identifiers remain live-only inside the provider closure | defined for #717; #726 proves the persistent tmux pane-worker topology and its observable teardown boundary on macOS; first production provider is tmux in the Deno and compiled foreground hosts; controlled non-tmux provider proves the core contract; implementation unbuilt |
| terminal grid (`<Terminal.Grid>` / `<Terminal>`) | replaces the root foreground terminal with one provider-neutral composite whose statically declared direct panes begin concurrently, stay independently interactive, preserve their final statuses until the reader closes the composite, and tear down completely before document execution continues. A paired pane expands isolated document flow; a self-closing pane runs the host's default shell. The grid owns one foreground-terminal lease, each pane owns a separate pane-terminal lease, and a pane-scoped native launcher lets `<Session.Launch>` use that pane without weakening the independent Agent session coordinator. The launcher terminates at the composite's required provider-neutral pane-execution operation; the authored ordinal stays in core's live closure, and the native request carries no pane identity. Core validates the complete row-major layout before provider contact, attaches only after every pane is ready, contains post-attach pane failures until close, and records the ordered provider-neutral outcomes. Completed replay contacts no terminal or Agent provider; partial replay rebuilds a fresh composite, restores completed panes as statuses, and continues incomplete pane effects under their existing durable identities. Provider commands, sockets, process topology and layout identifiers remain live-only inside the provider closure | defined for #717; #726 proves the persistent tmux pane-worker topology and its observable teardown boundary on macOS; structure and layout built in #729, provider-neutral execution and durability in #730, pane claim admission and native-launch middleware in #731; the required composite pane-execution endpoint is specified and implemented in #732, which is what gives a pane's `<Session.Launch>` that pane's terminal rather than the root's; the controlled non-tmux provider remains the authority for core lifecycle semantics; the tmux provider is built in #732 for the Deno and compiled foreground hosts — one invocation-private server per grid, authenticated persistent pane workers carrying exact argv, cwd and environment outside tmux parsing, explicit row-major layout imposed by pane swaps, a required composite `launch()` that gives a pane's `<Session.Launch>` its own terminal rather than the root's, and one ordered teardown that proves worker quiescence, channel closure and server disappearance before the document continues; its evidence uses a fake tmux with real workers and real sockets, and real tmux behaviour on macOS remains #726's; Node and Bun catalog and validate the same grids and install neither the provider nor the process observer, refusing before pane start |
| native session launch (`<Session.Launch>` / `launchAgentSession()`) | prepares one durable coding-agent session from the rendered body of `<Session.Launch>` and hands the provider's native UI the terminal for that exact session, then continues the document after it exits. The body renders completely first and only what it rendered crosses as the instruction layer; the launch performs no model turn; at the root it takes the run's foreground-terminal lease before an agent is resolved, while a launch inside `<Terminal>` takes that pane's lease through its pane-scoped native launcher. A host with no applicable terminal refuses without probing for an installed CLI. A session is constructed once, by one of two mechanisms, and its create-once construction route says which. Where the provider returns the identity, the ACPX provider creates the session, installs the layer at creation, releases ACP ownership before the spawn, and marks its handle stale so a later `<Prompt>` reattaches. Where the adapter names its own sessions, it allocates the identity inside ownership before any process exists, the native process creates the session under that name from a private mode-0600 instruction file, and ACP creates nothing — the instruction text reaches neither argv nor environment, and the file is removed on success, failure and cancellation alike while ownership is still held. Neither route converts into the other, and which one governs is chosen by the first operation that consumes the placement rather than by the `<Session>` that made it: a fresh `<Session>` publishes no route and establishes nothing, so a `<Session.Launch>` nested inside one constructs the session it placed, while a first subscribed `<Prompt>` publishes ACP-first before it ensures and keeps that account even if the turn that follows is never accepted. An established route is validated eagerly by a later `<Session>`, and a launch meeting a published ACP-first route refuses before an identity exists. A `<Session>` or `<Prompt>` meeting a bound client-allocated route attaches under the route's exact identity; a legacy unbound route or an unavailable attachment capability refuses before a turn and creates no substitute conversation. Phases are retained as `agent_session_launch` records under one expansion identity — `prepared` before ownership is released, then `detached`, then `exited` — so a completed replay launches nothing, a replay holding only `prepared` proves the handoff never began and may still create under the retained identity, and one holding `detached` resumes and never falls back. The public route carries an opaque one-use launch request and answers nothing; authority to run and retain a phase is delivered to the installed provider directly, so neither a returned completion nor a rebuilt request authors a launch. Every operation that can act on an advertised session takes exclusive ownership under one natural key first, through a coordinator the host built and passed in; contention refuses instead of queueing, and an owner that never proved it stopped leaves a recovery tombstone. A host that cannot say who owns a session refuses every advertised operation, and one that cannot say how a session was constructed additionally refuses an agent that names its own — before any provider effect. Every private setup or child-creation failure is normalized to `process-creation-failed` with fixed provider-owned text, carrying no path, argv, environment or host message. No launch path discards persistent provider state. A client-allocated session is bound to one executable build: the build is observed inside ownership before an identity is allocated, the binding is published with the V2 route and retained beside the prepared record, the native child runs the exact observed path in place of the launcher name, and every later create, resume, attachment and incomplete replay reobserves and compares before a process, an ensure or a turn. A `<Session>` or `<Prompt>` meeting a bound client-native route attaches to it: it reobserves the build, requires any retained provider arrangement to assert that same conversation, calls ensure with the route identity as `resumeSessionId`, and requires the provider to report that identity before a turn — refusing on missing capability, build drift, missing history or a differing assertion without creating a substitute conversation. ACP runtimes are partitioned by resolved agent command and binding, each handle is closed by the partition that created it, and a bound partition is torn down when its last handle closes. A legacy V1 client-native route keeps exactly the released native-only behavior and never attaches | built on the #517 stack, extended by the #519 and #561 stacks; Deno and the compiled binary assemble the host — coordinator, route store and executable observer — and Node and Bun keep the same advertised names while assembling none of it, so every advertised operation refuses before provider work; `claude` is advertised for native launch after passing the client-allocated gate at Claude Code 2.1.241 on macOS arm64 (#520) and separately for client-native attachment after passing the native-to-ACP marker gate (#561), and Codex remains unadvertised because nothing has run its provider-returned claims against an installed Codex; `Agent.AddDir` is unbuilt |
| `<Fetch>` | performs one XMD-mediated HTTP read through contextual `API.Fetch`, admitting the whole request before transport, and retains the normalized request and the detached response as one `fetch` durable observation; capture decides whether a status is data or a failure, and the trusted host's destination ceiling sits below the component | built on the #456 stack; a generated fragment may name the pinned identity only for a request the trusted host stated exactly, on the #369 stack |
| `API.Files` | routes every document filesystem operation to the installed provider, with no host default and structural failure data. Its mandatory semantic operations include `ensureDirectory`, which recursively creates or adopts one directory and returns Unit; separately loaded copies compose through the stable Api name | built on the #227 stack; directory ensure added by #643 |
Expand Down
16 changes: 16 additions & 0 deletions packages/cli/src/agent-stack.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -22,6 +22,8 @@ import {
} from "@executablemd/core";
import type { AgentProviderFactory, PermissionMode } from "@executablemd/core";
import { installForegroundLauncher, env as readEnv } from "@executablemd/runtime";
import { unsupportedTerminalGrid } from "./terminal/host.ts";
import type { TerminalGridInstaller } from "./terminal/host.ts";
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
Expand DownExpand Up@@ -67,6 +69,14 @@ export interface AgentStack {
adapters: EmbeddedAdapters;
/** What this host states about machine-wide agent sessions, if anything. */
sessions?: MachineSessionAssembly;
/**
* What presents this host's terminal grids.
*
* Deno and the compiled binary supply the tmux provider; Node and Bun supply
* the one that installs none, so those runtimes describe and validate the
* same grids and open none of them.
*/
installTerminalGrid?: TerminalGridInstaller;
}

/**
Expand All@@ -80,6 +90,7 @@ export interface AgentStack {
export function* resolveAgentStack(
flags: AgentFlags,
sessions: MachineSessionAssembly | undefined,
installTerminalGrid?: TerminalGridInstaller,
): Operation<Result<AgentStack>> {
const config = resolveAgentConfig(flags);
if ("error" in config) {
Expand All@@ -96,6 +107,7 @@ export function* resolveAgentStack(
permissionMode: config.permissionMode,
adapters: createEmbeddedAdapters(DEFAULT_ADAPTER_ROOT),
...(sessions === undefined ? {} : { sessions }),
...(installTerminalGrid === undefined ? {} : { installTerminalGrid }),
});
}

Expand DownExpand Up@@ -157,4 +169,8 @@ export function* installRunAgentStack(stack: AgentStack): Operation<void> {
// document inspection and `xmd test` install no launcher, so a document that
// reaches <Session.Launch> under any of them refuses instead of spawning.
yield* installForegroundLauncher();
// And whatever presents this host's terminal grids, which on a host that
// presents none still opens the installation so a grid is validated — the
// refusal a document meets there is core's own.
yield* (stack.installTerminalGrid ?? unsupportedTerminalGrid)();
}
14 changes: 13 additions & 1 deletion packages/cli/src/cli.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -95,6 +95,8 @@ import { installWebComponents, installWebElicitation } from "@executablemd/web";
import { timebox } from "@effectionx/timebox";
import { timeout as runTimeout } from "@executablemd/runtime";
import { installRunAgentStack, resolveAgentStack } from "./agent-stack.ts";
import { unsupportedTerminalGrid } from "./terminal/host.ts";
import type { TerminalGridInstaller } from "./terminal/host.ts";
import { planComponentDeclaration } from "./plan-component.ts";
import { VERBOSE_REGISTRATION } from "./verbose-component.ts";
import type { AgentStack } from "./agent-stack.ts";
Expand DownExpand Up@@ -725,8 +727,9 @@ function* underRunDeadline(timeouts: RunTimeouts, body: () => Operation<void>):
function* settleAgentStack(
flags: AgentFlags,
sessions: MachineSessionAssembly | undefined,
installTerminalGrid: TerminalGridInstaller,
): Operation<AgentStack | undefined> {
const stack = yield* resolveAgentStack(flags, sessions);
const stack = yield* resolveAgentStack(flags, sessions, installTerminalGrid);
if (!stack.ok) {
console.error(stack.error.message);
yield* exit(1);
Expand DownExpand Up@@ -2155,6 +2158,7 @@ function* dispatch(
installRepositories: RepositoryInstaller,
workflowHost: WorkflowHost | undefined,
sessions: MachineSessionAssembly | undefined,
installTerminalGrid: TerminalGridInstaller,
): Operation<void> {
const propsPhase = yield* preparePropsPhase(helpRequest.args, evalFlags);

Expand DownExpand Up@@ -2229,6 +2233,7 @@ function* dispatch(
denyAll: config.denyAll,
},
sessions,
installTerminalGrid,
);
if (runStack === undefined) {
break;
Expand DownExpand Up@@ -2290,6 +2295,7 @@ function* dispatch(
denyAll: config.denyAll,
},
sessions,
installTerminalGrid,
);
if (planStack === undefined) {
break;
Expand DownExpand Up@@ -2565,6 +2571,10 @@ export function* runXmd(
// owns the session or which build it belongs to. A caller that names none
// gets no machine sessions at all, which is the ordinary ACP behaviour.
sessions?: MachineSessionAssembly,
// What presents a terminal grid on this host. Deno and the compiled binary
// supply the tmux provider; Node and Bun supply the one that installs none,
// so those runtimes describe and validate the same grids and open none.
installTerminalGrid: TerminalGridInstaller = unsupportedTerminalGrid,
): Operation<void> {
// Before every scanner, before command selection, and before anything reads a
// path. `prompt` names no command, and a first token that names none is a
Expand DownExpand Up@@ -2632,6 +2642,7 @@ export function* runXmd(
installRepositories,
workflowHost,
sessions,
installTerminalGrid,
);
}

Expand All@@ -2651,6 +2662,7 @@ export function* runXmd(
installRepositories,
workflowHost,
sessions,
installTerminalGrid,
),
);
}
Loading
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
40 changes: 30 additions & 10 deletions architecture.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -2847,15 +2847,35 @@ request to act. This preserves provider composition without letting a document
or replacement context mint terminal ownership.

A pane claim grants one interactive terminal at that ordinal, not an Agent
session. Core installs a pane-scoped native launcher that closes over the claim.
`<Session.Launch>` in that pane consequently reserves, flushes, and launches on
the pane terminal instead of competing for the root lease. Launches in
different panes may run concurrently; two interactive launches in one pane
cannot. Sequential launches in one paired pane remain ordinary composition.
The session coordinator is unchanged and independently authoritative, so two
panes attempting to own the same logical Agent session still contend and one
is refused. The provider starts a self-closing pane's host-configured default
shell under the same kind of pane claim.
session. Core installs a pane-scoped native launcher that closes over the claim,
the composite, and the authored ordinal. `<Session.Launch>` in that pane
consequently reserves and flushes the pane, then terminates native-launch
routing at this required provider-neutral composite operation:

```ts
launch(
ordinal: number,
request: NativeLaunchRequest,
spawned: () => void,
): Operation<NativeLaunchOutcome>;
```

The ordinal exists only in core's live closure and never enters the native or
Agent request. Once nearer native-launch middleware delegates, the pane launcher
calls the composite operation instead of the root foreground launcher. Nearer
middleware may still observe, wrap, refuse, or short-circuit the request. A
composite that cannot execute the pane request refuses explicitly; falling
through to the root would put the child on the wrong physical terminal. Root
`<Session.Launch>` retains its existing foreground-launch route unchanged.

Launches in different panes may run concurrently; two interactive launches in
one pane cannot. Sequential launches in one paired pane remain ordinary
composition. The session coordinator is unchanged and independently
authoritative, so two panes attempting to own the same logical Agent session
still contend and one is refused. The composite's separate `shell()` operation
starts a self-closing pane's host-configured default shell under the same kind
of pane claim; it remains separate because its executable is live host policy,
not an authored or Agent-provided native launch request.

Each claim also closes over one host-owned readiness latch. The pane-scoped
native launcher acknowledges it from the runtime's successful child-spawn event
Expand DownExpand Up@@ -4137,7 +4157,7 @@ Status is measured against main.
| testing harness (`<Execution>`) | runs another document as a real root under a production host profile, authorized by canonical `<Test>` alone: declarations installed before the root import, child output displayed progressively and collected only when asked, journal retention selected independently of observation, and the outcome published by the invocation's own terminal through a request public middleware composes around but cannot answer | built on the #454 stack for `host="run"`; the workflow profile and `<WorkflowRun>` are unbuilt, and a host that offers no workflow profile refuses them |
| nested run-profile Agent and elicitation declarations | lets one `<Execution host="run">` declare one child-scoped `<TestAgent>` scenario set and one non-delegating `<Answers>` matcher set; only frozen test data crosses the harness request, the trusted host constructs both providers inside the isolated child, siblings share no session or provider state, ordinary component shadowing remains in force, and the child journal retains only the selected Prompt and Elicit components' ordinary results | built on the #641 stack |
| `Config` run deadline / exec default / Fetch default / verbosity | three independently owned contextual timeouts, absent unless configured, each read by exactly one consumer, and contextual verbosity — a boolean that is false unless configured, seeded by the command line and overridable for a lexical subtree, bounding nothing and owning no authority | built on this stack |
| terminal grid (`<Terminal.Grid>` / `<Terminal>`) | replaces the root foreground terminal with one provider-neutral composite whose statically declared direct panes begin concurrently, stay independently interactive, preserve their final statuses until the reader closes the composite, and tear down completely before document execution continues. A paired pane expands isolated document flow; a self-closing pane runs the host's default shell. The grid owns one foreground-terminal lease, each pane owns a separate pane-terminal lease, and a pane-scoped native launcher lets `<Session.Launch>` use that pane without weakening the independent Agent session coordinator. Core validates the complete row-major layout before provider contact, attaches only after every pane is ready, contains post-attach pane failures until close, and records the ordered provider-neutral outcomes. Completed replay contacts no terminal or Agent provider; partial replay rebuilds a fresh composite, restores completed panes as statuses, and continues incomplete pane effects under their existing durable identities. Provider commands, sockets, process topology and layout identifiers remain live-only inside the provider closure | defined for #717; #726 proves the persistent tmux pane-worker topology and its observable teardown boundary on macOS; first production provider is tmux in the Deno and compiled foreground hosts; controlled non-tmux provider proves the core contract; implementation unbuilt |
| terminal grid (`<Terminal.Grid>` / `<Terminal>`) | replaces the root foreground terminal with one provider-neutral composite whose statically declared direct panes begin concurrently, stay independently interactive, preserve their final statuses until the reader closes the composite, and tear down completely before document execution continues. A paired pane expands isolated document flow; a self-closing pane runs the host's default shell. The grid owns one foreground-terminal lease, each pane owns a separate pane-terminal lease, and a pane-scoped native launcher lets `<Session.Launch>` use that pane without weakening the independent Agent session coordinator. The launcher terminates at the composite's required provider-neutral pane-execution operation; the authored ordinal stays in core's live closure, and the native request carries no pane identity. Core validates the complete row-major layout before provider contact, attaches only after every pane is ready, contains post-attach pane failures until close, and records the ordered provider-neutral outcomes. Completed replay contacts no terminal or Agent provider; partial replay rebuilds a fresh composite, restores completed panes as statuses, and continues incomplete pane effects under their existing durable identities. Provider commands, sockets, process topology and layout identifiers remain live-only inside the provider closure | defined for #717; #726 proves the persistent tmux pane-worker topology and its observable teardown boundary on macOS; structure and layout built in #729, provider-neutral execution and durability in #730, pane claim admission and native-launch middleware in #731; the required composite pane-execution endpoint is specified and implemented in #732, which is what gives a pane's `<Session.Launch>` that pane's terminal rather than the root's; the controlled non-tmux provider remains the authority for core lifecycle semantics; the tmux provider is built in #732 for the Deno and compiled foreground hosts — one invocation-private server per grid, authenticated persistent pane workers carrying exact argv, cwd and environment outside tmux parsing, explicit row-major layout imposed by pane swaps, a required composite `launch()` that gives a pane's `<Session.Launch>` its own terminal rather than the root's, and one ordered teardown that proves worker quiescence, channel closure and server disappearance before the document continues; its evidence uses a fake tmux with real workers and real sockets, and real tmux behaviour on macOS remains #726's; Node and Bun catalog and validate the same grids and install neither the provider nor the process observer, refusing before pane start |
| native session launch (`<Session.Launch>` / `launchAgentSession()`) | prepares one durable coding-agent session from the rendered body of `<Session.Launch>` and hands the provider's native UI the terminal for that exact session, then continues the document after it exits. The body renders completely first and only what it rendered crosses as the instruction layer; the launch performs no model turn; at the root it takes the run's foreground-terminal lease before an agent is resolved, while a launch inside `<Terminal>` takes that pane's lease through its pane-scoped native launcher. A host with no applicable terminal refuses without probing for an installed CLI. A session is constructed once, by one of two mechanisms, and its create-once construction route says which. Where the provider returns the identity, the ACPX provider creates the session, installs the layer at creation, releases ACP ownership before the spawn, and marks its handle stale so a later `<Prompt>` reattaches. Where the adapter names its own sessions, it allocates the identity inside ownership before any process exists, the native process creates the session under that name from a private mode-0600 instruction file, and ACP creates nothing — the instruction text reaches neither argv nor environment, and the file is removed on success, failure and cancellation alike while ownership is still held. Neither route converts into the other, and which one governs is chosen by the first operation that consumes the placement rather than by the `<Session>` that made it: a fresh `<Session>` publishes no route and establishes nothing, so a `<Session.Launch>` nested inside one constructs the session it placed, while a first subscribed `<Prompt>` publishes ACP-first before it ensures and keeps that account even if the turn that follows is never accepted. An established route is validated eagerly by a later `<Session>`, and a launch meeting a published ACP-first route refuses before an identity exists. A `<Session>` or `<Prompt>` meeting a bound client-allocated route attaches under the route's exact identity; a legacy unbound route or an unavailable attachment capability refuses before a turn and creates no substitute conversation. Phases are retained as `agent_session_launch` records under one expansion identity — `prepared` before ownership is released, then `detached`, then `exited` — so a completed replay launches nothing, a replay holding only `prepared` proves the handoff never began and may still create under the retained identity, and one holding `detached` resumes and never falls back. The public route carries an opaque one-use launch request and answers nothing; authority to run and retain a phase is delivered to the installed provider directly, so neither a returned completion nor a rebuilt request authors a launch. Every operation that can act on an advertised session takes exclusive ownership under one natural key first, through a coordinator the host built and passed in; contention refuses instead of queueing, and an owner that never proved it stopped leaves a recovery tombstone. A host that cannot say who owns a session refuses every advertised operation, and one that cannot say how a session was constructed additionally refuses an agent that names its own — before any provider effect. Every private setup or child-creation failure is normalized to `process-creation-failed` with fixed provider-owned text, carrying no path, argv, environment or host message. No launch path discards persistent provider state. A client-allocated session is bound to one executable build: the build is observed inside ownership before an identity is allocated, the binding is published with the V2 route and retained beside the prepared record, the native child runs the exact observed path in place of the launcher name, and every later create, resume, attachment and incomplete replay reobserves and compares before a process, an ensure or a turn. A `<Session>` or `<Prompt>` meeting a bound client-native route attaches to it: it reobserves the build, requires any retained provider arrangement to assert that same conversation, calls ensure with the route identity as `resumeSessionId`, and requires the provider to report that identity before a turn — refusing on missing capability, build drift, missing history or a differing assertion without creating a substitute conversation. ACP runtimes are partitioned by resolved agent command and binding, each handle is closed by the partition that created it, and a bound partition is torn down when its last handle closes. A legacy V1 client-native route keeps exactly the released native-only behavior and never attaches | built on the #517 stack, extended by the #519 and #561 stacks; Deno and the compiled binary assemble the host — coordinator, route store and executable observer — and Node and Bun keep the same advertised names while assembling none of it, so every advertised operation refuses before provider work; `claude` is advertised for native launch after passing the client-allocated gate at Claude Code 2.1.241 on macOS arm64 (#520) and separately for client-native attachment after passing the native-to-ACP marker gate (#561), and Codex remains unadvertised because nothing has run its provider-returned claims against an installed Codex; `Agent.AddDir` is unbuilt |
| `<Fetch>` | performs one XMD-mediated HTTP read through contextual `API.Fetch`, admitting the whole request before transport, and retains the normalized request and the detached response as one `fetch` durable observation; capture decides whether a status is data or a failure, and the trusted host's destination ceiling sits below the component | built on the #456 stack; a generated fragment may name the pinned identity only for a request the trusted host stated exactly, on the #369 stack |
| `API.Files` | routes every document filesystem operation to the installed provider, with no host default and structural failure data. Its mandatory semantic operations include `ensureDirectory`, which recursively creates or adopts one directory and returns Unit; separately loaded copies compose through the stable Api name | built on the #227 stack; directory ensure added by #643 |
Expand Down
16 changes: 16 additions & 0 deletions packages/cli/src/agent-stack.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -22,6 +22,8 @@ import {
} from "@executablemd/core";
import type { AgentProviderFactory, PermissionMode } from "@executablemd/core";
import { installForegroundLauncher, env as readEnv } from "@executablemd/runtime";
import { unsupportedTerminalGrid } from "./terminal/host.ts";
import type { TerminalGridInstaller } from "./terminal/host.ts";
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
Expand DownExpand Up@@ -67,6 +69,14 @@ export interface AgentStack {
adapters: EmbeddedAdapters;
/** What this host states about machine-wide agent sessions, if anything. */
sessions?: MachineSessionAssembly;
/**
* What presents this host's terminal grids.
*
* Deno and the compiled binary supply the tmux provider; Node and Bun supply
* the one that installs none, so those runtimes describe and validate the
* same grids and open none of them.
*/
installTerminalGrid?: TerminalGridInstaller;
}

/**
Expand All@@ -80,6 +90,7 @@ export interface AgentStack {
export function* resolveAgentStack(
flags: AgentFlags,
sessions: MachineSessionAssembly | undefined,
installTerminalGrid?: TerminalGridInstaller,
): Operation<Result<AgentStack>> {
const config = resolveAgentConfig(flags);
if ("error" in config) {
Expand All@@ -96,6 +107,7 @@ export function* resolveAgentStack(
permissionMode: config.permissionMode,
adapters: createEmbeddedAdapters(DEFAULT_ADAPTER_ROOT),
...(sessions === undefined ? {} : { sessions }),
...(installTerminalGrid === undefined ? {} : { installTerminalGrid }),
});
}

Expand DownExpand Up@@ -157,4 +169,8 @@ export function* installRunAgentStack(stack: AgentStack): Operation<void> {
// document inspection and `xmd test` install no launcher, so a document that
// reaches <Session.Launch> under any of them refuses instead of spawning.
yield* installForegroundLauncher();
// And whatever presents this host's terminal grids, which on a host that
// presents none still opens the installation so a grid is validated — the
// refusal a document meets there is core's own.
yield* (stack.installTerminalGrid ?? unsupportedTerminalGrid)();
}
14 changes: 13 additions & 1 deletion packages/cli/src/cli.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -95,6 +95,8 @@ import { installWebComponents, installWebElicitation } from "@executablemd/web";
import { timebox } from "@effectionx/timebox";
import { timeout as runTimeout } from "@executablemd/runtime";
import { installRunAgentStack, resolveAgentStack } from "./agent-stack.ts";
import { unsupportedTerminalGrid } from "./terminal/host.ts";
import type { TerminalGridInstaller } from "./terminal/host.ts";
import { planComponentDeclaration } from "./plan-component.ts";
import { VERBOSE_REGISTRATION } from "./verbose-component.ts";
import type { AgentStack } from "./agent-stack.ts";
Expand DownExpand Up@@ -725,8 +727,9 @@ function* underRunDeadline(timeouts: RunTimeouts, body: () => Operation<void>):
function* settleAgentStack(
flags: AgentFlags,
sessions: MachineSessionAssembly | undefined,
installTerminalGrid: TerminalGridInstaller,
): Operation<AgentStack | undefined> {
const stack = yield* resolveAgentStack(flags, sessions);
const stack = yield* resolveAgentStack(flags, sessions, installTerminalGrid);
if (!stack.ok) {
console.error(stack.error.message);
yield* exit(1);
Expand DownExpand Up@@ -2155,6 +2158,7 @@ function* dispatch(
installRepositories: RepositoryInstaller,
workflowHost: WorkflowHost | undefined,
sessions: MachineSessionAssembly | undefined,
installTerminalGrid: TerminalGridInstaller,
): Operation<void> {
const propsPhase = yield* preparePropsPhase(helpRequest.args, evalFlags);

Expand DownExpand Up@@ -2229,6 +2233,7 @@ function* dispatch(
denyAll: config.denyAll,
},
sessions,
installTerminalGrid,
);
if (runStack === undefined) {
break;
Expand DownExpand Up@@ -2290,6 +2295,7 @@ function* dispatch(
denyAll: config.denyAll,
},
sessions,
installTerminalGrid,
);
if (planStack === undefined) {
break;
Expand DownExpand Up@@ -2565,6 +2571,10 @@ export function* runXmd(
// owns the session or which build it belongs to. A caller that names none
// gets no machine sessions at all, which is the ordinary ACP behaviour.
sessions?: MachineSessionAssembly,
// What presents a terminal grid on this host. Deno and the compiled binary
// supply the tmux provider; Node and Bun supply the one that installs none,
// so those runtimes describe and validate the same grids and open none.
installTerminalGrid: TerminalGridInstaller = unsupportedTerminalGrid,
): Operation<void> {
// Before every scanner, before command selection, and before anything reads a
// path. `prompt` names no command, and a first token that names none is a
Expand DownExpand Up@@ -2632,6 +2642,7 @@ export function* runXmd(
installRepositories,
workflowHost,
sessions,
installTerminalGrid,
);
}

Expand All@@ -2651,6 +2662,7 @@ export function* runXmd(
installRepositories,
workflowHost,
sessions,
installTerminalGrid,
),
);
}
Loading
Loading