docs: merge language-split agent integrations and close docs gaps - #919
Merged
Conversation
Claude Agent SDK, Claude Managed Agents, LangChain, OpenAI Agents, and Strands Agents each shipped as two adapters, two docs pages, and two `/sdk/` routes – one per language. Readers had to pick the language from a framework switcher before they could see whether the integration existed at all. Each integration is now a single page that selects the language with the `language`-synced tabs already used elsewhere in the docs, matching the Claude Managed Agents page that was written this way from the start. This covers get started, /guards/quick-start, the integration reference pages, and every building block that shipped a LangChain snippet. - Retire the `-py` and `-js` framework keys. `MERGED_GUARD_SDK_KEYS` maps each retired key to its merged adapter and generates the Astro and Vercel redirects, so published `/guards/:adapter-py`, `/sdk/:adapter-py/...`, and `?f=` URLs keep working. - Reorganize the paired snippet directories into `js/` and `py/` subdirectories with a tabbed wrapper at the top level, so page imports are unchanged. - Add the OpenAI Agents Python get started snippets, which were missing, so the merged page has both tabs. - Hoist `## 4. Start app` and the cross-reference out of the get started tabs to keep the heading anchor and avoid a duplicate link. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Rate limiting, sensitive information, content moderation, prompt injection, and the AI protection guides each shipped agent examples for only some adapters. A reader on CrewAI, Google ADK, Strands Agents, TanStack AI, or Claude Managed Agents saw the integration in the guards section but not in the building block that describes the rule they wanted, so the framework switcher had no entry for their stack. Add a worked example for each of those five adapters to: - /rate-limiting/quick-start and /rate-limiting/reference - /sensitive-info/quick-start - /content-moderation and /content-moderation/quick-start - /prompt-injection/quick-start - /ai-protection/abuse-protection, /ai-protection/budget-control, /ai-protection/data-loss-prevention, and /ai-protection/prompt-injection Each example uses the adapter's real deny point rather than a generic one: `register_arcjet_hooks` on `PRE_TOOL_CALL` for CrewAI, `guardPlugin` for Google ADK, `guardMiddleware` for TanStack AI, `guardTool` plus `guardHooks` for Strands Agents, and `guardCustomTool` plus `guardEvents` for Claude Managed Agents. Google ADK and TanStack AI have no `guardTool`, so their per-tool rules are selected by tool name inside the plugin or middleware. Strands Agents and Claude Managed Agents ship both languages, so those snippets use language tabs. The request-based reference pages (bot protection, email validation, filters, shield, signup protection, and the sensitive information reference) stay HTTP-only, because those rules have no Guard equivalent. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The two files had drifted from the docs in both directions: they still listed the retired language-specific agent guard pages, and they left out whole sections that had been added since they were last reviewed. llms.txt: - Collapse the split agent guard entries into one per integration and name the language each adapter covers. - Add the SDK-scoped agent guard quick starts next to the HTTP ones, and say which URL shape reaches a building block for an adapter. - Record that the retired `-py` and `-js` URLs redirect. - Add AI protection, which was missing from Features entirely. - Add sections for the utility packages, the third-party integrations, and the blueprints, none of which appeared. llms-full.txt: - Add an agent framework adapter table with the import path and deny point per framework, so a model reading this file doesn't reach for a raw `guard()` call inside a framework that owns the tool loop. - State the fail-closed contract and that human-in-the-loop confirmation is not a policy gate, since both are easy to get wrong. - Fix the reference list: merged adapter links, plus the guard quick start, remote policies, reference, AI protection, Nosecone, and `@arcjet/redact` pages that were missing. Every `docs.arcjet.com` URL in both files resolves against a built page. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Ran `.claude/skills/arcjet-writing-style` over every page in `src/content/docs` and `src/snippets`, then fixed what the regex pass misses: it skips code blocks and code files, and a reader reads a code comment as prose. - Replace every em dash with the house spaced en dash (76 occurrences, all inside code comments and example strings). - Replace `e.g.` with `such as` or `for example` (205 occurrences, again all in code comments), reading each rewrite rather than substituting blind. - Drop `please` from the example error strings so all of them read "Prompt injection detected – rephrase your message", matching the newer snippets instead of contradicting them. Rewrite the free-email message in the same voice. - Replace `via`, which is on the word list's don't-use list, and rename the three headings that carried it: the Genkit, Google ADK, and TanStack AI "Deny ... via" headings now read as tasks and match the merged pages. Nothing links to the old anchors. - Add the missing serial comma in the AI data loss prevention intro. The remaining checker output is triaged, not ignored. It is either a false positive on MDX import identifiers, frontmatter, badge URLs, and verbatim RFC and video titles, or the numbered quick start headings, which are a deliberate site-wide pattern that the `ajToc` anchors depend on. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Audited the site against the checks in github.com/zubair-trabzada/geo-seo-claude. Most of what it looks for is already here: llms.txt and llms-full.txt, a `Content-Signal` directive and a sitemap reference in robots.txt, canonical URLs, `dateModified` from git history, an `@id`-linked JSON-LD graph shared with arcjet.com, `BreadcrumbList`, `FAQPage`, Open Graph and Twitter cards, alt text on every image, and the security headers. These are the gaps that were real. Descriptions. Fourteen pages had none at all, including the docs home and all twelve JavaScript SDK reference pages, so they shipped with no meta description, no `og:description`, and no `description` on their JSON-LD node. Each now says what the page covers. Ten more were too short to stand on their own in a search result or an AI answer, so they are expanded, and the framework integrations description is re-ordered to put the purpose ahead of the framework list that was truncating it. Machine-readable copies. `/llms.txt`, `/llms-full.txt`, and the 23 `text/markdown` page copies were only reachable by guessing the URL. Every page now advertises them with `rel="alternate"`, and a `Link` response header points at both llms files so a HEAD request finds them. Product description. The `SoftwareApplication` node carried a name, a category, and a description. It now also carries `featureList`, `softwareHelp`, `operatingSystem`, and `sameAs`, so a page an assistant lands on can enumerate what Arcjet does and where the source lives. The audit template also asks for `offers` and `aggregateRating`. Both are left out on purpose, and a test asserts they stay out: this is technical documentation, and neither can be filled in without inventing a price or a rating. Also fixes the `e.g.` in the shared FAQ copy, which is documentation prose even though it lives in `src/lib`. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Runs `npm run pw:run -- --update-snapshots=changed` for the content changes on this branch, and deletes the eight screenshots for the four guard pages that the merge retired. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Contributor
There was a problem hiding this comment.
Arcjet Review — 🔴 High Risk
Decision: Cannot Assess
Rationale: Review failed due to an internal error: unknown error. Escalating to human reviewers.
Review: 36a4debe | Powered by Arcjet Review
The merged integration pages showed a JavaScript / Python switcher that did nothing. Two separate faults, one of them long-standing. Starlight `<Tabs>` cannot be used inside a snippet. `SlotByFramework` renders slots with `Astro.slots.render()`, which returns an HTML string. Astro only hoists a component's `<script>` for components it renders into the page, so the `<starlight-tabs>` markup shipped without the script that defines the custom element: 10 tab groups on /sdk/claude-agent-sdk/get-started/ and no `customElements.define` for any of them. The 102 snippet wrappers now use `SelectableContent`, the component this repo already uses for switching snippet content, which is a React island and so hydrates wherever it lands. It persists to `starlight-synced-tabs__language`, the same key Starlight's tabs use, so the snippet switcher and the page-level tabs on the integration reference pages stay in step. `slotIdx` has to sit on a rendered element. On a `<Fragment>` it is dropped, and the slot order then falls back to prop order, which is not stable – the install block on get-started came up with Python first while every other block on the page came up with JavaScript first. The wrappers now put it on a `<div>`, matching the package-manager blocks. `SelectableContent` itself never swapped content, on any page. Astro passes slots to a React island as `memo(StaticHtml, () => true)`, which never re-renders on an update because Astro assumes island slot content is static. Selecting a different slot is an update, so React kept the previous slot's HTML. Keying the rendered slot forces a remount, which the memo cannot skip. This also repairs every switcher that was already in the docs: on /sdk/next/get-started/ picking pnpm left `npm i @arcjet/next`, and on /sdk/node/filters/quick-start/ picking TS left `index.js`. Both now change. Adds tests/selectable-content.test.ts, which drives the DOM rather than reading the select, because that is the only way any of this is visible. It covers the package manager and JS/TS switchers that already shipped as well as the merged language pages. Two tests here asserted that both languages were in the DOM at once, which was true only while the inert tabs rendered every panel. They now assert the switcher is offered instead. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The merge commit redirected the `/guards/:adapter-py` pages and the `/sdk/:adapter-py/` routes, but two cases were still broken. `?f=` only covered the two hub pages. `langchain-js` was selectable on nine building blocks as well – the rate limiting, sensitive information, content moderation, prompt injection, and AI protection guides – so a shared URL such as `/sensitive-info/quick-start?f=langchain-js` landed on the page with no framework selected. The doc paths each retired key accepted are now recorded in `MERGED_GUARD_SDK_LEGACY_DOC_PATHS` and drive the redirects. That set is frozen rather than derived, because it is a record of URLs that were published and cannot change now the keys are gone. This takes the retired `?f=` redirects from 4 to 18. The bare `/sdk/:adapter-py` route pointed at a 404. Guard adapters have no `/sdk/:sdk/` index, so the Vercel rule sending `/sdk/langchain-js` to `/sdk/langchain` resolved to nothing. It now matches the Astro rule and lands on the adapter's get started guide. Verified by resolving all 28 retired-key redirect destinations against the build, and by driving the Astro rules through the preview server. Tests cover both: one asserts the bare route lands on a page that exists, and one asserts every page that accepted a retired `?f=` value still redirects. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…hots CI failed on reference-nextjs-light and reference-remix-dark with the same 5139-pixel diff on both attempts, in the same 212x24 region of both images. That region is the table of contents: CI rendered "Overview" highlighted as the current section and the committed snapshots did not. Locally the highlight is present and stable from first paint, so the committed images were the wrong ones, not CI. They got that way through the regeneration itself. In an earlier run these two tests failed on the 5s `toHaveScreenshot` budget while the runner was loaded, and `--update-snapshots=changed` writes whatever the attempt captured. A capture that timed out mid-settle was written to disk as the new expectation, and every later run compared against it and agreed, because the same wrong image kept being rewritten. Regenerated both serially. They are now pixel-identical to what CI rendered – 0 differing pixels against the actual images from the failed run – and all 178 screenshots pass with `--retries=0`, which is the check that would have caught this. Raises the screenshot budget to 20s so a slow capture waits instead of timing out. It goes on the assertion rather than in the config, because `expect.toHaveScreenshot` takes image options only and widening `expect.timeout` would loosen every assertion in the suite. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
cursor Bot
pushed a commit
that referenced
this pull request
Sep 3, 2026
Keep linux llms snapshots in sync with the new Google ADK Python and Cloudflare Think entries after the #919 rebase. Co-authored-by: David Mytton <davidmytton@users.noreply.github.com>
cursor Bot
pushed a commit
that referenced
this pull request
Sep 3, 2026
Update get-started, Guard index, quick start, and framework integrations snapshots for the new adapters after the #919 rebase. Co-authored-by: David Mytton <davidmytton@users.noreply.github.com>
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.
Five changes, one commit each.
Merge the language-split integrations. Claude Agent SDK, Claude Managed Agents, LangChain, OpenAI Agents, and Strands Agents each had two pages and two
/sdk/routes, one per language. Each is now one page withlanguagetabs, across get started,/guards/quick-start, and the integration pages.MERGED_GUARD_SDK_KEYSandMERGED_GUARD_SDK_LEGACY_DOC_PATHSgenerate redirects so every published-py,-js, and?f=URL still resolves – including?f=langchain-json the nine building blocks where it was selectable, and the bare/sdk/:adapter-pyroute. All 28 destinations are checked against the build. Also writes the OpenAI Agents Python get started snippets, which didn't exist.Cover every adapter in the building blocks. CrewAI, Google ADK, Strands Agents, TanStack AI, and Claude Managed Agents were missing from the rate limiting, sensitive information, content moderation, prompt injection, and AI protection guides. Each now has an example using that adapter's real deny point. The request-based reference pages stay HTTP-only – those rules have no Guard equivalent.
Refresh llms.txt and llms-full.txt. Drop the retired pages, add the SDK-scoped guard quick starts, and add AI protection, the utility packages, the integrations, and the blueprints, which were absent. llms-full gains an adapter table with the import path and deny point per framework. Every
docs.arcjet.comURL checked against a built page.Apply the writing style skill. 76 em dashes, 205 uses of
e.g.,pleasein example error strings, andvia. Most sat in code comments, which the checker skips. Remaining checker output is triaged in the commit message.Close the AI-search gaps. 14 pages had no
description– the docs home and all 12 JavaScript SDK reference pages./llms.txt,/llms-full.txt, and the 23.mdpage copies were reachable only by guessing, so every page now advertises them withrel="alternate"plus aLinkheader.SoftwareApplicationgainsfeatureList,softwareHelp, andsameAs.Fix the language switcher. The merged pages showed a switcher that did nothing, from two faults. Starlight
<Tabs>can't be used in a snippet:SlotByFrameworkrenders slots viaAstro.slots.render(), so the markup shipped without the script defining<starlight-tabs>. The wrappers now useSelectableContent, this repo's own snippet switcher, which is a React island and persists to the samestarlight-synced-tabs__languagekey the page-level tabs use. Separately,SelectableContentnever swapped content on any page: Astro passes island slots asmemo(StaticHtml, () => true), so React kept the previous slot's HTML. Keying the rendered slot fixes it, which also repairs every switcher already in the docs – picking pnpm on/sdk/next/get-started/leftnpm i @arcjet/next, and picking TS on/sdk/node/filters/quick-start/leftindex.js.tests/selectable-content.test.tsdrives the DOM to cover both.Left for you
vercel.jsondoesn't set. That's a production change, not a docs change.offersandaggregateRatingare deliberately absent, with a test to keep them out: neither can be filled in without inventing a price or a rating.descriptionfrontmatter render literally in search snippets. ~15 pages already did this, so it's a house convention to flip or keep.Verification
astro check --minimumSeverity warning0 errors, 0 warnings.astro buildclean, all internal links valid. 518 Playwright tests pass, no failures and no flakes. Snapshots regenerated, and the 8 screenshots for the retired pages deleted.🤖 Generated with Claude Code