diff --git a/docs/cron-guide.md b/docs/cron-guide.md index 3ea21f62..e8961fdd 100644 --- a/docs/cron-guide.md +++ b/docs/cron-guide.md @@ -208,9 +208,9 @@ trailing carriage return: | `paste` | `session.write()` — writes directly to the PTY/mux | Bulk paste-style delivery. | > ⚠️ **Single-line only — enforced.** A job fires unattended, and the submit -> check that re-presses an ignored Enter cannot recognise a multi-line paste in -> the composer, so a stranded prompt would go unnoticed (in paste mode the -> newlines reach the CLI as keys). So newlines are **rejected**: the +> check that re-presses an ignored Enter cannot reliably recognise a multi-line +> paste in the composer, so a stranded prompt would go unnoticed (in paste mode +> the newlines reach the CLI as keys). So newlines are **rejected**: the > schema and the form refuse a multi-line `promptText`, and at fire time a > prompt file whose content is multi-line (after stripping trailing newlines) > fails the run with a clear `errorMessage`. Put multi-line instructions in a diff --git a/docs/wiki/Cron-Jobs.md b/docs/wiki/Cron-Jobs.md index 36d04993..a3d49c1d 100644 --- a/docs/wiki/Cron-Jobs.md +++ b/docs/wiki/Cron-Jobs.md @@ -52,9 +52,9 @@ or a server restart shifts later runs slightly. That drift is accepted rather th ## Prompts are single line This is the rule people trip over. A cron job fires with nobody watching, and the check that -re-presses an Enter the agent ignored cannot recognise a multi-line paste in the composer, so -a multi-line prompt could sit there unsent. In `paste` mode the newlines would reach the agent -as keys. So a multi-line prompt is **rejected**: the form refuses it, and a prompt file whose +re-presses an Enter the agent ignored cannot reliably recognise a multi-line paste in the +composer, so a multi-line prompt could sit there unsent. In `paste` mode the newlines would +reach the agent as keys. So a multi-line prompt is **rejected**: the form refuses it, and a prompt file whose contents are multi-line fails the run with a clear message. For anything longer than a sentence, put the instructions in a file and make the prompt tell diff --git a/src/cron/cron-service.ts b/src/cron/cron-service.ts index b1d33cd6..d2b6f753 100644 --- a/src/cron/cron-service.ts +++ b/src/cron/cron-service.ts @@ -97,7 +97,7 @@ const CRON_PSEUDO_FS_TREES: readonly string[] = ['/proc', '/sys', '/dev']; /** Sync blocklist for the create/update workingDir gate (no settings extras). */ const CRON_WORKING_DIR_BLOCKED_TREES: readonly string[] = [...DEFAULT_BLOCKED_TREES, ...CRON_PSEUDO_FS_TREES]; -/** Prompt delivery is single-line only: the submit check cannot recognise a multi-line paste. */ +/** Prompt delivery is single-line only: the submit check cannot reliably recognise a multi-line paste. */ const HAS_NEWLINE = /[\r\n]/; /** The three session calls prompt delivery needs, so it can be tested without a PTY. */ @@ -538,7 +538,7 @@ export class CronService { /** * Resolves the prompt text and enforces the single-line constraint: a job * fires unattended, and the submit check that re-presses an ignored Enter - * cannot recognise a multi-line paste in the composer (typed mode), so a + * cannot reliably recognise a multi-line paste in the composer (typed mode), so a * stranded prompt would go unnoticed; in paste mode the newlines reach the * CLI as keys. Rather than risk an unattended agent's instructions, fail the * run with a clear error. A prompt FILE may end with trailing newline(s) diff --git a/src/web/schemas.ts b/src/web/schemas.ts index 0a276d3c..2f3fcc86 100644 --- a/src/web/schemas.ts +++ b/src/web/schemas.ts @@ -1905,7 +1905,7 @@ export const ScheduledRunSchema = z.object({ /** 'HH:MM' 24-hour time. */ const hhmmSchema = z.string().regex(/^([01]?\d|2[0-3]):[0-5]\d$/, 'Time must be HH:MM (24-hour)'); -/** Prompt delivery is single-line only (the submit check cannot see a multi-line paste) — reject newlines outright. */ +/** Prompt delivery is single-line only (the submit check cannot reliably recognise a multi-line paste) — reject newlines outright. */ const noNewlines = (v: string) => !/[\r\n]/.test(v); /** Shared field shape for creating/updating a scheduled job. */