feat(chat): route shared deploy intents to the Assistant with fail-closed validation - #286
Open
zjy365 wants to merge 1 commit into
Open
feat(chat): route shared deploy intents to the Assistant with fail-closed validation#286zjy365 wants to merge 1 commit into
zjy365 wants to merge 1 commit into
Conversation
…osed validation External entry points (Template site, GitHub one-click deploy, blogs, solution pages) can land users in Brain with `?side=...&intent=<encoded-json>`. The intent is converted once on the client into a `data-deployIntent` part on a synthetic first user message, dropped from the URL via history.replaceState, and re-validated fail-closed on POST /api/chat before the model ever sees it. - new data-deployIntent part + zod schema/reader (version 1; template/github/ topic payloads with bounded fields) - message-level bridge (like data-selectedResource, ADR-0044) that renders an escaped `<deploy_intent>` block labeled data-not-instructions; never system prompt - inbound scrub: at most one valid intent; template name must exist in listTemplateCatalog, args whitelisted by catalog inputs with type checks and isSensitiveDeploymentInput stripping; github aligned with chatDeploymentTaskSourceSchema (HTTPS github.com URL, owner/repo, branch); topic bounded free text. Any failure or catalog outage drops the part(s) without blocking normal chat. - agent behavior: searchDeployCatalog tool for catalog disambiguation, deployment-source routing prompt, and GitHub task creation from chat via the existing engine flow (credential binding / public-repo check) - ADR-0065 records the trust-level difference vs data-selectedResource, untrusted external URLs, fail-closed validation, secret ban, and final confirmation rules
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
External entry points (Template site, GitHub one-click deploy, blogs, solution pages) can land users in Brain with:
sidekeeps the existing UI navigation untouched;intentcarries Agent context that must reach the right-hand Chat Agent and let the user complete the deployment there.This PR implements the Deployment Intent standard:
data-deployIntentUI message part + zod schema/reader (version 1;template/github/topicpayloads with bounded fields).data-selectedResource, ADR-0044): renders an attribute-escaped<deploy_intent … />block labeled data, not instructions, injected beforeconvertToModelMessages; never enters the system prompt.POST /api/chatfor every source (including attacker-forged parts): at most one valid intent;templatemust exist verbatim inlistTemplateCatalog, args whitelisted to catalog-declared inputs with type checks andisSensitiveDeploymentInputstripping;githubaligned withchatDeploymentTaskSourceSchema(legal HTTPS github.com URL, owner/repo, branch);topicbounded free text. Any failure or catalog outage drops the part(s) without blocking normal chat.data-deployIntentpart on a synthetic first user message;history.replaceStatedrops the param, and achatId + rawsession marker prevents Strict Mode / re-render / refresh duplicates. Existingsidebehavior is preserved.searchDeployCatalogfor catalog disambiguation, deployment-source routing in the system prompt, and GitHub task creation from chat via the existing engine flow (credential binding when present, public-repo check when unbound). The intent is context, not a direct deploy command — tool approval/confirmation semantics unchanged.data-selectedResource, untrusted external URLs, fail-closed validation, the secret ban, and final confirmation rules.No new DB tables, migrations, env vars, or services.
Testing
bun typecheck(turbo, full monorepo): all tasks passbun check(ultracite): cleancd apps/ui && bun test src/features/chat src/app/api/chat src/features/deploy: 757 pass / 0 fail (schema/parse, template valid/forged/catalog-unavailable, args whitelist + sensitive, github legal/illegal, topic bounds, duplicate parts, injection escaping, URL one-time consumption + replaceState, ordinary-chat regression, route inbound keep/drop, bridge, github-from-chat)Relationship to related PRs
codex/github-repo-deploy-link) — GitHub one-click deploy deep-link. Kept compatible:side=github-deployment:*/project-creation:githubDirectbehavior unchanged; this PR only adds theintentparam and reuses the existing GitHub deployment task engine (no re-implementation).feat/chat-deploy-intent-routing) — overlaps onsearchDeployCatalog+ GitHub-from-chat + routing prompt. This PR implements the same tool name/semantics with the fail-closed intent protocol and ADR; if feat(ui): route chat deploy intent through the template catalog #281 merges first, its tool content can be folded into this standard (no protocol conflict).External follow-ups (not in this repo)
sideandintentquery params through to the Brain iframe (the accepted fragile assumption); Brain exposes a direct-URL path + unit tests.apps/ui/src/features/deploy/deploy-intent-link.tsand ADR-0065:{version:1, kind:"template"|"github"|"topic", source?, payload:…}; no secrets/tokens/kubeconfig; blog content only viaquery/ref.