From fa0af5617064977077d1a7610e786a84bbbab8df Mon Sep 17 00:00:00 2001 From: Neil Date: Fri, 11 Sep 2026 02:45:52 -0700 Subject: [PATCH] fix(runtime): an identifier is not a credential, and a comment is not decoration Two more false positives of the same family as the ternary one, found by crossing the detector's own vocabulary against the shapes a term takes when it is output rather than a prompt. `candidate.credential` set the credential-noun flag: only the auth-verb regex had been taught that a dot means a property access. That flag corroborates an otherwise-inert bottom row, so an identifier alone could refuse a screen that was only printing source. Excluded `.` and `.` only -- `\w` would break the env-var ask (`OPENAI_API_KEY`), which is a real prompt. ` // Sign in with the provider console to continue` read as a dialog leading its row, because the decoration run before the action phrase accepted a comment marker. A dialog decorates its instruction with box rules, bullets, carets and menu numbers, never with `//` or `*`. Both pinned, and the #19749 sign-in dialog and auth-method menu still refuse. --- ...rminal-credential-prompt-detection.test.ts | 11 +++++++++ .../terminal-credential-prompt-detection.ts | 24 ++++++++++++------- 2 files changed, 27 insertions(+), 8 deletions(-) diff --git a/src/main/runtime/terminal-credential-prompt-detection.test.ts b/src/main/runtime/terminal-credential-prompt-detection.test.ts index f1bdd6e803c..88b58f4b907 100644 --- a/src/main/runtime/terminal-credential-prompt-detection.test.ts +++ b/src/main/runtime/terminal-credential-prompt-detection.test.ts @@ -603,6 +603,17 @@ const LEGITIMATE_AGENT_SCREENS: readonly (readonly [string, string[]])[] = [ ], // No caret, and the bottom row DOES lead with an action phrase, so the only thing keeping this // from reading as a live prompt is that `candidate.login` is a property access. + [ + 'source comment leading with an action phrase, corroborated by the comment above it', + [ + ' // See the authentication guide for details', + ' // Sign in with the provider console to continue' + ] + ], + [ + 'source rows where a .credential access is the only would-be credential noun', + [' const owner = candidate.credential', ' Enter the code below to finish linking'] + ], [ 'source rows where a .login access is the only would-be auth verb', [ diff --git a/src/main/runtime/terminal-credential-prompt-detection.ts b/src/main/runtime/terminal-credential-prompt-detection.ts index bac539c7ff6..c336399882b 100644 --- a/src/main/runtime/terminal-credential-prompt-detection.ts +++ b/src/main/runtime/terminal-credential-prompt-detection.ts @@ -40,11 +40,15 @@ const MAX_CREDENTIAL_LINE_LENGTH = 512 // segment before it is handled by the prefix rules, not here — a variable-length prefix inside // a noun alternation makes this whole pattern quadratic on long lines, and it runs per retained // tail line at streaming rate. -const CREDENTIAL_NOUN_SOURCE = +export const CREDENTIAL_NOUN_SOURCE = 'password|passphrase|api[ _-]?keys?|access[ _-]tokens?|auth(?:orization)?[ _-]tokens?|bearer tokens?|personal access tokens?|secret keys?|client secrets?|one[- ]time (?:code|password)|otp|verification codes?|authentication codes?|security codes?|2fa codes?|device codes?|credentials?' -const CREDENTIAL_NOUN_RE = new RegExp(CREDENTIAL_NOUN_SOURCE, 'gi') -const CREDENTIAL_NOUN_ANYWHERE_RE = new RegExp(CREDENTIAL_NOUN_SOURCE, 'i') +// Why the lookbehind: `candidate.credential`, `settings.apiKey` and `config.password` are property +// accesses. The noun is what corroborates an otherwise-inert bottom row, so an identifier reading +// as the noun is enough on its own to refuse a screen that is only printing source. Only `.` is +// excluded, not all of `\w` — the env-var form (`OPENAI_API_KEY`) is a real ask. +const CREDENTIAL_NOUN_RE = new RegExp(`(?` are // property accesses, not auth wording, and `\b` treats `.` as a boundary. Agents print this // repo's own source constantly. -const AUTH_VERB_RE = - /(?