Skip to content

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

@jamesadevine

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions