Skip to content

Inject runtime parameter values into the agent prompt via ado-script (sanitization at runtime, not compile time) #432

Description

@jamesadevine

Motivation

We want authors to flag a runtime template parameter as "feed this value into the agent prompt", so its value can vary per-run (manual queue or queue-build safe output) without re-compiling the pipeline. A first attempt (#430, since reverted) put both the sanitization rules and the prompt-append logic into the Rust compiler, generating bash heredocs and validating defaults at ado-aw compile time. That coupled the security policy to compiler releases, duplicated work the runtime already has to do for queue-build-supplied values, and made the bash hard to test in isolation.

This issue tracks the redo: keep the compiler dumb (just pass values through env vars), and let the ado-script runtime bundle (the same one that ships gate-eval.py today, and prompt.js / gate.js going forward) own validation and prompt assembly.

Proposed design

Front matter (compiler)

Add an opt-in flag on PipelineParameter:

parameters:
  - name: focusArea
    displayName: "Focus area for this run"
    type: string
    default: "no specific focus"
    prompt-context: true

Compiler responsibilities, and only these:

  • Parse the new prompt-context: bool field (kebab-case, skip_serializing so it never leaks into emitted ADO YAML).
  • For each opted-in parameter, emit a step-level env: mapping ADO_AW_CTX_<UPPER_NAME>: ${{ parameters.<name> }} on the existing "Prepare agent prompt" step.
  • Emit one additional env var, ADO_AW_PROMPT_CONTEXT_SPEC, holding a small base64-encoded JSON spec: an ordered list of { envKey, displayName } entries plus the prompt-file path. (Mirrors the ADO_AW_PROMPT_SPEC pattern used by prompt.js.)
  • No bash generation. No printf heredocs. No validation of default values at compile time — the runtime will validate the same way it validates queue-build-supplied parameter values.

Runtime (ado-script)

Sanitization and injection live in a new module under scripts/ado-script/src/prompt-context/ (TypeScript, NodeNext imports with .js, consistent with prompt/ and gate/). Wired into prompt.js so it runs as part of the existing prompt-render step — no new pipeline step, no extra script download.

Validation rules (applied per value at runtime; reject the run on violation):

  • Bounded byte length (≤ 4 KiB) and bounded newline count (≤ 64).
  • Reject ADO expression syntax: ${{, $(, $[.
  • Reject pipeline logging commands: ##vso[, ##[.
  • Reject the compiler's template-marker delimiter {{.
  • Reject CR and non-tab control characters.
  • displayName (validated when the spec is decoded) rejects ", \, `, $.

On success, append a clearly delimited "Additional Run Context" section to the rendered prompt — one sub-section per non-empty value, using displayName as the header — with a preamble that explicitly tells the agent the section is untrusted user input.

Why ado-script, not the compiler

  • Single source of truth for the policy: the same sanitizer that already gates queue-build parameter values can be reused, instead of two implementations drifting.
  • Policy updates ship as scripts.zip releases (already verified via checksums.txt + sha256sum, see src/compile/extensions/trigger_filters.rs:102-109); no compiler release required.
  • Testable in TS with the rest of the runtime; the compiler stays free of bash codegen.
  • Defaults that look fine statically but resolve through ADO expression evaluation can still be caught (the runtime sees the post-evaluation string).

Acceptance criteria

  • prompt-context: true round-trips through front-matter and is stripped from emitted YAML.
  • Compiled pipelines with no opted-in parameter are byte-equivalent to today's output.
  • Compiled pipelines with ≥ 1 opted-in parameter add the ADO_AW_CTX_* env vars and ADO_AW_PROMPT_CONTEXT_SPEC to the "Prepare agent prompt" step, with no other changes.
  • prompt.js decodes the spec, validates each value with the rules above, and either appends the section or fails the step with a clear error.
  • Manual queue and queue-build paths both work end-to-end.
  • Tests cover each rejection rule in TS, plus a Rust test confirming the env mapping and spec are emitted (and absent when no parameter opts in).
  • docs/parameters.md, docs/front-matter.md, and docs/ado-script.md describe the feature and the runtime-side validation contract.

Out of scope

  • Changing how queue-build allowlists or validates parameters (already covered by allowed-parameters + expression-syntax rejection).
  • Surfacing values to anything other than the agent prompt.

References

  • Reverted attempt: commit 131c6ed on copilot/optional-context-parameter (revert in 461b443).
  • Existing scripts.zip delivery pattern: src/compile/extensions/trigger_filters.rs:102-109.
  • Existing runtime spec-via-base64-env pattern: prompt.js and ADO_AW_PROMPT_SPEC.

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

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions