Evaluate Agent-generated XMD through a constrained allowlist #369

Description

@taras

Story

As a workflow author, I want untrusted XMD returned by an Agent to be validated and executed by the host, so the Agent can request observations and propose changes without receiving direct Workspace or tool authority.

Delivery status

Slice 1: read-only observation admission — delivered

PR #497 supplies the provider-neutral generated-XMD admission boundary. PR #550 supplies the authored workflow seam:

<Evaluatesource={turn.source}as="observation" />

The trusted workflow host supplies the exact candidate source, immutable pinned observation identities, the retained Workspace roots and selected root, and exact request ceilings such as allowed HTTP requests. The complete fragment is preflighted before its first effect. Admitted observations execute through ordinary durable XMD effects, and the filtered source, decision, observation values, rendered output, identities, roots, and request policy are retained for replay.

<Evaluate> currently accepts only source; omitting the new allow prop below preserves this delivered behavior exactly.

Slice 2: Workspace mutation admission

Extend the same <Evaluate> seam rather than adding a second construct:

<!-- `allow` omitted: read-only, as today -->
<Evaluatesource={turn.source}as="observation" />
<!-- generated mutations confined to the run-owned Workspace -->
<Evaluate source={proposal.changes} allow={["write"]} />

allow is an optional non-empty set drawn from the closed values read and write. Omission means exactly ["read"]. Duplicate or unknown values are refused before the candidate source is parsed. The normalized set is retained with the admission.

The two values describe effect classes, not component identities:

  • read selects observation-only identities. It includes core's pinned self-closing File:read identity and includes pinned Fetch only when the host supplied at least one exact request ceiling. Fetch remains the existing non-mutating GET/HEAD component; there is no mutating Fetch form.
  • write selects mutations confined to the run-owned Workspace. This initial slice admits the exact pinned identities for lexical Dir and paired File:write. <File.Delete> and its pinned identity are owned by Add a contextual File.Delete component for contained file removal #567 and join the standard host policy only after both the ordinary component and this admission slice exist. The initial table excludes local Git even though those effects are also Workspace-local; Git-host, Issue, process, eval, exec, native-command, credential, and other external effects are outside the class entirely.

An authored document may request both classes, but same-name forms remain distinct identities. A self-closing <File /> resolves only to File:read; a paired <File>…</File> resolves only to File:write. Admission therefore resolves by the exact pinned definition and admitted form, not by the word File. A same-name repository component, registration, bundle member, or middleware answer satisfies neither identity.

The complete fragment is still preflighted before its first effect. A component, form, request, expression, interpolation, import, code block, or native execution outside the selected classes refuses the whole fragment before any generated effect. Admitted mutations then execute through their ordinary contextual providers, Workspace transactions, durability, and return semantics. Generated source receives no special mutation API.

Binding and output contract

allow and as are independent. allow selects effect classes from host policy; as is the language's ordinary caller-owned binding for the value <Evaluate> returns. A read-, write-, or mixed-policy invocation may be bound:

<Evaluate source={proposal.changes} allow={["write"]} as="evaluation" />

This does not grant authority and does not expose the caller's binding environment to generated source. <Evaluate> remains an ordinary function-component boundary: its return crosses to the caller only when the caller writes as, and an unbound non-string return renders nothing.

Slice 2 does not invent a generic mutation results collection. <Evaluate> keeps its delivered return shape:

{
"observations": [{ "name": "File", "value": "..." }],
"output": "..."
}

observations contains the ordinary values of admitted read identities, in invocation order. output contains what the generated fragment rendered. Admitted write identities do not acquire synthetic receipts or entries merely because they mutated the Workspace; their durable effect records remain the authoritative account. A write-only fragment therefore normally binds { "observations": [], "output": "" } with this slice's Dir and paired File:write table.

The generated-XMD preflight's delivered refusal of as inside generated source, and of generated binding reads, remains unchanged in this mutation slice. That restriction is separate from ordinary component semantics: authored write operations such as <Git.Commit ... as="commit" /> continue to bind their ordinary returns, and <Evaluate ... as="evaluation" /> remains valid for every allow selection. Exporting named fragment-local bindings would be a separate generated-language contract, including replay and collision semantics; this slice does not need it for #181.

Authored approval

Admission does not prompt and does not approve. Approval is ordinary authored control flow before a write-enabled <Evaluate> is reached.

For PR #181, the existing UserCheckpoint authorizes the converged plan before Implementation begins. That authorization is sufficient for the implementation Agent's generated Workspace changes; the workflow does not require a second approval of the literal fragment. A workflow that wants exact-fragment approval places its own elicitation or suspension gate immediately before <Evaluate allow={["write"]}>.

If the authored approval branch is not taken, the write-enabled <Evaluate> is never invoked and no generated admission or mutation exists.

Host policy and authority

Generated XMD is untrusted input. The Agent output carries data, never authorization. Only the trusted workflow host installs generated-XMD policy, as immutable values captured before any installation, middleware, or document code runs.

The host policy contains separate read and optional write tables. Each table holds exact pinned definitions and their form/request constraints. Slice 2 makes the standard Deno workflow profile supply the Workspace-local write table with lexical Dir and paired File:write. The profile supplies no external-write or execution table. Another host that supplied no write table refuses allow={["write"]} before parsing the generated source. #567 owns the ordinary <File.Delete> component and its later pinned-policy integration; deletion is not part of this initial slice, and #181 cannot consume it until #567 is delivered.

The authored allow prop selects a subset of that already-installed policy. It can narrow or request a class but cannot create a pinned identity, add a root, authorize a destination, attach a credential, or widen a request. Public middleware, document props, eval bindings, repository components, generated names, and another loaded package copy cannot manufacture admission.

Generated source receives none of the policy values. Network destination, method, headers, timeout, retained roots, selected root, exact component definitions, and credentials remain host-owned ceilings beneath the read and write labels.

Replay contract

One ordinary durable generated_xmd admission retains:

  • the normalized allow selection;
  • the exact admitted source;
  • retained roots and selected root;
  • every exact pinned identity and admitted form the fragment named; and
  • normalized request constraints.

A continuation compares that policy whole and exactly before one generated component is invoked. A changed class, root, selected root, component definition, admitted form, or request ceiling refuses without performing an effect. The retained source, rather than a later caller's candidate, is what expands. Completed replay asks neither the Agent nor a provider to repeat completed work.

Integration with the workflow Agent

#302 supplies the retained Agent session and authored multi-turn loop:

  1. The Agent returns an observation fragment.
  2. <Evaluate> defaults to read, admits it under the host's read table, and returns its observation value.
  3. The authored <Evaluate ... as="observation" /> invocation binds that value for a later Prompt in the same Agent session.
  4. The Agent eventually returns one final proposal as source data.
  5. Authored approval control reaches <Evaluate allow={["write"]}>.
  6. The host intersects that request with its immutable write table and admits the proposal or refuses it whole.

The Agent receives no writable channel through this integration.

Acceptance

Existing read behavior

  • <Evaluate source={source} /> is byte-for-byte and behaviorally compatible with the delivered read-only seam.
  • Omitted allow normalizes to ["read"] and retains that selection.
  • Pinned self-closing File:read and exact bounded Fetch continue to work; paired File, mutating/unknown HTTP methods, and requests outside the exact Fetch ceiling perform nothing.
  • Bound read evaluation keeps the delivered { observations, output } value; unbound evaluation renders nothing.

Write behavior

  • allow accepts only a non-empty, duplicate-free set of read and write; invalid policy is refused before candidate parsing or retention.
  • allow={["write"]} fails before candidate parsing when the host installed no write table.
  • The host's immutable write table, never the prop or generated source, selects the exact pinned mutation identities.
  • A paired pinned File:write can mutate the selected run-owned Workspace through the ordinary Files provider and effect transaction.
  • Self-closing and paired forms of the same component resolve to different pinned identities; selecting only one class never admits the other form.
  • The first consumer admits only the Workspace-local forms its host supplied. Generated Git push, PullRequest, Issue upsert, process, eval, exec, native command, credential, and arbitrary network effects remain refused.
  • Mixed admitted and unadmitted syntax produces no generated effect.
  • Rejected mutation syntax or authority produces no partial effect.
  • Admitted mutations use their ordinary providers, transactions, and durable records; the evaluator creates no generic mutation result or receipt.
  • as on <Evaluate> is valid for read, write, and mixed selections and binds the same { observations, output } shape. It neither widens admission nor exports the caller's bindings.
  • Authored refusal before the write-enabled Evaluate produces no generated admission or mutation.
  • Continuation compares the normalized classes, identities, forms, roots, and request ceilings before performing anything; completed replay repeats no mutation.
  • Identity substitution, same-name repository components, middleware replacement, another loaded copy, and a changed host policy cannot turn a read identity into a write identity or manufacture either one.

Dependencies

Out of scope

  • Direct Agent filesystem, terminal, MCP, or network access.
  • ACP additionalDirectories.
  • A document prop or public component that grants generated source authority; allow only narrows the host's captured policy.
  • Mutating HTTP through <Fetch>.
  • Generated local Git operations, Git-host mutations, Issue upserts, process execution, eval/exec, native commands, credentials, or an unspecified external-write class.
  • Workflow-bundled Markdown component admission.
  • <File.Delete> and its pinned write-policy integration; Add a contextual File.Delete component for contained file removal #567 owns both after this initial slice.
  • Generated-source as, binding reads, or exporting a fragment-local binding environment.
  • Synthetic mutation results or receipts.
  • Partial execution before complete-fragment preflight.
  • A hidden approval prompt inside <Evaluate>.
  • Mandatory exact-fragment approval after a user already authorized the converged plan.
  • Unattended approval of mutation proposals.
  • Portable ACP adapter-level no-tool enforcement; Require ACP adapters to enforce tool-free workflow Agent sessions #496 owns it.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions

      , '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

      Evaluate Agent-generated XMD through a constrained allowlist #369

      Description

      @taras

      Story

      As a workflow author, I want untrusted XMD returned by an Agent to be validated and executed by the host, so the Agent can request observations and propose changes without receiving direct Workspace or tool authority.

      Delivery status

      Slice 1: read-only observation admission — delivered

      PR #497 supplies the provider-neutral generated-XMD admission boundary. PR #550 supplies the authored workflow seam:

      <Evaluatesource={turn.source}as="observation" />

      The trusted workflow host supplies the exact candidate source, immutable pinned observation identities, the retained Workspace roots and selected root, and exact request ceilings such as allowed HTTP requests. The complete fragment is preflighted before its first effect. Admitted observations execute through ordinary durable XMD effects, and the filtered source, decision, observation values, rendered output, identities, roots, and request policy are retained for replay.

      <Evaluate> currently accepts only source; omitting the new allow prop below preserves this delivered behavior exactly.

      Slice 2: Workspace mutation admission

      Extend the same <Evaluate> seam rather than adding a second construct:

      <!-- `allow` omitted: read-only, as today -->
      <Evaluatesource={turn.source}as="observation" />
      <!-- generated mutations confined to the run-owned Workspace -->
      <Evaluate source={proposal.changes} allow={["write"]} />

      allow is an optional non-empty set drawn from the closed values read and write. Omission means exactly ["read"]. Duplicate or unknown values are refused before the candidate source is parsed. The normalized set is retained with the admission.

      The two values describe effect classes, not component identities:

      • read selects observation-only identities. It includes core's pinned self-closing File:read identity and includes pinned Fetch only when the host supplied at least one exact request ceiling. Fetch remains the existing non-mutating GET/HEAD component; there is no mutating Fetch form.
      • write selects mutations confined to the run-owned Workspace. This initial slice admits the exact pinned identities for lexical Dir and paired File:write. <File.Delete> and its pinned identity are owned by Add a contextual File.Delete component for contained file removal #567 and join the standard host policy only after both the ordinary component and this admission slice exist. The initial table excludes local Git even though those effects are also Workspace-local; Git-host, Issue, process, eval, exec, native-command, credential, and other external effects are outside the class entirely.

      An authored document may request both classes, but same-name forms remain distinct identities. A self-closing <File /> resolves only to File:read; a paired <File>…</File> resolves only to File:write. Admission therefore resolves by the exact pinned definition and admitted form, not by the word File. A same-name repository component, registration, bundle member, or middleware answer satisfies neither identity.

      The complete fragment is still preflighted before its first effect. A component, form, request, expression, interpolation, import, code block, or native execution outside the selected classes refuses the whole fragment before any generated effect. Admitted mutations then execute through their ordinary contextual providers, Workspace transactions, durability, and return semantics. Generated source receives no special mutation API.

      Binding and output contract

      allow and as are independent. allow selects effect classes from host policy; as is the language's ordinary caller-owned binding for the value <Evaluate> returns. A read-, write-, or mixed-policy invocation may be bound:

      <Evaluate source={proposal.changes} allow={["write"]} as="evaluation" />

      This does not grant authority and does not expose the caller's binding environment to generated source. <Evaluate> remains an ordinary function-component boundary: its return crosses to the caller only when the caller writes as, and an unbound non-string return renders nothing.

      Slice 2 does not invent a generic mutation results collection. <Evaluate> keeps its delivered return shape:

      {
      "observations": [{ "name": "File", "value": "..." }],
      "output": "..."
      }

      observations contains the ordinary values of admitted read identities, in invocation order. output contains what the generated fragment rendered. Admitted write identities do not acquire synthetic receipts or entries merely because they mutated the Workspace; their durable effect records remain the authoritative account. A write-only fragment therefore normally binds { "observations": [], "output": "" } with this slice's Dir and paired File:write table.

      The generated-XMD preflight's delivered refusal of as inside generated source, and of generated binding reads, remains unchanged in this mutation slice. That restriction is separate from ordinary component semantics: authored write operations such as <Git.Commit ... as="commit" /> continue to bind their ordinary returns, and <Evaluate ... as="evaluation" /> remains valid for every allow selection. Exporting named fragment-local bindings would be a separate generated-language contract, including replay and collision semantics; this slice does not need it for #181.

      Authored approval

      Admission does not prompt and does not approve. Approval is ordinary authored control flow before a write-enabled <Evaluate> is reached.

      For PR #181, the existing UserCheckpoint authorizes the converged plan before Implementation begins. That authorization is sufficient for the implementation Agent's generated Workspace changes; the workflow does not require a second approval of the literal fragment. A workflow that wants exact-fragment approval places its own elicitation or suspension gate immediately before <Evaluate allow={["write"]}>.

      If the authored approval branch is not taken, the write-enabled <Evaluate> is never invoked and no generated admission or mutation exists.

      Host policy and authority

      Generated XMD is untrusted input. The Agent output carries data, never authorization. Only the trusted workflow host installs generated-XMD policy, as immutable values captured before any installation, middleware, or document code runs.

      The host policy contains separate read and optional write tables. Each table holds exact pinned definitions and their form/request constraints. Slice 2 makes the standard Deno workflow profile supply the Workspace-local write table with lexical Dir and paired File:write. The profile supplies no external-write or execution table. Another host that supplied no write table refuses allow={["write"]} before parsing the generated source. #567 owns the ordinary <File.Delete> component and its later pinned-policy integration; deletion is not part of this initial slice, and #181 cannot consume it until #567 is delivered.

      The authored allow prop selects a subset of that already-installed policy. It can narrow or request a class but cannot create a pinned identity, add a root, authorize a destination, attach a credential, or widen a request. Public middleware, document props, eval bindings, repository components, generated names, and another loaded package copy cannot manufacture admission.

      Generated source receives none of the policy values. Network destination, method, headers, timeout, retained roots, selected root, exact component definitions, and credentials remain host-owned ceilings beneath the read and write labels.

      Replay contract

      One ordinary durable generated_xmd admission retains:

      • the normalized allow selection;
      • the exact admitted source;
      • retained roots and selected root;
      • every exact pinned identity and admitted form the fragment named; and
      • normalized request constraints.

      A continuation compares that policy whole and exactly before one generated component is invoked. A changed class, root, selected root, component definition, admitted form, or request ceiling refuses without performing an effect. The retained source, rather than a later caller's candidate, is what expands. Completed replay asks neither the Agent nor a provider to repeat completed work.

      Integration with the workflow Agent

      #302 supplies the retained Agent session and authored multi-turn loop:

      1. The Agent returns an observation fragment.
      2. <Evaluate> defaults to read, admits it under the host's read table, and returns its observation value.
      3. The authored <Evaluate ... as="observation" /> invocation binds that value for a later Prompt in the same Agent session.
      4. The Agent eventually returns one final proposal as source data.
      5. Authored approval control reaches <Evaluate allow={["write"]}>.
      6. The host intersects that request with its immutable write table and admits the proposal or refuses it whole.

      The Agent receives no writable channel through this integration.

      Acceptance

      Existing read behavior

      • <Evaluate source={source} /> is byte-for-byte and behaviorally compatible with the delivered read-only seam.
      • Omitted allow normalizes to ["read"] and retains that selection.
      • Pinned self-closing File:read and exact bounded Fetch continue to work; paired File, mutating/unknown HTTP methods, and requests outside the exact Fetch ceiling perform nothing.
      • Bound read evaluation keeps the delivered { observations, output } value; unbound evaluation renders nothing.

      Write behavior

      • allow accepts only a non-empty, duplicate-free set of read and write; invalid policy is refused before candidate parsing or retention.
      • allow={["write"]} fails before candidate parsing when the host installed no write table.
      • The host's immutable write table, never the prop or generated source, selects the exact pinned mutation identities.
      • A paired pinned File:write can mutate the selected run-owned Workspace through the ordinary Files provider and effect transaction.
      • Self-closing and paired forms of the same component resolve to different pinned identities; selecting only one class never admits the other form.
      • The first consumer admits only the Workspace-local forms its host supplied. Generated Git push, PullRequest, Issue upsert, process, eval, exec, native command, credential, and arbitrary network effects remain refused.
      • Mixed admitted and unadmitted syntax produces no generated effect.
      • Rejected mutation syntax or authority produces no partial effect.
      • Admitted mutations use their ordinary providers, transactions, and durable records; the evaluator creates no generic mutation result or receipt.
      • as on <Evaluate> is valid for read, write, and mixed selections and binds the same { observations, output } shape. It neither widens admission nor exports the caller's bindings.
      • Authored refusal before the write-enabled Evaluate produces no generated admission or mutation.
      • Continuation compares the normalized classes, identities, forms, roots, and request ceilings before performing anything; completed replay repeats no mutation.
      • Identity substitution, same-name repository components, middleware replacement, another loaded copy, and a changed host policy cannot turn a read identity into a write identity or manufacture either one.

      Dependencies

      Out of scope

      • Direct Agent filesystem, terminal, MCP, or network access.
      • ACP additionalDirectories.
      • A document prop or public component that grants generated source authority; allow only narrows the host's captured policy.
      • Mutating HTTP through <Fetch>.
      • Generated local Git operations, Git-host mutations, Issue upserts, process execution, eval/exec, native commands, credentials, or an unspecified external-write class.
      • Workflow-bundled Markdown component admission.
      • <File.Delete> and its pinned write-policy integration; Add a contextual File.Delete component for contained file removal #567 owns both after this initial slice.
      • Generated-source as, binding reads, or exporting a fragment-local binding environment.
      • Synthetic mutation results or receipts.
      • Partial execution before complete-fragment preflight.
      • A hidden approval prompt inside <Evaluate>.
      • Mandatory exact-fragment approval after a user already authorized the converged plan.
      • Unattended approval of mutation proposals.
      • Portable ACP adapter-level no-tool enforcement; Require ACP adapters to enforce tool-free workflow Agent sessions #496 owns it.

      Activity

      Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

      Metadata

      Metadata

      Assignees

      No one assigned

        Labels

        No labels
        No labels

        Projects

        No projects

          Relationships

          None yet

          Development

          No branches or pull requests

          Issue actions

          , '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

          Evaluate Agent-generated XMD through a constrained allowlist #369

          Description

          @taras

          Story

          As a workflow author, I want untrusted XMD returned by an Agent to be validated and executed by the host, so the Agent can request observations and propose changes without receiving direct Workspace or tool authority.

          Delivery status

          Slice 1: read-only observation admission — delivered

          PR #497 supplies the provider-neutral generated-XMD admission boundary. PR #550 supplies the authored workflow seam:

          <Evaluatesource={turn.source}as="observation" />

          The trusted workflow host supplies the exact candidate source, immutable pinned observation identities, the retained Workspace roots and selected root, and exact request ceilings such as allowed HTTP requests. The complete fragment is preflighted before its first effect. Admitted observations execute through ordinary durable XMD effects, and the filtered source, decision, observation values, rendered output, identities, roots, and request policy are retained for replay.

          <Evaluate> currently accepts only source; omitting the new allow prop below preserves this delivered behavior exactly.

          Slice 2: Workspace mutation admission

          Extend the same <Evaluate> seam rather than adding a second construct:

          <!-- `allow` omitted: read-only, as today -->
          <Evaluatesource={turn.source}as="observation" />
          <!-- generated mutations confined to the run-owned Workspace -->
          <Evaluate source={proposal.changes} allow={["write"]} />

          allow is an optional non-empty set drawn from the closed values read and write. Omission means exactly ["read"]. Duplicate or unknown values are refused before the candidate source is parsed. The normalized set is retained with the admission.

          The two values describe effect classes, not component identities:

          • read selects observation-only identities. It includes core's pinned self-closing File:read identity and includes pinned Fetch only when the host supplied at least one exact request ceiling. Fetch remains the existing non-mutating GET/HEAD component; there is no mutating Fetch form.
          • write selects mutations confined to the run-owned Workspace. This initial slice admits the exact pinned identities for lexical Dir and paired File:write. <File.Delete> and its pinned identity are owned by Add a contextual File.Delete component for contained file removal #567 and join the standard host policy only after both the ordinary component and this admission slice exist. The initial table excludes local Git even though those effects are also Workspace-local; Git-host, Issue, process, eval, exec, native-command, credential, and other external effects are outside the class entirely.

          An authored document may request both classes, but same-name forms remain distinct identities. A self-closing <File /> resolves only to File:read; a paired <File>…</File> resolves only to File:write. Admission therefore resolves by the exact pinned definition and admitted form, not by the word File. A same-name repository component, registration, bundle member, or middleware answer satisfies neither identity.

          The complete fragment is still preflighted before its first effect. A component, form, request, expression, interpolation, import, code block, or native execution outside the selected classes refuses the whole fragment before any generated effect. Admitted mutations then execute through their ordinary contextual providers, Workspace transactions, durability, and return semantics. Generated source receives no special mutation API.

          Binding and output contract

          allow and as are independent. allow selects effect classes from host policy; as is the language's ordinary caller-owned binding for the value <Evaluate> returns. A read-, write-, or mixed-policy invocation may be bound:

          <Evaluate source={proposal.changes} allow={["write"]} as="evaluation" />

          This does not grant authority and does not expose the caller's binding environment to generated source. <Evaluate> remains an ordinary function-component boundary: its return crosses to the caller only when the caller writes as, and an unbound non-string return renders nothing.

          Slice 2 does not invent a generic mutation results collection. <Evaluate> keeps its delivered return shape:

          {
          "observations": [{ "name": "File", "value": "..." }],
          "output": "..."
          }

          observations contains the ordinary values of admitted read identities, in invocation order. output contains what the generated fragment rendered. Admitted write identities do not acquire synthetic receipts or entries merely because they mutated the Workspace; their durable effect records remain the authoritative account. A write-only fragment therefore normally binds { "observations": [], "output": "" } with this slice's Dir and paired File:write table.

          The generated-XMD preflight's delivered refusal of as inside generated source, and of generated binding reads, remains unchanged in this mutation slice. That restriction is separate from ordinary component semantics: authored write operations such as <Git.Commit ... as="commit" /> continue to bind their ordinary returns, and <Evaluate ... as="evaluation" /> remains valid for every allow selection. Exporting named fragment-local bindings would be a separate generated-language contract, including replay and collision semantics; this slice does not need it for #181.

          Authored approval

          Admission does not prompt and does not approve. Approval is ordinary authored control flow before a write-enabled <Evaluate> is reached.

          For PR #181, the existing UserCheckpoint authorizes the converged plan before Implementation begins. That authorization is sufficient for the implementation Agent's generated Workspace changes; the workflow does not require a second approval of the literal fragment. A workflow that wants exact-fragment approval places its own elicitation or suspension gate immediately before <Evaluate allow={["write"]}>.

          If the authored approval branch is not taken, the write-enabled <Evaluate> is never invoked and no generated admission or mutation exists.

          Host policy and authority

          Generated XMD is untrusted input. The Agent output carries data, never authorization. Only the trusted workflow host installs generated-XMD policy, as immutable values captured before any installation, middleware, or document code runs.

          The host policy contains separate read and optional write tables. Each table holds exact pinned definitions and their form/request constraints. Slice 2 makes the standard Deno workflow profile supply the Workspace-local write table with lexical Dir and paired File:write. The profile supplies no external-write or execution table. Another host that supplied no write table refuses allow={["write"]} before parsing the generated source. #567 owns the ordinary <File.Delete> component and its later pinned-policy integration; deletion is not part of this initial slice, and #181 cannot consume it until #567 is delivered.

          The authored allow prop selects a subset of that already-installed policy. It can narrow or request a class but cannot create a pinned identity, add a root, authorize a destination, attach a credential, or widen a request. Public middleware, document props, eval bindings, repository components, generated names, and another loaded package copy cannot manufacture admission.

          Generated source receives none of the policy values. Network destination, method, headers, timeout, retained roots, selected root, exact component definitions, and credentials remain host-owned ceilings beneath the read and write labels.

          Replay contract

          One ordinary durable generated_xmd admission retains:

          • the normalized allow selection;
          • the exact admitted source;
          • retained roots and selected root;
          • every exact pinned identity and admitted form the fragment named; and
          • normalized request constraints.

          A continuation compares that policy whole and exactly before one generated component is invoked. A changed class, root, selected root, component definition, admitted form, or request ceiling refuses without performing an effect. The retained source, rather than a later caller's candidate, is what expands. Completed replay asks neither the Agent nor a provider to repeat completed work.

          Integration with the workflow Agent

          #302 supplies the retained Agent session and authored multi-turn loop:

          1. The Agent returns an observation fragment.
          2. <Evaluate> defaults to read, admits it under the host's read table, and returns its observation value.
          3. The authored <Evaluate ... as="observation" /> invocation binds that value for a later Prompt in the same Agent session.
          4. The Agent eventually returns one final proposal as source data.
          5. Authored approval control reaches <Evaluate allow={["write"]}>.
          6. The host intersects that request with its immutable write table and admits the proposal or refuses it whole.

          The Agent receives no writable channel through this integration.

          Acceptance

          Existing read behavior

          • <Evaluate source={source} /> is byte-for-byte and behaviorally compatible with the delivered read-only seam.
          • Omitted allow normalizes to ["read"] and retains that selection.
          • Pinned self-closing File:read and exact bounded Fetch continue to work; paired File, mutating/unknown HTTP methods, and requests outside the exact Fetch ceiling perform nothing.
          • Bound read evaluation keeps the delivered { observations, output } value; unbound evaluation renders nothing.

          Write behavior

          • allow accepts only a non-empty, duplicate-free set of read and write; invalid policy is refused before candidate parsing or retention.
          • allow={["write"]} fails before candidate parsing when the host installed no write table.
          • The host's immutable write table, never the prop or generated source, selects the exact pinned mutation identities.
          • A paired pinned File:write can mutate the selected run-owned Workspace through the ordinary Files provider and effect transaction.
          • Self-closing and paired forms of the same component resolve to different pinned identities; selecting only one class never admits the other form.
          • The first consumer admits only the Workspace-local forms its host supplied. Generated Git push, PullRequest, Issue upsert, process, eval, exec, native command, credential, and arbitrary network effects remain refused.
          • Mixed admitted and unadmitted syntax produces no generated effect.
          • Rejected mutation syntax or authority produces no partial effect.
          • Admitted mutations use their ordinary providers, transactions, and durable records; the evaluator creates no generic mutation result or receipt.
          • as on <Evaluate> is valid for read, write, and mixed selections and binds the same { observations, output } shape. It neither widens admission nor exports the caller's bindings.
          • Authored refusal before the write-enabled Evaluate produces no generated admission or mutation.
          • Continuation compares the normalized classes, identities, forms, roots, and request ceilings before performing anything; completed replay repeats no mutation.
          • Identity substitution, same-name repository components, middleware replacement, another loaded copy, and a changed host policy cannot turn a read identity into a write identity or manufacture either one.

          Dependencies

          Out of scope

          • Direct Agent filesystem, terminal, MCP, or network access.
          • ACP additionalDirectories.
          • A document prop or public component that grants generated source authority; allow only narrows the host's captured policy.
          • Mutating HTTP through <Fetch>.
          • Generated local Git operations, Git-host mutations, Issue upserts, process execution, eval/exec, native commands, credentials, or an unspecified external-write class.
          • Workflow-bundled Markdown component admission.
          • <File.Delete> and its pinned write-policy integration; Add a contextual File.Delete component for contained file removal #567 owns both after this initial slice.
          • Generated-source as, binding reads, or exporting a fragment-local binding environment.
          • Synthetic mutation results or receipts.
          • Partial execution before complete-fragment preflight.
          • A hidden approval prompt inside <Evaluate>.
          • Mandatory exact-fragment approval after a user already authorized the converged plan.
          • Unattended approval of mutation proposals.
          • Portable ACP adapter-level no-tool enforcement; Require ACP adapters to enforce tool-free workflow Agent sessions #496 owns it.

          Activity

          Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

          Metadata

          Metadata

          Assignees

          No one assigned

            Labels

            No labels
            No labels

            Projects

            No projects

              Relationships

              None yet

              Development

              No branches or pull requests

              Issue actions

              , '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

              Evaluate Agent-generated XMD through a constrained allowlist #369

              Description

              @taras

              Story

              As a workflow author, I want untrusted XMD returned by an Agent to be validated and executed by the host, so the Agent can request observations and propose changes without receiving direct Workspace or tool authority.

              Delivery status

              Slice 1: read-only observation admission — delivered

              PR #497 supplies the provider-neutral generated-XMD admission boundary. PR #550 supplies the authored workflow seam:

              <Evaluatesource={turn.source}as="observation" />

              The trusted workflow host supplies the exact candidate source, immutable pinned observation identities, the retained Workspace roots and selected root, and exact request ceilings such as allowed HTTP requests. The complete fragment is preflighted before its first effect. Admitted observations execute through ordinary durable XMD effects, and the filtered source, decision, observation values, rendered output, identities, roots, and request policy are retained for replay.

              <Evaluate> currently accepts only source; omitting the new allow prop below preserves this delivered behavior exactly.

              Slice 2: Workspace mutation admission

              Extend the same <Evaluate> seam rather than adding a second construct:

              <!-- `allow` omitted: read-only, as today -->
              <Evaluatesource={turn.source}as="observation" />
              <!-- generated mutations confined to the run-owned Workspace -->
              <Evaluate source={proposal.changes} allow={["write"]} />

              allow is an optional non-empty set drawn from the closed values read and write. Omission means exactly ["read"]. Duplicate or unknown values are refused before the candidate source is parsed. The normalized set is retained with the admission.

              The two values describe effect classes, not component identities:

              • read selects observation-only identities. It includes core's pinned self-closing File:read identity and includes pinned Fetch only when the host supplied at least one exact request ceiling. Fetch remains the existing non-mutating GET/HEAD component; there is no mutating Fetch form.
              • write selects mutations confined to the run-owned Workspace. This initial slice admits the exact pinned identities for lexical Dir and paired File:write. <File.Delete> and its pinned identity are owned by Add a contextual File.Delete component for contained file removal #567 and join the standard host policy only after both the ordinary component and this admission slice exist. The initial table excludes local Git even though those effects are also Workspace-local; Git-host, Issue, process, eval, exec, native-command, credential, and other external effects are outside the class entirely.

              An authored document may request both classes, but same-name forms remain distinct identities. A self-closing <File /> resolves only to File:read; a paired <File>…</File> resolves only to File:write. Admission therefore resolves by the exact pinned definition and admitted form, not by the word File. A same-name repository component, registration, bundle member, or middleware answer satisfies neither identity.

              The complete fragment is still preflighted before its first effect. A component, form, request, expression, interpolation, import, code block, or native execution outside the selected classes refuses the whole fragment before any generated effect. Admitted mutations then execute through their ordinary contextual providers, Workspace transactions, durability, and return semantics. Generated source receives no special mutation API.

              Binding and output contract

              allow and as are independent. allow selects effect classes from host policy; as is the language's ordinary caller-owned binding for the value <Evaluate> returns. A read-, write-, or mixed-policy invocation may be bound:

              <Evaluate source={proposal.changes} allow={["write"]} as="evaluation" />

              This does not grant authority and does not expose the caller's binding environment to generated source. <Evaluate> remains an ordinary function-component boundary: its return crosses to the caller only when the caller writes as, and an unbound non-string return renders nothing.

              Slice 2 does not invent a generic mutation results collection. <Evaluate> keeps its delivered return shape:

              {
              "observations": [{ "name": "File", "value": "..." }],
              "output": "..."
              }

              observations contains the ordinary values of admitted read identities, in invocation order. output contains what the generated fragment rendered. Admitted write identities do not acquire synthetic receipts or entries merely because they mutated the Workspace; their durable effect records remain the authoritative account. A write-only fragment therefore normally binds { "observations": [], "output": "" } with this slice's Dir and paired File:write table.

              The generated-XMD preflight's delivered refusal of as inside generated source, and of generated binding reads, remains unchanged in this mutation slice. That restriction is separate from ordinary component semantics: authored write operations such as <Git.Commit ... as="commit" /> continue to bind their ordinary returns, and <Evaluate ... as="evaluation" /> remains valid for every allow selection. Exporting named fragment-local bindings would be a separate generated-language contract, including replay and collision semantics; this slice does not need it for #181.

              Authored approval

              Admission does not prompt and does not approve. Approval is ordinary authored control flow before a write-enabled <Evaluate> is reached.

              For PR #181, the existing UserCheckpoint authorizes the converged plan before Implementation begins. That authorization is sufficient for the implementation Agent's generated Workspace changes; the workflow does not require a second approval of the literal fragment. A workflow that wants exact-fragment approval places its own elicitation or suspension gate immediately before <Evaluate allow={["write"]}>.

              If the authored approval branch is not taken, the write-enabled <Evaluate> is never invoked and no generated admission or mutation exists.

              Host policy and authority

              Generated XMD is untrusted input. The Agent output carries data, never authorization. Only the trusted workflow host installs generated-XMD policy, as immutable values captured before any installation, middleware, or document code runs.

              The host policy contains separate read and optional write tables. Each table holds exact pinned definitions and their form/request constraints. Slice 2 makes the standard Deno workflow profile supply the Workspace-local write table with lexical Dir and paired File:write. The profile supplies no external-write or execution table. Another host that supplied no write table refuses allow={["write"]} before parsing the generated source. #567 owns the ordinary <File.Delete> component and its later pinned-policy integration; deletion is not part of this initial slice, and #181 cannot consume it until #567 is delivered.

              The authored allow prop selects a subset of that already-installed policy. It can narrow or request a class but cannot create a pinned identity, add a root, authorize a destination, attach a credential, or widen a request. Public middleware, document props, eval bindings, repository components, generated names, and another loaded package copy cannot manufacture admission.

              Generated source receives none of the policy values. Network destination, method, headers, timeout, retained roots, selected root, exact component definitions, and credentials remain host-owned ceilings beneath the read and write labels.

              Replay contract

              One ordinary durable generated_xmd admission retains:

              • the normalized allow selection;
              • the exact admitted source;
              • retained roots and selected root;
              • every exact pinned identity and admitted form the fragment named; and
              • normalized request constraints.

              A continuation compares that policy whole and exactly before one generated component is invoked. A changed class, root, selected root, component definition, admitted form, or request ceiling refuses without performing an effect. The retained source, rather than a later caller's candidate, is what expands. Completed replay asks neither the Agent nor a provider to repeat completed work.

              Integration with the workflow Agent

              #302 supplies the retained Agent session and authored multi-turn loop:

              1. The Agent returns an observation fragment.
              2. <Evaluate> defaults to read, admits it under the host's read table, and returns its observation value.
              3. The authored <Evaluate ... as="observation" /> invocation binds that value for a later Prompt in the same Agent session.
              4. The Agent eventually returns one final proposal as source data.
              5. Authored approval control reaches <Evaluate allow={["write"]}>.
              6. The host intersects that request with its immutable write table and admits the proposal or refuses it whole.

              The Agent receives no writable channel through this integration.

              Acceptance

              Existing read behavior

              • <Evaluate source={source} /> is byte-for-byte and behaviorally compatible with the delivered read-only seam.
              • Omitted allow normalizes to ["read"] and retains that selection.
              • Pinned self-closing File:read and exact bounded Fetch continue to work; paired File, mutating/unknown HTTP methods, and requests outside the exact Fetch ceiling perform nothing.
              • Bound read evaluation keeps the delivered { observations, output } value; unbound evaluation renders nothing.

              Write behavior

              • allow accepts only a non-empty, duplicate-free set of read and write; invalid policy is refused before candidate parsing or retention.
              • allow={["write"]} fails before candidate parsing when the host installed no write table.
              • The host's immutable write table, never the prop or generated source, selects the exact pinned mutation identities.
              • A paired pinned File:write can mutate the selected run-owned Workspace through the ordinary Files provider and effect transaction.
              • Self-closing and paired forms of the same component resolve to different pinned identities; selecting only one class never admits the other form.
              • The first consumer admits only the Workspace-local forms its host supplied. Generated Git push, PullRequest, Issue upsert, process, eval, exec, native command, credential, and arbitrary network effects remain refused.
              • Mixed admitted and unadmitted syntax produces no generated effect.
              • Rejected mutation syntax or authority produces no partial effect.
              • Admitted mutations use their ordinary providers, transactions, and durable records; the evaluator creates no generic mutation result or receipt.
              • as on <Evaluate> is valid for read, write, and mixed selections and binds the same { observations, output } shape. It neither widens admission nor exports the caller's bindings.
              • Authored refusal before the write-enabled Evaluate produces no generated admission or mutation.
              • Continuation compares the normalized classes, identities, forms, roots, and request ceilings before performing anything; completed replay repeats no mutation.
              • Identity substitution, same-name repository components, middleware replacement, another loaded copy, and a changed host policy cannot turn a read identity into a write identity or manufacture either one.

              Dependencies

              Out of scope

              • Direct Agent filesystem, terminal, MCP, or network access.
              • ACP additionalDirectories.
              • A document prop or public component that grants generated source authority; allow only narrows the host's captured policy.
              • Mutating HTTP through <Fetch>.
              • Generated local Git operations, Git-host mutations, Issue upserts, process execution, eval/exec, native commands, credentials, or an unspecified external-write class.
              • Workflow-bundled Markdown component admission.
              • <File.Delete> and its pinned write-policy integration; Add a contextual File.Delete component for contained file removal #567 owns both after this initial slice.
              • Generated-source as, binding reads, or exporting a fragment-local binding environment.
              • Synthetic mutation results or receipts.
              • Partial execution before complete-fragment preflight.
              • A hidden approval prompt inside <Evaluate>.
              • Mandatory exact-fragment approval after a user already authorized the converged plan.
              • Unattended approval of mutation proposals.
              • Portable ACP adapter-level no-tool enforcement; Require ACP adapters to enforce tool-free workflow Agent sessions #496 owns it.

              Activity

              Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

              Metadata

              Metadata

              Assignees

              No one assigned

                Labels

                No labels
                No labels

                Projects

                No projects

                  Relationships

                  None yet

                  Development

                  No branches or pull requests

                  Issue actions

                  , '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

                  Evaluate Agent-generated XMD through a constrained allowlist #369

                  Description

                  @taras

                  Story

                  As a workflow author, I want untrusted XMD returned by an Agent to be validated and executed by the host, so the Agent can request observations and propose changes without receiving direct Workspace or tool authority.

                  Delivery status

                  Slice 1: read-only observation admission — delivered

                  PR #497 supplies the provider-neutral generated-XMD admission boundary. PR #550 supplies the authored workflow seam:

                  <Evaluatesource={turn.source}as="observation" />

                  The trusted workflow host supplies the exact candidate source, immutable pinned observation identities, the retained Workspace roots and selected root, and exact request ceilings such as allowed HTTP requests. The complete fragment is preflighted before its first effect. Admitted observations execute through ordinary durable XMD effects, and the filtered source, decision, observation values, rendered output, identities, roots, and request policy are retained for replay.

                  <Evaluate> currently accepts only source; omitting the new allow prop below preserves this delivered behavior exactly.

                  Slice 2: Workspace mutation admission

                  Extend the same <Evaluate> seam rather than adding a second construct:

                  <!-- `allow` omitted: read-only, as today -->
                  <Evaluatesource={turn.source}as="observation" />
                  <!-- generated mutations confined to the run-owned Workspace -->
                  <Evaluate source={proposal.changes} allow={["write"]} />

                  allow is an optional non-empty set drawn from the closed values read and write. Omission means exactly ["read"]. Duplicate or unknown values are refused before the candidate source is parsed. The normalized set is retained with the admission.

                  The two values describe effect classes, not component identities:

                  • read selects observation-only identities. It includes core's pinned self-closing File:read identity and includes pinned Fetch only when the host supplied at least one exact request ceiling. Fetch remains the existing non-mutating GET/HEAD component; there is no mutating Fetch form.
                  • write selects mutations confined to the run-owned Workspace. This initial slice admits the exact pinned identities for lexical Dir and paired File:write. <File.Delete> and its pinned identity are owned by Add a contextual File.Delete component for contained file removal #567 and join the standard host policy only after both the ordinary component and this admission slice exist. The initial table excludes local Git even though those effects are also Workspace-local; Git-host, Issue, process, eval, exec, native-command, credential, and other external effects are outside the class entirely.

                  An authored document may request both classes, but same-name forms remain distinct identities. A self-closing <File /> resolves only to File:read; a paired <File>…</File> resolves only to File:write. Admission therefore resolves by the exact pinned definition and admitted form, not by the word File. A same-name repository component, registration, bundle member, or middleware answer satisfies neither identity.

                  The complete fragment is still preflighted before its first effect. A component, form, request, expression, interpolation, import, code block, or native execution outside the selected classes refuses the whole fragment before any generated effect. Admitted mutations then execute through their ordinary contextual providers, Workspace transactions, durability, and return semantics. Generated source receives no special mutation API.

                  Binding and output contract

                  allow and as are independent. allow selects effect classes from host policy; as is the language's ordinary caller-owned binding for the value <Evaluate> returns. A read-, write-, or mixed-policy invocation may be bound:

                  <Evaluate source={proposal.changes} allow={["write"]} as="evaluation" />

                  This does not grant authority and does not expose the caller's binding environment to generated source. <Evaluate> remains an ordinary function-component boundary: its return crosses to the caller only when the caller writes as, and an unbound non-string return renders nothing.

                  Slice 2 does not invent a generic mutation results collection. <Evaluate> keeps its delivered return shape:

                  {
                  "observations": [{ "name": "File", "value": "..." }],
                  "output": "..."
                  }

                  observations contains the ordinary values of admitted read identities, in invocation order. output contains what the generated fragment rendered. Admitted write identities do not acquire synthetic receipts or entries merely because they mutated the Workspace; their durable effect records remain the authoritative account. A write-only fragment therefore normally binds { "observations": [], "output": "" } with this slice's Dir and paired File:write table.

                  The generated-XMD preflight's delivered refusal of as inside generated source, and of generated binding reads, remains unchanged in this mutation slice. That restriction is separate from ordinary component semantics: authored write operations such as <Git.Commit ... as="commit" /> continue to bind their ordinary returns, and <Evaluate ... as="evaluation" /> remains valid for every allow selection. Exporting named fragment-local bindings would be a separate generated-language contract, including replay and collision semantics; this slice does not need it for #181.

                  Authored approval

                  Admission does not prompt and does not approve. Approval is ordinary authored control flow before a write-enabled <Evaluate> is reached.

                  For PR #181, the existing UserCheckpoint authorizes the converged plan before Implementation begins. That authorization is sufficient for the implementation Agent's generated Workspace changes; the workflow does not require a second approval of the literal fragment. A workflow that wants exact-fragment approval places its own elicitation or suspension gate immediately before <Evaluate allow={["write"]}>.

                  If the authored approval branch is not taken, the write-enabled <Evaluate> is never invoked and no generated admission or mutation exists.

                  Host policy and authority

                  Generated XMD is untrusted input. The Agent output carries data, never authorization. Only the trusted workflow host installs generated-XMD policy, as immutable values captured before any installation, middleware, or document code runs.

                  The host policy contains separate read and optional write tables. Each table holds exact pinned definitions and their form/request constraints. Slice 2 makes the standard Deno workflow profile supply the Workspace-local write table with lexical Dir and paired File:write. The profile supplies no external-write or execution table. Another host that supplied no write table refuses allow={["write"]} before parsing the generated source. #567 owns the ordinary <File.Delete> component and its later pinned-policy integration; deletion is not part of this initial slice, and #181 cannot consume it until #567 is delivered.

                  The authored allow prop selects a subset of that already-installed policy. It can narrow or request a class but cannot create a pinned identity, add a root, authorize a destination, attach a credential, or widen a request. Public middleware, document props, eval bindings, repository components, generated names, and another loaded package copy cannot manufacture admission.

                  Generated source receives none of the policy values. Network destination, method, headers, timeout, retained roots, selected root, exact component definitions, and credentials remain host-owned ceilings beneath the read and write labels.

                  Replay contract

                  One ordinary durable generated_xmd admission retains:

                  • the normalized allow selection;
                  • the exact admitted source;
                  • retained roots and selected root;
                  • every exact pinned identity and admitted form the fragment named; and
                  • normalized request constraints.

                  A continuation compares that policy whole and exactly before one generated component is invoked. A changed class, root, selected root, component definition, admitted form, or request ceiling refuses without performing an effect. The retained source, rather than a later caller's candidate, is what expands. Completed replay asks neither the Agent nor a provider to repeat completed work.

                  Integration with the workflow Agent

                  #302 supplies the retained Agent session and authored multi-turn loop:

                  1. The Agent returns an observation fragment.
                  2. <Evaluate> defaults to read, admits it under the host's read table, and returns its observation value.
                  3. The authored <Evaluate ... as="observation" /> invocation binds that value for a later Prompt in the same Agent session.
                  4. The Agent eventually returns one final proposal as source data.
                  5. Authored approval control reaches <Evaluate allow={["write"]}>.
                  6. The host intersects that request with its immutable write table and admits the proposal or refuses it whole.

                  The Agent receives no writable channel through this integration.

                  Acceptance

                  Existing read behavior

                  • <Evaluate source={source} /> is byte-for-byte and behaviorally compatible with the delivered read-only seam.
                  • Omitted allow normalizes to ["read"] and retains that selection.
                  • Pinned self-closing File:read and exact bounded Fetch continue to work; paired File, mutating/unknown HTTP methods, and requests outside the exact Fetch ceiling perform nothing.
                  • Bound read evaluation keeps the delivered { observations, output } value; unbound evaluation renders nothing.

                  Write behavior

                  • allow accepts only a non-empty, duplicate-free set of read and write; invalid policy is refused before candidate parsing or retention.
                  • allow={["write"]} fails before candidate parsing when the host installed no write table.
                  • The host's immutable write table, never the prop or generated source, selects the exact pinned mutation identities.
                  • A paired pinned File:write can mutate the selected run-owned Workspace through the ordinary Files provider and effect transaction.
                  • Self-closing and paired forms of the same component resolve to different pinned identities; selecting only one class never admits the other form.
                  • The first consumer admits only the Workspace-local forms its host supplied. Generated Git push, PullRequest, Issue upsert, process, eval, exec, native command, credential, and arbitrary network effects remain refused.
                  • Mixed admitted and unadmitted syntax produces no generated effect.
                  • Rejected mutation syntax or authority produces no partial effect.
                  • Admitted mutations use their ordinary providers, transactions, and durable records; the evaluator creates no generic mutation result or receipt.
                  • as on <Evaluate> is valid for read, write, and mixed selections and binds the same { observations, output } shape. It neither widens admission nor exports the caller's bindings.
                  • Authored refusal before the write-enabled Evaluate produces no generated admission or mutation.
                  • Continuation compares the normalized classes, identities, forms, roots, and request ceilings before performing anything; completed replay repeats no mutation.
                  • Identity substitution, same-name repository components, middleware replacement, another loaded copy, and a changed host policy cannot turn a read identity into a write identity or manufacture either one.

                  Dependencies

                  Out of scope

                  • Direct Agent filesystem, terminal, MCP, or network access.
                  • ACP additionalDirectories.
                  • A document prop or public component that grants generated source authority; allow only narrows the host's captured policy.
                  • Mutating HTTP through <Fetch>.
                  • Generated local Git operations, Git-host mutations, Issue upserts, process execution, eval/exec, native commands, credentials, or an unspecified external-write class.
                  • Workflow-bundled Markdown component admission.
                  • <File.Delete> and its pinned write-policy integration; Add a contextual File.Delete component for contained file removal #567 owns both after this initial slice.
                  • Generated-source as, binding reads, or exporting a fragment-local binding environment.
                  • Synthetic mutation results or receipts.
                  • Partial execution before complete-fragment preflight.
                  • A hidden approval prompt inside <Evaluate>.
                  • Mandatory exact-fragment approval after a user already authorized the converged plan.
                  • Unattended approval of mutation proposals.
                  • Portable ACP adapter-level no-tool enforcement; Require ACP adapters to enforce tool-free workflow Agent sessions #496 owns it.

                  Activity

                  Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

                  Metadata

                  Metadata

                  Assignees

                  No one assigned

                    Labels

                    No labels
                    No labels

                    Projects

                    No projects

                      Relationships

                      None yet

                      Development

                      No branches or pull requests

                      Issue actions

                      , '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

                      Evaluate Agent-generated XMD through a constrained allowlist #369

                      Description

                      @taras

                      Story

                      As a workflow author, I want untrusted XMD returned by an Agent to be validated and executed by the host, so the Agent can request observations and propose changes without receiving direct Workspace or tool authority.

                      Delivery status

                      Slice 1: read-only observation admission — delivered

                      PR #497 supplies the provider-neutral generated-XMD admission boundary. PR #550 supplies the authored workflow seam:

                      <Evaluatesource={turn.source}as="observation" />

                      The trusted workflow host supplies the exact candidate source, immutable pinned observation identities, the retained Workspace roots and selected root, and exact request ceilings such as allowed HTTP requests. The complete fragment is preflighted before its first effect. Admitted observations execute through ordinary durable XMD effects, and the filtered source, decision, observation values, rendered output, identities, roots, and request policy are retained for replay.

                      <Evaluate> currently accepts only source; omitting the new allow prop below preserves this delivered behavior exactly.

                      Slice 2: Workspace mutation admission

                      Extend the same <Evaluate> seam rather than adding a second construct:

                      <!-- `allow` omitted: read-only, as today -->
                      <Evaluatesource={turn.source}as="observation" />
                      <!-- generated mutations confined to the run-owned Workspace -->
                      <Evaluate source={proposal.changes} allow={["write"]} />

                      allow is an optional non-empty set drawn from the closed values read and write. Omission means exactly ["read"]. Duplicate or unknown values are refused before the candidate source is parsed. The normalized set is retained with the admission.

                      The two values describe effect classes, not component identities:

                      • read selects observation-only identities. It includes core's pinned self-closing File:read identity and includes pinned Fetch only when the host supplied at least one exact request ceiling. Fetch remains the existing non-mutating GET/HEAD component; there is no mutating Fetch form.
                      • write selects mutations confined to the run-owned Workspace. This initial slice admits the exact pinned identities for lexical Dir and paired File:write. <File.Delete> and its pinned identity are owned by Add a contextual File.Delete component for contained file removal #567 and join the standard host policy only after both the ordinary component and this admission slice exist. The initial table excludes local Git even though those effects are also Workspace-local; Git-host, Issue, process, eval, exec, native-command, credential, and other external effects are outside the class entirely.

                      An authored document may request both classes, but same-name forms remain distinct identities. A self-closing <File /> resolves only to File:read; a paired <File>…</File> resolves only to File:write. Admission therefore resolves by the exact pinned definition and admitted form, not by the word File. A same-name repository component, registration, bundle member, or middleware answer satisfies neither identity.

                      The complete fragment is still preflighted before its first effect. A component, form, request, expression, interpolation, import, code block, or native execution outside the selected classes refuses the whole fragment before any generated effect. Admitted mutations then execute through their ordinary contextual providers, Workspace transactions, durability, and return semantics. Generated source receives no special mutation API.

                      Binding and output contract

                      allow and as are independent. allow selects effect classes from host policy; as is the language's ordinary caller-owned binding for the value <Evaluate> returns. A read-, write-, or mixed-policy invocation may be bound:

                      <Evaluate source={proposal.changes} allow={["write"]} as="evaluation" />

                      This does not grant authority and does not expose the caller's binding environment to generated source. <Evaluate> remains an ordinary function-component boundary: its return crosses to the caller only when the caller writes as, and an unbound non-string return renders nothing.

                      Slice 2 does not invent a generic mutation results collection. <Evaluate> keeps its delivered return shape:

                      {
                      "observations": [{ "name": "File", "value": "..." }],
                      "output": "..."
                      }

                      observations contains the ordinary values of admitted read identities, in invocation order. output contains what the generated fragment rendered. Admitted write identities do not acquire synthetic receipts or entries merely because they mutated the Workspace; their durable effect records remain the authoritative account. A write-only fragment therefore normally binds { "observations": [], "output": "" } with this slice's Dir and paired File:write table.

                      The generated-XMD preflight's delivered refusal of as inside generated source, and of generated binding reads, remains unchanged in this mutation slice. That restriction is separate from ordinary component semantics: authored write operations such as <Git.Commit ... as="commit" /> continue to bind their ordinary returns, and <Evaluate ... as="evaluation" /> remains valid for every allow selection. Exporting named fragment-local bindings would be a separate generated-language contract, including replay and collision semantics; this slice does not need it for #181.

                      Authored approval

                      Admission does not prompt and does not approve. Approval is ordinary authored control flow before a write-enabled <Evaluate> is reached.

                      For PR #181, the existing UserCheckpoint authorizes the converged plan before Implementation begins. That authorization is sufficient for the implementation Agent's generated Workspace changes; the workflow does not require a second approval of the literal fragment. A workflow that wants exact-fragment approval places its own elicitation or suspension gate immediately before <Evaluate allow={["write"]}>.

                      If the authored approval branch is not taken, the write-enabled <Evaluate> is never invoked and no generated admission or mutation exists.

                      Host policy and authority

                      Generated XMD is untrusted input. The Agent output carries data, never authorization. Only the trusted workflow host installs generated-XMD policy, as immutable values captured before any installation, middleware, or document code runs.

                      The host policy contains separate read and optional write tables. Each table holds exact pinned definitions and their form/request constraints. Slice 2 makes the standard Deno workflow profile supply the Workspace-local write table with lexical Dir and paired File:write. The profile supplies no external-write or execution table. Another host that supplied no write table refuses allow={["write"]} before parsing the generated source. #567 owns the ordinary <File.Delete> component and its later pinned-policy integration; deletion is not part of this initial slice, and #181 cannot consume it until #567 is delivered.

                      The authored allow prop selects a subset of that already-installed policy. It can narrow or request a class but cannot create a pinned identity, add a root, authorize a destination, attach a credential, or widen a request. Public middleware, document props, eval bindings, repository components, generated names, and another loaded package copy cannot manufacture admission.

                      Generated source receives none of the policy values. Network destination, method, headers, timeout, retained roots, selected root, exact component definitions, and credentials remain host-owned ceilings beneath the read and write labels.

                      Replay contract

                      One ordinary durable generated_xmd admission retains:

                      • the normalized allow selection;
                      • the exact admitted source;
                      • retained roots and selected root;
                      • every exact pinned identity and admitted form the fragment named; and
                      • normalized request constraints.

                      A continuation compares that policy whole and exactly before one generated component is invoked. A changed class, root, selected root, component definition, admitted form, or request ceiling refuses without performing an effect. The retained source, rather than a later caller's candidate, is what expands. Completed replay asks neither the Agent nor a provider to repeat completed work.

                      Integration with the workflow Agent

                      #302 supplies the retained Agent session and authored multi-turn loop:

                      1. The Agent returns an observation fragment.
                      2. <Evaluate> defaults to read, admits it under the host's read table, and returns its observation value.
                      3. The authored <Evaluate ... as="observation" /> invocation binds that value for a later Prompt in the same Agent session.
                      4. The Agent eventually returns one final proposal as source data.
                      5. Authored approval control reaches <Evaluate allow={["write"]}>.
                      6. The host intersects that request with its immutable write table and admits the proposal or refuses it whole.

                      The Agent receives no writable channel through this integration.

                      Acceptance

                      Existing read behavior

                      • <Evaluate source={source} /> is byte-for-byte and behaviorally compatible with the delivered read-only seam.
                      • Omitted allow normalizes to ["read"] and retains that selection.
                      • Pinned self-closing File:read and exact bounded Fetch continue to work; paired File, mutating/unknown HTTP methods, and requests outside the exact Fetch ceiling perform nothing.
                      • Bound read evaluation keeps the delivered { observations, output } value; unbound evaluation renders nothing.

                      Write behavior

                      • allow accepts only a non-empty, duplicate-free set of read and write; invalid policy is refused before candidate parsing or retention.
                      • allow={["write"]} fails before candidate parsing when the host installed no write table.
                      • The host's immutable write table, never the prop or generated source, selects the exact pinned mutation identities.
                      • A paired pinned File:write can mutate the selected run-owned Workspace through the ordinary Files provider and effect transaction.
                      • Self-closing and paired forms of the same component resolve to different pinned identities; selecting only one class never admits the other form.
                      • The first consumer admits only the Workspace-local forms its host supplied. Generated Git push, PullRequest, Issue upsert, process, eval, exec, native command, credential, and arbitrary network effects remain refused.
                      • Mixed admitted and unadmitted syntax produces no generated effect.
                      • Rejected mutation syntax or authority produces no partial effect.
                      • Admitted mutations use their ordinary providers, transactions, and durable records; the evaluator creates no generic mutation result or receipt.
                      • as on <Evaluate> is valid for read, write, and mixed selections and binds the same { observations, output } shape. It neither widens admission nor exports the caller's bindings.
                      • Authored refusal before the write-enabled Evaluate produces no generated admission or mutation.
                      • Continuation compares the normalized classes, identities, forms, roots, and request ceilings before performing anything; completed replay repeats no mutation.
                      • Identity substitution, same-name repository components, middleware replacement, another loaded copy, and a changed host policy cannot turn a read identity into a write identity or manufacture either one.

                      Dependencies

                      Out of scope

                      • Direct Agent filesystem, terminal, MCP, or network access.
                      • ACP additionalDirectories.
                      • A document prop or public component that grants generated source authority; allow only narrows the host's captured policy.
                      • Mutating HTTP through <Fetch>.
                      • Generated local Git operations, Git-host mutations, Issue upserts, process execution, eval/exec, native commands, credentials, or an unspecified external-write class.
                      • Workflow-bundled Markdown component admission.
                      • <File.Delete> and its pinned write-policy integration; Add a contextual File.Delete component for contained file removal #567 owns both after this initial slice.
                      • Generated-source as, binding reads, or exporting a fragment-local binding environment.
                      • Synthetic mutation results or receipts.
                      • Partial execution before complete-fragment preflight.
                      • A hidden approval prompt inside <Evaluate>.
                      • Mandatory exact-fragment approval after a user already authorized the converged plan.
                      • Unattended approval of mutation proposals.
                      • Portable ACP adapter-level no-tool enforcement; Require ACP adapters to enforce tool-free workflow Agent sessions #496 owns it.

                      Activity

                      Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

                      Metadata

                      Metadata

                      Assignees

                      No one assigned

                        Labels

                        No labels
                        No labels

                        Projects

                        No projects

                          Relationships

                          None yet

                          Development

                          No branches or pull requests

                          Issue actions

                          , '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

                          Evaluate Agent-generated XMD through a constrained allowlist #369

                          Description

                          @taras

                          Story

                          As a workflow author, I want untrusted XMD returned by an Agent to be validated and executed by the host, so the Agent can request observations and propose changes without receiving direct Workspace or tool authority.

                          Delivery status

                          Slice 1: read-only observation admission — delivered

                          PR #497 supplies the provider-neutral generated-XMD admission boundary. PR #550 supplies the authored workflow seam:

                          <Evaluatesource={turn.source}as="observation" />

                          The trusted workflow host supplies the exact candidate source, immutable pinned observation identities, the retained Workspace roots and selected root, and exact request ceilings such as allowed HTTP requests. The complete fragment is preflighted before its first effect. Admitted observations execute through ordinary durable XMD effects, and the filtered source, decision, observation values, rendered output, identities, roots, and request policy are retained for replay.

                          <Evaluate> currently accepts only source; omitting the new allow prop below preserves this delivered behavior exactly.

                          Slice 2: Workspace mutation admission

                          Extend the same <Evaluate> seam rather than adding a second construct:

                          <!-- `allow` omitted: read-only, as today -->
                          <Evaluatesource={turn.source}as="observation" />
                          <!-- generated mutations confined to the run-owned Workspace -->
                          <Evaluate source={proposal.changes} allow={["write"]} />

                          allow is an optional non-empty set drawn from the closed values read and write. Omission means exactly ["read"]. Duplicate or unknown values are refused before the candidate source is parsed. The normalized set is retained with the admission.

                          The two values describe effect classes, not component identities:

                          • read selects observation-only identities. It includes core's pinned self-closing File:read identity and includes pinned Fetch only when the host supplied at least one exact request ceiling. Fetch remains the existing non-mutating GET/HEAD component; there is no mutating Fetch form.
                          • write selects mutations confined to the run-owned Workspace. This initial slice admits the exact pinned identities for lexical Dir and paired File:write. <File.Delete> and its pinned identity are owned by Add a contextual File.Delete component for contained file removal #567 and join the standard host policy only after both the ordinary component and this admission slice exist. The initial table excludes local Git even though those effects are also Workspace-local; Git-host, Issue, process, eval, exec, native-command, credential, and other external effects are outside the class entirely.

                          An authored document may request both classes, but same-name forms remain distinct identities. A self-closing <File /> resolves only to File:read; a paired <File>…</File> resolves only to File:write. Admission therefore resolves by the exact pinned definition and admitted form, not by the word File. A same-name repository component, registration, bundle member, or middleware answer satisfies neither identity.

                          The complete fragment is still preflighted before its first effect. A component, form, request, expression, interpolation, import, code block, or native execution outside the selected classes refuses the whole fragment before any generated effect. Admitted mutations then execute through their ordinary contextual providers, Workspace transactions, durability, and return semantics. Generated source receives no special mutation API.

                          Binding and output contract

                          allow and as are independent. allow selects effect classes from host policy; as is the language's ordinary caller-owned binding for the value <Evaluate> returns. A read-, write-, or mixed-policy invocation may be bound:

                          <Evaluate source={proposal.changes} allow={["write"]} as="evaluation" />

                          This does not grant authority and does not expose the caller's binding environment to generated source. <Evaluate> remains an ordinary function-component boundary: its return crosses to the caller only when the caller writes as, and an unbound non-string return renders nothing.

                          Slice 2 does not invent a generic mutation results collection. <Evaluate> keeps its delivered return shape:

                          {
                          "observations": [{ "name": "File", "value": "..." }],
                          "output": "..."
                          }

                          observations contains the ordinary values of admitted read identities, in invocation order. output contains what the generated fragment rendered. Admitted write identities do not acquire synthetic receipts or entries merely because they mutated the Workspace; their durable effect records remain the authoritative account. A write-only fragment therefore normally binds { "observations": [], "output": "" } with this slice's Dir and paired File:write table.

                          The generated-XMD preflight's delivered refusal of as inside generated source, and of generated binding reads, remains unchanged in this mutation slice. That restriction is separate from ordinary component semantics: authored write operations such as <Git.Commit ... as="commit" /> continue to bind their ordinary returns, and <Evaluate ... as="evaluation" /> remains valid for every allow selection. Exporting named fragment-local bindings would be a separate generated-language contract, including replay and collision semantics; this slice does not need it for #181.

                          Authored approval

                          Admission does not prompt and does not approve. Approval is ordinary authored control flow before a write-enabled <Evaluate> is reached.

                          For PR #181, the existing UserCheckpoint authorizes the converged plan before Implementation begins. That authorization is sufficient for the implementation Agent's generated Workspace changes; the workflow does not require a second approval of the literal fragment. A workflow that wants exact-fragment approval places its own elicitation or suspension gate immediately before <Evaluate allow={["write"]}>.

                          If the authored approval branch is not taken, the write-enabled <Evaluate> is never invoked and no generated admission or mutation exists.

                          Host policy and authority

                          Generated XMD is untrusted input. The Agent output carries data, never authorization. Only the trusted workflow host installs generated-XMD policy, as immutable values captured before any installation, middleware, or document code runs.

                          The host policy contains separate read and optional write tables. Each table holds exact pinned definitions and their form/request constraints. Slice 2 makes the standard Deno workflow profile supply the Workspace-local write table with lexical Dir and paired File:write. The profile supplies no external-write or execution table. Another host that supplied no write table refuses allow={["write"]} before parsing the generated source. #567 owns the ordinary <File.Delete> component and its later pinned-policy integration; deletion is not part of this initial slice, and #181 cannot consume it until #567 is delivered.

                          The authored allow prop selects a subset of that already-installed policy. It can narrow or request a class but cannot create a pinned identity, add a root, authorize a destination, attach a credential, or widen a request. Public middleware, document props, eval bindings, repository components, generated names, and another loaded package copy cannot manufacture admission.

                          Generated source receives none of the policy values. Network destination, method, headers, timeout, retained roots, selected root, exact component definitions, and credentials remain host-owned ceilings beneath the read and write labels.

                          Replay contract

                          One ordinary durable generated_xmd admission retains:

                          • the normalized allow selection;
                          • the exact admitted source;
                          • retained roots and selected root;
                          • every exact pinned identity and admitted form the fragment named; and
                          • normalized request constraints.

                          A continuation compares that policy whole and exactly before one generated component is invoked. A changed class, root, selected root, component definition, admitted form, or request ceiling refuses without performing an effect. The retained source, rather than a later caller's candidate, is what expands. Completed replay asks neither the Agent nor a provider to repeat completed work.

                          Integration with the workflow Agent

                          #302 supplies the retained Agent session and authored multi-turn loop:

                          1. The Agent returns an observation fragment.
                          2. <Evaluate> defaults to read, admits it under the host's read table, and returns its observation value.
                          3. The authored <Evaluate ... as="observation" /> invocation binds that value for a later Prompt in the same Agent session.
                          4. The Agent eventually returns one final proposal as source data.
                          5. Authored approval control reaches <Evaluate allow={["write"]}>.
                          6. The host intersects that request with its immutable write table and admits the proposal or refuses it whole.

                          The Agent receives no writable channel through this integration.

                          Acceptance

                          Existing read behavior

                          • <Evaluate source={source} /> is byte-for-byte and behaviorally compatible with the delivered read-only seam.
                          • Omitted allow normalizes to ["read"] and retains that selection.
                          • Pinned self-closing File:read and exact bounded Fetch continue to work; paired File, mutating/unknown HTTP methods, and requests outside the exact Fetch ceiling perform nothing.
                          • Bound read evaluation keeps the delivered { observations, output } value; unbound evaluation renders nothing.

                          Write behavior

                          • allow accepts only a non-empty, duplicate-free set of read and write; invalid policy is refused before candidate parsing or retention.
                          • allow={["write"]} fails before candidate parsing when the host installed no write table.
                          • The host's immutable write table, never the prop or generated source, selects the exact pinned mutation identities.
                          • A paired pinned File:write can mutate the selected run-owned Workspace through the ordinary Files provider and effect transaction.
                          • Self-closing and paired forms of the same component resolve to different pinned identities; selecting only one class never admits the other form.
                          • The first consumer admits only the Workspace-local forms its host supplied. Generated Git push, PullRequest, Issue upsert, process, eval, exec, native command, credential, and arbitrary network effects remain refused.
                          • Mixed admitted and unadmitted syntax produces no generated effect.
                          • Rejected mutation syntax or authority produces no partial effect.
                          • Admitted mutations use their ordinary providers, transactions, and durable records; the evaluator creates no generic mutation result or receipt.
                          • as on <Evaluate> is valid for read, write, and mixed selections and binds the same { observations, output } shape. It neither widens admission nor exports the caller's bindings.
                          • Authored refusal before the write-enabled Evaluate produces no generated admission or mutation.
                          • Continuation compares the normalized classes, identities, forms, roots, and request ceilings before performing anything; completed replay repeats no mutation.
                          • Identity substitution, same-name repository components, middleware replacement, another loaded copy, and a changed host policy cannot turn a read identity into a write identity or manufacture either one.

                          Dependencies

                          Out of scope

                          • Direct Agent filesystem, terminal, MCP, or network access.
                          • ACP additionalDirectories.
                          • A document prop or public component that grants generated source authority; allow only narrows the host's captured policy.
                          • Mutating HTTP through <Fetch>.
                          • Generated local Git operations, Git-host mutations, Issue upserts, process execution, eval/exec, native commands, credentials, or an unspecified external-write class.
                          • Workflow-bundled Markdown component admission.
                          • <File.Delete> and its pinned write-policy integration; Add a contextual File.Delete component for contained file removal #567 owns both after this initial slice.
                          • Generated-source as, binding reads, or exporting a fragment-local binding environment.
                          • Synthetic mutation results or receipts.
                          • Partial execution before complete-fragment preflight.
                          • A hidden approval prompt inside <Evaluate>.
                          • Mandatory exact-fragment approval after a user already authorized the converged plan.
                          • Unattended approval of mutation proposals.
                          • Portable ACP adapter-level no-tool enforcement; Require ACP adapters to enforce tool-free workflow Agent sessions #496 owns it.

                          Activity

                          Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

                          Metadata

                          Metadata

                          Assignees

                          No one assigned

                            Labels

                            No labels
                            No labels

                            Projects

                            No projects

                              Relationships

                              None yet

                              Development

                              No branches or pull requests

                              Issue actions

                              , '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

                              Evaluate Agent-generated XMD through a constrained allowlist #369

                              Description

                              @taras

                              Story

                              As a workflow author, I want untrusted XMD returned by an Agent to be validated and executed by the host, so the Agent can request observations and propose changes without receiving direct Workspace or tool authority.

                              Delivery status

                              Slice 1: read-only observation admission — delivered

                              PR #497 supplies the provider-neutral generated-XMD admission boundary. PR #550 supplies the authored workflow seam:

                              <Evaluatesource={turn.source}as="observation" />

                              The trusted workflow host supplies the exact candidate source, immutable pinned observation identities, the retained Workspace roots and selected root, and exact request ceilings such as allowed HTTP requests. The complete fragment is preflighted before its first effect. Admitted observations execute through ordinary durable XMD effects, and the filtered source, decision, observation values, rendered output, identities, roots, and request policy are retained for replay.

                              <Evaluate> currently accepts only source; omitting the new allow prop below preserves this delivered behavior exactly.

                              Slice 2: Workspace mutation admission

                              Extend the same <Evaluate> seam rather than adding a second construct:

                              <!-- `allow` omitted: read-only, as today -->
                              <Evaluatesource={turn.source}as="observation" />
                              <!-- generated mutations confined to the run-owned Workspace -->
                              <Evaluate source={proposal.changes} allow={["write"]} />

                              allow is an optional non-empty set drawn from the closed values read and write. Omission means exactly ["read"]. Duplicate or unknown values are refused before the candidate source is parsed. The normalized set is retained with the admission.

                              The two values describe effect classes, not component identities:

                              • read selects observation-only identities. It includes core's pinned self-closing File:read identity and includes pinned Fetch only when the host supplied at least one exact request ceiling. Fetch remains the existing non-mutating GET/HEAD component; there is no mutating Fetch form.
                              • write selects mutations confined to the run-owned Workspace. This initial slice admits the exact pinned identities for lexical Dir and paired File:write. <File.Delete> and its pinned identity are owned by Add a contextual File.Delete component for contained file removal #567 and join the standard host policy only after both the ordinary component and this admission slice exist. The initial table excludes local Git even though those effects are also Workspace-local; Git-host, Issue, process, eval, exec, native-command, credential, and other external effects are outside the class entirely.

                              An authored document may request both classes, but same-name forms remain distinct identities. A self-closing <File /> resolves only to File:read; a paired <File>…</File> resolves only to File:write. Admission therefore resolves by the exact pinned definition and admitted form, not by the word File. A same-name repository component, registration, bundle member, or middleware answer satisfies neither identity.

                              The complete fragment is still preflighted before its first effect. A component, form, request, expression, interpolation, import, code block, or native execution outside the selected classes refuses the whole fragment before any generated effect. Admitted mutations then execute through their ordinary contextual providers, Workspace transactions, durability, and return semantics. Generated source receives no special mutation API.

                              Binding and output contract

                              allow and as are independent. allow selects effect classes from host policy; as is the language's ordinary caller-owned binding for the value <Evaluate> returns. A read-, write-, or mixed-policy invocation may be bound:

                              <Evaluate source={proposal.changes} allow={["write"]} as="evaluation" />

                              This does not grant authority and does not expose the caller's binding environment to generated source. <Evaluate> remains an ordinary function-component boundary: its return crosses to the caller only when the caller writes as, and an unbound non-string return renders nothing.

                              Slice 2 does not invent a generic mutation results collection. <Evaluate> keeps its delivered return shape:

                              {
                              "observations": [{ "name": "File", "value": "..." }],
                              "output": "..."
                              }

                              observations contains the ordinary values of admitted read identities, in invocation order. output contains what the generated fragment rendered. Admitted write identities do not acquire synthetic receipts or entries merely because they mutated the Workspace; their durable effect records remain the authoritative account. A write-only fragment therefore normally binds { "observations": [], "output": "" } with this slice's Dir and paired File:write table.

                              The generated-XMD preflight's delivered refusal of as inside generated source, and of generated binding reads, remains unchanged in this mutation slice. That restriction is separate from ordinary component semantics: authored write operations such as <Git.Commit ... as="commit" /> continue to bind their ordinary returns, and <Evaluate ... as="evaluation" /> remains valid for every allow selection. Exporting named fragment-local bindings would be a separate generated-language contract, including replay and collision semantics; this slice does not need it for #181.

                              Authored approval

                              Admission does not prompt and does not approve. Approval is ordinary authored control flow before a write-enabled <Evaluate> is reached.

                              For PR #181, the existing UserCheckpoint authorizes the converged plan before Implementation begins. That authorization is sufficient for the implementation Agent's generated Workspace changes; the workflow does not require a second approval of the literal fragment. A workflow that wants exact-fragment approval places its own elicitation or suspension gate immediately before <Evaluate allow={["write"]}>.

                              If the authored approval branch is not taken, the write-enabled <Evaluate> is never invoked and no generated admission or mutation exists.

                              Host policy and authority

                              Generated XMD is untrusted input. The Agent output carries data, never authorization. Only the trusted workflow host installs generated-XMD policy, as immutable values captured before any installation, middleware, or document code runs.

                              The host policy contains separate read and optional write tables. Each table holds exact pinned definitions and their form/request constraints. Slice 2 makes the standard Deno workflow profile supply the Workspace-local write table with lexical Dir and paired File:write. The profile supplies no external-write or execution table. Another host that supplied no write table refuses allow={["write"]} before parsing the generated source. #567 owns the ordinary <File.Delete> component and its later pinned-policy integration; deletion is not part of this initial slice, and #181 cannot consume it until #567 is delivered.

                              The authored allow prop selects a subset of that already-installed policy. It can narrow or request a class but cannot create a pinned identity, add a root, authorize a destination, attach a credential, or widen a request. Public middleware, document props, eval bindings, repository components, generated names, and another loaded package copy cannot manufacture admission.

                              Generated source receives none of the policy values. Network destination, method, headers, timeout, retained roots, selected root, exact component definitions, and credentials remain host-owned ceilings beneath the read and write labels.

                              Replay contract

                              One ordinary durable generated_xmd admission retains:

                              • the normalized allow selection;
                              • the exact admitted source;
                              • retained roots and selected root;
                              • every exact pinned identity and admitted form the fragment named; and
                              • normalized request constraints.

                              A continuation compares that policy whole and exactly before one generated component is invoked. A changed class, root, selected root, component definition, admitted form, or request ceiling refuses without performing an effect. The retained source, rather than a later caller's candidate, is what expands. Completed replay asks neither the Agent nor a provider to repeat completed work.

                              Integration with the workflow Agent

                              #302 supplies the retained Agent session and authored multi-turn loop:

                              1. The Agent returns an observation fragment.
                              2. <Evaluate> defaults to read, admits it under the host's read table, and returns its observation value.
                              3. The authored <Evaluate ... as="observation" /> invocation binds that value for a later Prompt in the same Agent session.
                              4. The Agent eventually returns one final proposal as source data.
                              5. Authored approval control reaches <Evaluate allow={["write"]}>.
                              6. The host intersects that request with its immutable write table and admits the proposal or refuses it whole.

                              The Agent receives no writable channel through this integration.

                              Acceptance

                              Existing read behavior

                              • <Evaluate source={source} /> is byte-for-byte and behaviorally compatible with the delivered read-only seam.
                              • Omitted allow normalizes to ["read"] and retains that selection.
                              • Pinned self-closing File:read and exact bounded Fetch continue to work; paired File, mutating/unknown HTTP methods, and requests outside the exact Fetch ceiling perform nothing.
                              • Bound read evaluation keeps the delivered { observations, output } value; unbound evaluation renders nothing.

                              Write behavior

                              • allow accepts only a non-empty, duplicate-free set of read and write; invalid policy is refused before candidate parsing or retention.
                              • allow={["write"]} fails before candidate parsing when the host installed no write table.
                              • The host's immutable write table, never the prop or generated source, selects the exact pinned mutation identities.
                              • A paired pinned File:write can mutate the selected run-owned Workspace through the ordinary Files provider and effect transaction.
                              • Self-closing and paired forms of the same component resolve to different pinned identities; selecting only one class never admits the other form.
                              • The first consumer admits only the Workspace-local forms its host supplied. Generated Git push, PullRequest, Issue upsert, process, eval, exec, native command, credential, and arbitrary network effects remain refused.
                              • Mixed admitted and unadmitted syntax produces no generated effect.
                              • Rejected mutation syntax or authority produces no partial effect.
                              • Admitted mutations use their ordinary providers, transactions, and durable records; the evaluator creates no generic mutation result or receipt.
                              • as on <Evaluate> is valid for read, write, and mixed selections and binds the same { observations, output } shape. It neither widens admission nor exports the caller's bindings.
                              • Authored refusal before the write-enabled Evaluate produces no generated admission or mutation.
                              • Continuation compares the normalized classes, identities, forms, roots, and request ceilings before performing anything; completed replay repeats no mutation.
                              • Identity substitution, same-name repository components, middleware replacement, another loaded copy, and a changed host policy cannot turn a read identity into a write identity or manufacture either one.

                              Dependencies

                              Out of scope

                              • Direct Agent filesystem, terminal, MCP, or network access.
                              • ACP additionalDirectories.
                              • A document prop or public component that grants generated source authority; allow only narrows the host's captured policy.
                              • Mutating HTTP through <Fetch>.
                              • Generated local Git operations, Git-host mutations, Issue upserts, process execution, eval/exec, native commands, credentials, or an unspecified external-write class.
                              • Workflow-bundled Markdown component admission.
                              • <File.Delete> and its pinned write-policy integration; Add a contextual File.Delete component for contained file removal #567 owns both after this initial slice.
                              • Generated-source as, binding reads, or exporting a fragment-local binding environment.
                              • Synthetic mutation results or receipts.
                              • Partial execution before complete-fragment preflight.
                              • A hidden approval prompt inside <Evaluate>.
                              • Mandatory exact-fragment approval after a user already authorized the converged plan.
                              • Unattended approval of mutation proposals.
                              • Portable ACP adapter-level no-tool enforcement; Require ACP adapters to enforce tool-free workflow Agent sessions #496 owns it.

                              Activity

                              Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

                              Metadata

                              Metadata

                              Assignees

                              No one assigned

                                Labels

                                No labels
                                No labels

                                Projects

                                No projects

                                  Relationships

                                  None yet

                                  Development

                                  No branches or pull requests

                                  Issue actions