Skip to content

feat(chat): route shared deploy intents to the Assistant with fail-closed validation - #286

Open
zjy365 wants to merge 1 commit into
mainfrom
codex/deploy-intent-standard
Open

feat(chat): route shared deploy intents to the Assistant with fail-closed validation#286
zjy365 wants to merge 1 commit into
mainfrom
codex/deploy-intent-standard

Conversation

@zjy365

@zjy365 zjy365 commented Aug 21, 2026

Copy link
Copy Markdown
Collaborator

Summary

External entry points (Template site, GitHub one-click deploy, blogs, solution pages) can land users in Brain with:

?openapp=system-brain&side=<encoded-side>&intent=<encoded-json>

side keeps the existing UI navigation untouched; intent carries 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:

  • New data-deployIntent UI message part + zod schema/reader (version 1; template / github / topic payloads with bounded fields).
  • Message-level context bridge (same shape as data-selectedResource, ADR-0044): renders an attribute-escaped <deploy_intent … /> block labeled data, not instructions, injected before convertToModelMessages; never enters the system prompt.
  • Fail-closed inbound validation on POST /api/chat for every source (including attacker-forged parts): at most one valid intent; template must exist verbatim in listTemplateCatalog, args whitelisted to catalog-declared inputs with type checks and isSensitiveDeploymentInput stripping; github aligned with chatDeploymentTaskSourceSchema (legal HTTPS github.com URL, owner/repo, branch); topic bounded free text. Any failure or catalog outage drops the part(s) without blocking normal chat.
  • Client one-time consumption: the entry-URL intent becomes a data-deployIntent part on a synthetic first user message; history.replaceState drops the param, and a chatId + raw session marker prevents Strict Mode / re-render / refresh duplicates. Existing side behavior is preserved.
  • Agent behavior: searchDeployCatalog for 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.
  • ADR-0065 documents the trust-level difference vs 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 pass
  • bun check (ultracite): clean
  • cd 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

External follow-ups (not in this repo)

  • Launcher must pass side and intent query params through to the Brain iframe (the accepted fragile assumption); Brain exposes a direct-URL path + unit tests.
  • External producers build links per the contract in apps/ui/src/features/deploy/deploy-intent-link.ts and ADR-0065: {version:1, kind:"template"|"github"|"topic", source?, payload:…}; no secrets/tokens/kubeconfig; blog content only via query/ref.

…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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant