feat: add prompt input for custom automation runs#103
Conversation
Factory-managed Custom CI automations generate workflows that pass a free-text task prompt, but the action had no input to receive it, so scheduled runs did nothing. Add a prompt input that runs droid exec directly with the given task on automation events (schedule/workflow_dispatch/repository_dispatch/ workflow_run) and on entity events carrying no @droid command; explicit commands and automatic-review flows keep precedence. Entity-triggered prompt runs keep the human-actor and write-permission checks. Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
|
Droid finished @eric-factory's task —— View job The PR cleanly adds a custom-automation prompt mode and wiring, with sensible precedence over existing tag and review flows. The main correctness risk is that |
| factory_api_key: "", | ||
| github_token: "", | ||
| droid_args: "", | ||
| prompt: "", |
There was a problem hiding this comment.
[P1] Fix action input presence detection for missing keys
collectActionInputsPresence() treats missing keys as an empty string (allInputs[name] || ""), which means any key absent from toJson(inputs) but with a non-empty default gets incorrectly marked as "present" ("" !== defaultValue). Since inputDefaults includes keys that are not defined as action inputs in action.yml (e.g. mode, branch_prefix, allowed_tools), action_inputs_present can become unreliable for downstream consumers.
| DROID_ARGS: ${{ inputs.droid_args }} | ||
| PROMPT: ${{ inputs.prompt }} | ||
| INCLUDE_SUGGESTIONS: ${{ inputs.include_suggestions }} | ||
| ALL_INPUTS: ${{ toJson(inputs) }} |
There was a problem hiding this comment.
[P2] Avoid serializing full custom prompt into ALL_INPUTS
With the new prompt input, ALL_INPUTS: ${{ toJson(inputs) }} can now embed arbitrary multi-line free text into an environment variable even though collectActionInputsPresence() only needs default-vs-present booleans. This can create brittle failures with very large prompts (environment size limits) and increases the blast radius of sensitive prompt content, so it would be safer to compute presence directly from known inputs or omit prompt from the serialized blob.
| const promptContent = buildCustomAutomationPrompt(context); | ||
|
|
||
| console.log("===== CUSTOM AUTOMATION PROMPT ====="); | ||
| console.log(promptContent); | ||
| console.log("===================================="); | ||
|
|
||
| const promptDir = `${process.env.RUNNER_TEMP || "/tmp"}/droid-prompts`; | ||
| await mkdir(promptDir, { recursive: true }); | ||
| await writeFile(`${promptDir}/droid-prompt.txt`, promptContent); |
There was a problem hiding this comment.
[P2] [security] Avoid logging raw custom automation prompt content
prepareCustomAutomationMode() logs the full generated prompt (including the user-provided task) to stdout. For custom automations this prompt can contain sensitive internal details, and GitHub Actions runners interpret certain log lines (for example ones starting with ::) as workflow commands. Consider avoiding verbatim logging, sanitizing ::-style sequences, and/or redacting or truncating prompt content before writing it to logs.
Description
Adds a
promptinput so the action can run arbitrary custom automation tasks. This is the runtime half of Factory's Custom CI automations (factory-mono#16011): the Automations UI generates workflows that pass the user's task asprompt:, but the action previously had no such input, so scheduled Custom automations ran the action and did nothing.Behavior:
promptinput (PROMPTenv into the prepare step) parsed ontocontext.inputs.prompt.src/custom-automation/mode: writes the task (with an environment preamble: repo, triggering event, non-interactive expectations, open-a-PR-instead-of-pushing guidance) to the standard prompt file and passes userdroid_argsthrough untouched (this is how the Factory UI carries the-m <model>selection). No tool restrictions are imposed; the task is arbitrary by design.contains_triggeris nowshouldTriggerTag || shouldTriggerCustomAutomation. Dispatch insrc/prepare/index.tsgives the tag/review flows strict precedence: explicit@droidcommands andautomatic_review/automatic_security_reviewalways win; the prompt only runs when no tag/review flow claimed the event.schedule,workflow_dispatch,repository_dispatch,workflow_run) and on entity events that carry no command. Entity-triggered prompt runs keep the existing security posture:checkWritePermissions(prepare entrypoint) pluscheckHumanActor(bots only when allow-listed).prompt+droid_argsdocumented in Core Inputs, plus a Custom Automations section with a scheduled example.Backwards compatible: workflows without a
promptbehave exactly as before (automation events still producecontains_trigger=falseand skip).How Has This Been Tested?
bun test: 460 passing, including newtest/custom-automation.test.tscovering trigger detection (automation events, whitespace-only prompts, command-less entity events, precedence of automatic review), prompt-file content,droid_argspass-through, entity descriptions, and bot-actor rejection.tsc --noEmitclean;prettierapplied.