From 5961159e6c50c4eb41c69f8742857c8699bc641f Mon Sep 17 00:00:00 2001 From: Bryant Date: Mon, 13 Jul 2026 09:28:13 +0800 Subject: [PATCH 01/22] =?UTF-8?q?=F0=9F=94=A7=20(pre-commit):=20Exclude=20?= =?UTF-8?q?vendored=20quickstart=5Fsnippets=20from=20hooks?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The vendored quick-start snippets are verbatim, partial governance slices; some end on a bodyless `try:`/`with … as ctx:` that is a valid doc excerpt but not a standalone module. Formatting or parsing them would rewrite the vendored copy and break the drift check, so exclude the dir from all hooks. Refs AAASM-4513 Co-Authored-By: Claude Opus 4.8 (1M context) Claude-Session: https://claude.ai/code/session_01Rk7iwwAuGv6HbwK8hY8Fbr --- .pre-commit-config.yaml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index b3b8152b..531239b1 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,4 +1,9 @@ -exclude: '.github/|docs/' +# quickstart_snippets/ holds verbatim, vendored copies of the examples repo's +# quick-start regions (AAASM-4513); several are partial governance slices (e.g. +# a trailing `try:`/`with … as ctx:` with no body) that are valid as doc +# excerpts but not as standalone modules. Formatting/parsing them would rewrite +# the vendored copy and break the drift check, so exclude the dir from all hooks. +exclude: '.github/|docs/|quickstart_snippets/' repos: - repo: https://github.com/pre-commit/pre-commit-hooks From 999d8e6145c0deb0dd777bd1b80dd61ea6bfe4d2 Mon Sep 17 00:00:00 2001 From: Bryant Date: Mon, 13 Jul 2026 09:28:39 +0800 Subject: [PATCH 02/22] =?UTF-8?q?=F0=9F=93=9D=20(quickstart):=20Vendor=20s?= =?UTF-8?q?nippets=20manifest=20and=20README?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The python slice of the manifest produced by the examples repo's scripts/extract_snippets.py (AAASM-4512, PR examples#267) plus a README documenting the vendoring contract. The manifest's `frameworks` array (listed order) is the single source of truth for which tabs the generator emits; the per-framework .py slices land in the following commits. Refs AAASM-4513 Co-Authored-By: Claude Opus 4.8 (1M context) Claude-Session: https://claude.ai/code/session_01Rk7iwwAuGv6HbwK8hY8Fbr --- quickstart_snippets/README.md | 33 +++++++++ quickstart_snippets/manifest.json | 119 ++++++++++++++++++++++++++++++ 2 files changed, 152 insertions(+) create mode 100644 quickstart_snippets/README.md create mode 100644 quickstart_snippets/manifest.json diff --git a/quickstart_snippets/README.md b/quickstart_snippets/README.md new file mode 100644 index 00000000..fd1898a0 --- /dev/null +++ b/quickstart_snippets/README.md @@ -0,0 +1,33 @@ +# Vendored quick-start snippets + +These files are a **committed, drift-checked copy** of the per-framework +"govern your first agent" snippets produced by the `ai-agent-assembly/examples` +repo (`scripts/extract_snippets.py`, AAASM-4512 / PR examples#267). Each `.py` +file is the `region: quickstart` slice — `init_assembly()` plus that framework's +adapter wiring — cut verbatim from the matching runnable example's entrypoint. + +## Why vendored, not fetched at build time + +`examples` is a **separate repository**; the MkDocs build here must not reach +across repos at render time. Vendoring keeps the docs build hermetic while the +snippets stay honest: `scripts/generate_quickstart_tabs.py` turns them into the +`pymdownx.tabbed` block in `docs/quick-start.md` §3, and a CI drift check +(`.github/workflows/quickstart-tabs-check.yml`) fails if the committed doc no +longer matches these inputs. + +## Refreshing after the examples change + +When the upstream examples' quick-start regions change, re-copy the Python +snippet files and the `python` slice of `examples/snippets/manifest.json` into +this directory (`manifest.json` here keeps only the `python` entries), then run: + +```bash +python scripts/generate_quickstart_tabs.py +``` + +and commit the regenerated `docs/quick-start.md` alongside the updated snippets. + +`manifest.json` is the data-driven tab index: `frameworks[]` ordered as the tab +list should render, each with `framework_id` (also the `.py` +filename), `label` (tab text), `status`, `lang`, and `source_example` +(provenance in the examples repo). diff --git a/quickstart_snippets/manifest.json b/quickstart_snippets/manifest.json new file mode 100644 index 00000000..db69a595 --- /dev/null +++ b/quickstart_snippets/manifest.json @@ -0,0 +1,119 @@ +{ + "schema_version": 1, + "source": "ai-agent-assembly/examples: snippets/manifest.json (AAASM-4512, PR examples#267)", + "sdk": "python", + "frameworks": [ + { + "framework_id": "agno-tool-policy", + "label": "Agno", + "status": "validated", + "lang": "python", + "source_example": "python/agno-tool-policy/src/main.py" + }, + { + "framework_id": "autogen-tool-policy", + "label": "AutoGen", + "status": "validated", + "lang": "python", + "source_example": "python/autogen-tool-policy/src/main.py" + }, + { + "framework_id": "crewai-research-crew", + "label": "CrewAI", + "status": "validated", + "lang": "python", + "source_example": "python/crewai-research-crew/src/main.py" + }, + { + "framework_id": "custom-tool-policy", + "label": "Custom (no framework)", + "status": "validated", + "lang": "python", + "source_example": "python/custom-tool-policy/src/main.py" + }, + { + "framework_id": "google-adk", + "label": "Google ADK", + "status": "validated", + "lang": "python", + "source_example": "python/google-adk/src/main.py" + }, + { + "framework_id": "haystack-tool-policy", + "label": "Haystack", + "status": "validated", + "lang": "python", + "source_example": "python/haystack-tool-policy/src/main.py" + }, + { + "framework_id": "langchain-basic-agent", + "label": "LangChain", + "status": "validated", + "lang": "python", + "source_example": "python/langchain-basic-agent/src/main.py" + }, + { + "framework_id": "langchain-research-agent", + "label": "LangChain (Research Agent)", + "status": "validated", + "lang": "python", + "source_example": "python/langchain-research-agent/src/main.py" + }, + { + "framework_id": "langgraph", + "label": "LangGraph", + "status": "validated", + "lang": "python", + "source_example": "python/langgraph/src/main.py" + }, + { + "framework_id": "llamaindex-tool-policy", + "label": "LlamaIndex", + "status": "validated", + "lang": "python", + "source_example": "python/llamaindex-tool-policy/src/main.py" + }, + { + "framework_id": "microsoft-agent-framework-tool-policy", + "label": "Microsoft Agent Framework", + "status": "validated", + "lang": "python", + "source_example": "python/microsoft-agent-framework-tool-policy/src/main.py" + }, + { + "framework_id": "openai-agents-sdk", + "label": "OpenAI Agents SDK", + "status": "validated", + "lang": "python", + "source_example": "python/openai-agents-sdk/src/main.py" + }, + { + "framework_id": "pydantic-ai", + "label": "Pydantic AI", + "status": "validated", + "lang": "python", + "source_example": "python/pydantic-ai/src/main.py" + }, + { + "framework_id": "semantic-kernel-tool-policy", + "label": "Semantic Kernel", + "status": "validated", + "lang": "python", + "source_example": "python/semantic-kernel-tool-policy/src/main.py" + }, + { + "framework_id": "smolagents-tool-policy", + "label": "smolagents", + "status": "validated", + "lang": "python", + "source_example": "python/smolagents-tool-policy/src/main.py" + }, + { + "framework_id": "strands-agents-tool-policy", + "label": "Strands Agents", + "status": "validated", + "lang": "python", + "source_example": "python/strands-agents-tool-policy/src/main.py" + } + ] +} From 95be10f5f8ddf07dc81f5c32a6819a37decccd30 Mon Sep 17 00:00:00 2001 From: Bryant Date: Mon, 13 Jul 2026 09:28:39 +0800 Subject: [PATCH 03/22] =?UTF-8?q?=F0=9F=93=9D=20(quickstart):=20Add=20Agno?= =?UTF-8?q?=20govern=20snippet?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Vendored verbatim `quickstart` region slice from the examples repo's python/agno-tool-policy entrypoint (AAASM-4512) — init_assembly() plus that framework's governance wiring. Partial by design (cut at the region boundary), which is why the dir is hook-excluded and drift-gated rather than linted. Refs AAASM-4513 Co-Authored-By: Claude Opus 4.8 (1M context) Claude-Session: https://claude.ai/code/session_01Rk7iwwAuGv6HbwK8hY8Fbr --- quickstart_snippets/agno-tool-policy.py | 29 +++++++++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 quickstart_snippets/agno-tool-policy.py diff --git a/quickstart_snippets/agno-tool-policy.py b/quickstart_snippets/agno-tool-policy.py new file mode 100644 index 00000000..454b0894 --- /dev/null +++ b/quickstart_snippets/agno-tool-policy.py @@ -0,0 +1,29 @@ +with init_assembly( + gateway_url=gateway_url, + api_key=api_key, + agent_id="agno-demo-agent", + mode="sdk-only", +) as ctx: + print(f" Agent: {ctx.client.agent_id}") + print(f" Gateway: {ctx.client.gateway_url}") + print(f" Mode: {ctx.network_mode} (offline demo)") + print() + + policy = LocalPolicyEngine() + + print("Policy rules (local simulation of gateway policy):") + print(" DENY — execute_sql, run_shell_command (arbitrary execution)") + print(" ALLOW — everything else") + print() + + # In production init_assembly() auto-detects Agno and wires the live + # runtime as the interceptor automatically. In this offline sdk-only demo + # there is no live runtime, so init_assembly() installs a no-op hook; we + # revert it and re-apply the hook wired to our local policy so the demo + # shows real allow/deny decisions without a gateway. (The patch is + # idempotent, so we must revert the no-op hook before installing ours.) + AgnoPatch(policy).revert() + patch = AgnoPatch(policy) + assert patch.apply(), ( + "Agno governance hook did not install — is agno importable?" + ) From 1afffdea30dba4def84ff7739bba20dd00b7279c Mon Sep 17 00:00:00 2001 From: Bryant Date: Mon, 13 Jul 2026 09:28:39 +0800 Subject: [PATCH 04/22] =?UTF-8?q?=F0=9F=93=9D=20(quickstart):=20Add=20Auto?= =?UTF-8?q?Gen=20govern=20snippet?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Vendored verbatim `quickstart` region slice from the examples repo's python/autogen-tool-policy entrypoint (AAASM-4512) — init_assembly() plus that framework's governance wiring. Partial by design (cut at the region boundary), which is why the dir is hook-excluded and drift-gated rather than linted. Refs AAASM-4513 Co-Authored-By: Claude Opus 4.8 (1M context) Claude-Session: https://claude.ai/code/session_01Rk7iwwAuGv6HbwK8hY8Fbr --- quickstart_snippets/autogen-tool-policy.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 quickstart_snippets/autogen-tool-policy.py diff --git a/quickstart_snippets/autogen-tool-policy.py b/quickstart_snippets/autogen-tool-policy.py new file mode 100644 index 00000000..82b71661 --- /dev/null +++ b/quickstart_snippets/autogen-tool-policy.py @@ -0,0 +1,12 @@ +with init_assembly( + gateway_url=gateway_url, + api_key=api_key, + agent_id="autogen-demo-agent", + mode="sdk-only", +) as ctx: + print(f" Agent: {ctx.client.agent_id}") + print(f" Gateway: {ctx.client.gateway_url}") + print(f" Mode: {ctx.network_mode} (offline demo)") + print() + + policy = LocalPolicyEngine() From 653c04ea4d1bfaab0f98f05c4aaa607ca4a7a7bf Mon Sep 17 00:00:00 2001 From: Bryant Date: Mon, 13 Jul 2026 09:28:40 +0800 Subject: [PATCH 05/22] =?UTF-8?q?=F0=9F=93=9D=20(quickstart):=20Add=20Crew?= =?UTF-8?q?AI=20govern=20snippet?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Vendored verbatim `quickstart` region slice from the examples repo's python/crewai-research-crew entrypoint (AAASM-4512) — init_assembly() plus that framework's governance wiring. Partial by design (cut at the region boundary), which is why the dir is hook-excluded and drift-gated rather than linted. Refs AAASM-4513 Co-Authored-By: Claude Opus 4.8 (1M context) Claude-Session: https://claude.ai/code/session_01Rk7iwwAuGv6HbwK8hY8Fbr --- quickstart_snippets/crewai-research-crew.py | 24 +++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 quickstart_snippets/crewai-research-crew.py diff --git a/quickstart_snippets/crewai-research-crew.py b/quickstart_snippets/crewai-research-crew.py new file mode 100644 index 00000000..ae693944 --- /dev/null +++ b/quickstart_snippets/crewai-research-crew.py @@ -0,0 +1,24 @@ +with init_assembly( + gateway_url=gateway_url, + api_key=api_key, + agent_id="crewai-research-crew", + mode="sdk-only", +) as ctx: + print(f" Agent: {ctx.client.agent_id}") + print(f" Gateway: {ctx.client.gateway_url}") + print(f" Mode: {ctx.network_mode} ({mode_label})") + print() + + print("Crew members:") + for member in CREW: + print(f" • {member.name:<11} — {member.role}") + print() + + print("Crew policy (local simulation of gateway policy):") + print(" APPROVAL — any agent attempting a file write must be approved") + print(f" BUDGET — ${DAILY_BUDGET_USD:.2f} / day, shared across all agents") + print(" TRACK — every call recorded with its delegation call stack") + print() + + policy = CrewPolicyEngine(approver=MockApprover(auto_approve=False)) + handler = AssemblyCallbackHandler(interceptor=policy) From 92cdb95b47131f1f2617f988928196eb4104b154 Mon Sep 17 00:00:00 2001 From: Bryant Date: Mon, 13 Jul 2026 09:28:40 +0800 Subject: [PATCH 06/22] =?UTF-8?q?=F0=9F=93=9D=20(quickstart):=20Add=20Cust?= =?UTF-8?q?om=20(no=20framework)=20govern=20snippet?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Vendored verbatim `quickstart` region slice from the examples repo's python/custom-tool-policy entrypoint (AAASM-4512) — init_assembly() plus that framework's governance wiring. Partial by design (cut at the region boundary), which is why the dir is hook-excluded and drift-gated rather than linted. Refs AAASM-4513 Co-Authored-By: Claude Opus 4.8 (1M context) Claude-Session: https://claude.ai/code/session_01Rk7iwwAuGv6HbwK8hY8Fbr --- quickstart_snippets/custom-tool-policy.py | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 quickstart_snippets/custom-tool-policy.py diff --git a/quickstart_snippets/custom-tool-policy.py b/quickstart_snippets/custom-tool-policy.py new file mode 100644 index 00000000..3c83bf48 --- /dev/null +++ b/quickstart_snippets/custom-tool-policy.py @@ -0,0 +1,20 @@ +with init_assembly( + gateway_url=gateway_url, + api_key=api_key, + agent_id="custom-tool-demo-agent", + mode="sdk-only", +) as ctx: + print(f" Agent: {ctx.client.agent_id}") + print(f" Gateway: {ctx.client.gateway_url}") + print(f" Mode: {ctx.network_mode} (offline demo)") + print() + + policy = LocalPolicyEngine() + + raw_fns = { + "compute_sum": compute_sum, + "fetch_stock_price": fetch_stock_price, + "send_http_request": send_http_request, + "write_to_disk": write_to_disk, + } + tools = {name: governed(name, fn, policy) for name, fn in raw_fns.items()} From 10ee54f46c82a04919300b9e2bbed628f9489bda Mon Sep 17 00:00:00 2001 From: Bryant Date: Mon, 13 Jul 2026 09:28:40 +0800 Subject: [PATCH 07/22] =?UTF-8?q?=F0=9F=93=9D=20(quickstart):=20Add=20Goog?= =?UTF-8?q?le=20ADK=20govern=20snippet?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Vendored verbatim `quickstart` region slice from the examples repo's python/google-adk entrypoint (AAASM-4512) — init_assembly() plus that framework's governance wiring. Partial by design (cut at the region boundary), which is why the dir is hook-excluded and drift-gated rather than linted. Refs AAASM-4513 Co-Authored-By: Claude Opus 4.8 (1M context) Claude-Session: https://claude.ai/code/session_01Rk7iwwAuGv6HbwK8hY8Fbr --- quickstart_snippets/google-adk.py | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 quickstart_snippets/google-adk.py diff --git a/quickstart_snippets/google-adk.py b/quickstart_snippets/google-adk.py new file mode 100644 index 00000000..1efd391b --- /dev/null +++ b/quickstart_snippets/google-adk.py @@ -0,0 +1,11 @@ +# Govern the concrete demo tool class BEFORE init_assembly so the offline +# LocalPolicyEngine stays wired as the interceptor (the patch is idempotent). +govern_tool_class(DemoTool, LocalPolicyEngine()) + +try: + with init_assembly( + gateway_url=gateway_url, + api_key=api_key, + agent_id="google-adk-demo-agent", + mode="sdk-only", + ) as ctx: From 935620a5ef6ade83876c4dde9ee414e4d4a011f4 Mon Sep 17 00:00:00 2001 From: Bryant Date: Mon, 13 Jul 2026 09:28:41 +0800 Subject: [PATCH 08/22] =?UTF-8?q?=F0=9F=93=9D=20(quickstart):=20Add=20Hays?= =?UTF-8?q?tack=20govern=20snippet?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Vendored verbatim `quickstart` region slice from the examples repo's python/haystack-tool-policy entrypoint (AAASM-4512) — init_assembly() plus that framework's governance wiring. Partial by design (cut at the region boundary), which is why the dir is hook-excluded and drift-gated rather than linted. Refs AAASM-4513 Co-Authored-By: Claude Opus 4.8 (1M context) Claude-Session: https://claude.ai/code/session_01Rk7iwwAuGv6HbwK8hY8Fbr --- quickstart_snippets/haystack-tool-policy.py | 27 +++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 quickstart_snippets/haystack-tool-policy.py diff --git a/quickstart_snippets/haystack-tool-policy.py b/quickstart_snippets/haystack-tool-policy.py new file mode 100644 index 00000000..f0424de3 --- /dev/null +++ b/quickstart_snippets/haystack-tool-policy.py @@ -0,0 +1,27 @@ +with init_assembly( + gateway_url=gateway_url, + api_key=api_key, + agent_id="haystack-demo-agent", + mode="sdk-only", +) as ctx: + print(f" Agent: {ctx.client.agent_id}") + print(f" Gateway: {ctx.client.gateway_url}") + print(f" Mode: {ctx.network_mode} (offline demo)") + print() + + print("Policy rules (local simulation of gateway policy):") + print(" DENY — execute_sql, run_shell_command (arbitrary execution)") + print(" ALLOW — everything else") + print() + + # init_assembly() has already auto-detected Haystack and patched + # Tool.invoke — but in offline sdk-only mode it wires a no-op interceptor + # (there is no live gateway/runtime to answer policy). For this *offline* + # demo we revert that and re-install the same native adapter against a + # LocalPolicyEngine so a real allow/deny is visible without a gateway. In + # production you would instead point init_assembly() at a gateway and let + # its auto-detected adapter enforce real policy — no manual re-install. + print("Installing the native Haystack adapter against the demo policy...") + HaystackPatch(LocalPolicyEngine()).revert() # drop the auto-applied no-op patch + patch = HaystackPatch(LocalPolicyEngine()) + installed = patch.apply() From 6ef1a8d4e6cecb1976af9c8f6bef387625c48d89 Mon Sep 17 00:00:00 2001 From: Bryant Date: Mon, 13 Jul 2026 09:28:41 +0800 Subject: [PATCH 09/22] =?UTF-8?q?=F0=9F=93=9D=20(quickstart):=20Add=20Lang?= =?UTF-8?q?Chain=20govern=20snippet?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Vendored verbatim `quickstart` region slice from the examples repo's python/langchain-basic-agent entrypoint (AAASM-4512) — init_assembly() plus that framework's governance wiring. Partial by design (cut at the region boundary), which is why the dir is hook-excluded and drift-gated rather than linted. Refs AAASM-4513 Co-Authored-By: Claude Opus 4.8 (1M context) Claude-Session: https://claude.ai/code/session_01Rk7iwwAuGv6HbwK8hY8Fbr --- quickstart_snippets/langchain-basic-agent.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 quickstart_snippets/langchain-basic-agent.py diff --git a/quickstart_snippets/langchain-basic-agent.py b/quickstart_snippets/langchain-basic-agent.py new file mode 100644 index 00000000..74b9afcf --- /dev/null +++ b/quickstart_snippets/langchain-basic-agent.py @@ -0,0 +1,13 @@ +with init_assembly( + gateway_url=gateway_url, + api_key=api_key, + agent_id="langchain-demo-agent", + mode="sdk-only", +) as ctx: + print(f" Agent: {ctx.client.agent_id}") + print(f" Gateway: {ctx.client.gateway_url}") + print(f" Mode: {ctx.network_mode} (offline demo)") + print() + + policy = LocalPolicyEngine() + handler = AssemblyCallbackHandler(interceptor=policy) From 77f9e44ca7326a182a4a5851c76efc6e548a8788 Mon Sep 17 00:00:00 2001 From: Bryant Date: Mon, 13 Jul 2026 09:28:41 +0800 Subject: [PATCH 10/22] =?UTF-8?q?=F0=9F=93=9D=20(quickstart):=20Add=20Lang?= =?UTF-8?q?Chain=20(Research=20Agent)=20govern=20snippet?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Vendored verbatim `quickstart` region slice from the examples repo's python/langchain-research-agent entrypoint (AAASM-4512) — init_assembly() plus that framework's governance wiring. Partial by design (cut at the region boundary), which is why the dir is hook-excluded and drift-gated rather than linted. Refs AAASM-4513 Co-Authored-By: Claude Opus 4.8 (1M context) Claude-Session: https://claude.ai/code/session_01Rk7iwwAuGv6HbwK8hY8Fbr --- quickstart_snippets/langchain-research-agent.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 quickstart_snippets/langchain-research-agent.py diff --git a/quickstart_snippets/langchain-research-agent.py b/quickstart_snippets/langchain-research-agent.py new file mode 100644 index 00000000..ff179593 --- /dev/null +++ b/quickstart_snippets/langchain-research-agent.py @@ -0,0 +1,13 @@ +with init_assembly( + gateway_url=gateway_url, + api_key=api_key, + agent_id="langchain-research-agent", + mode="sdk-only", +) as ctx: + print(f" Agent: {ctx.client.agent_id}") + print(f" Gateway: {ctx.client.gateway_url}") + print(f" Mode: {ctx.network_mode} ({mode_label})") + print() + + policy = BalancedPolicyEngine(daily_budget_usd=DAILY_BUDGET_USD) + handler = AssemblyCallbackHandler(interceptor=policy) From 336bbdba623ecbff658a15bbf831bcb882024ec9 Mon Sep 17 00:00:00 2001 From: Bryant Date: Mon, 13 Jul 2026 09:28:42 +0800 Subject: [PATCH 11/22] =?UTF-8?q?=F0=9F=93=9D=20(quickstart):=20Add=20Lang?= =?UTF-8?q?Graph=20govern=20snippet?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Vendored verbatim `quickstart` region slice from the examples repo's python/langgraph entrypoint (AAASM-4512) — init_assembly() plus that framework's governance wiring. Partial by design (cut at the region boundary), which is why the dir is hook-excluded and drift-gated rather than linted. Refs AAASM-4513 Co-Authored-By: Claude Opus 4.8 (1M context) Claude-Session: https://claude.ai/code/session_01Rk7iwwAuGv6HbwK8hY8Fbr --- quickstart_snippets/langgraph.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 quickstart_snippets/langgraph.py diff --git a/quickstart_snippets/langgraph.py b/quickstart_snippets/langgraph.py new file mode 100644 index 00000000..ccffca96 --- /dev/null +++ b/quickstart_snippets/langgraph.py @@ -0,0 +1,19 @@ +with init_assembly( + gateway_url=gateway_url, + api_key=api_key, + agent_id="langgraph-demo-agent", + mode="sdk-only", +) as ctx: + print(f" Agent: {ctx.client.agent_id}") + print(f" Gateway: {ctx.client.gateway_url}") + print(f" Mode: {ctx.network_mode} (offline demo)") + print() + + policy = LocalPolicyEngine() + handler = AssemblyCallbackHandler(interceptor=policy) + + # Install LangGraph node-level governance hooks. The adapter wraps the + # compiled graph's nodes so tool calls inside each node are governed. + adapter = LangGraphAdapter() + adapter.set_process_agent_id(ctx.client.agent_id) + adapter.register_hooks(handler) From 8cdbdcd9db60c27b6d2c17ee4e552135bee2b2af Mon Sep 17 00:00:00 2001 From: Bryant Date: Mon, 13 Jul 2026 09:28:42 +0800 Subject: [PATCH 12/22] =?UTF-8?q?=F0=9F=93=9D=20(quickstart):=20Add=20Llam?= =?UTF-8?q?aIndex=20govern=20snippet?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Vendored verbatim `quickstart` region slice from the examples repo's python/llamaindex-tool-policy entrypoint (AAASM-4512) — init_assembly() plus that framework's governance wiring. Partial by design (cut at the region boundary), which is why the dir is hook-excluded and drift-gated rather than linted. Refs AAASM-4513 Co-Authored-By: Claude Opus 4.8 (1M context) Claude-Session: https://claude.ai/code/session_01Rk7iwwAuGv6HbwK8hY8Fbr --- quickstart_snippets/llamaindex-tool-policy.py | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 quickstart_snippets/llamaindex-tool-policy.py diff --git a/quickstart_snippets/llamaindex-tool-policy.py b/quickstart_snippets/llamaindex-tool-policy.py new file mode 100644 index 00000000..672538dc --- /dev/null +++ b/quickstart_snippets/llamaindex-tool-policy.py @@ -0,0 +1,29 @@ +with init_assembly( + gateway_url=gateway_url, + api_key=api_key, + agent_id="llamaindex-demo-agent", + mode="sdk-only", +) as ctx: + print(f" Agent: {ctx.client.agent_id}") + print(f" Gateway: {ctx.client.gateway_url}") + print(f" Mode: {ctx.network_mode} (offline demo)") + print() + + print("Policy rules (local simulation of gateway policy):") + print(" DENY — execute_sql, run_shell_command (arbitrary execution)") + print(" ALLOW — everything else") + print() + + # Register the native LlamaIndex adapter against the local policy engine. + # This patches FunctionTool.call so every tool call below is governed + # automatically — no per-tool wrapper needed. + # + # init_assembly() in sdk-only mode already auto-detected LlamaIndex and + # patched FunctionTool.call against a no-op interceptor (there is no + # gateway offline). Revert that first so this example's LocalPolicyEngine + # is the live interceptor; in production init_assembly wires the adapter + # to the gateway and this manual step is unnecessary. + print("Registering the native LlamaIndex governance adapter...") + LlamaIndexPatch(callback_handler=None).revert() + adapter = LlamaIndexAdapter() + adapter.register_hooks(LocalPolicyEngine()) From 2311ecd24926161c34823b1a85ad635769b8b961 Mon Sep 17 00:00:00 2001 From: Bryant Date: Mon, 13 Jul 2026 09:28:42 +0800 Subject: [PATCH 13/22] =?UTF-8?q?=F0=9F=93=9D=20(quickstart):=20Add=20Micr?= =?UTF-8?q?osoft=20Agent=20Framework=20govern=20snippet?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Vendored verbatim `quickstart` region slice from the examples repo's python/microsoft-agent-framework-tool-policy entrypoint (AAASM-4512) — init_assembly() plus that framework's governance wiring. Partial by design (cut at the region boundary), which is why the dir is hook-excluded and drift-gated rather than linted. Refs AAASM-4513 Co-Authored-By: Claude Opus 4.8 (1M context) Claude-Session: https://claude.ai/code/session_01Rk7iwwAuGv6HbwK8hY8Fbr --- .../microsoft-agent-framework-tool-policy.py | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 quickstart_snippets/microsoft-agent-framework-tool-policy.py diff --git a/quickstart_snippets/microsoft-agent-framework-tool-policy.py b/quickstart_snippets/microsoft-agent-framework-tool-policy.py new file mode 100644 index 00000000..6874538b --- /dev/null +++ b/quickstart_snippets/microsoft-agent-framework-tool-policy.py @@ -0,0 +1,20 @@ +policy = LocalPolicyEngine() + +# Live path: install the governance hooks BEFORE init_assembly. The adapter +# patches `agent_framework.FunctionTool.invoke`; because the patch is +# idempotent, registering first makes init_assembly's auto-detection a no-op +# and keeps the offline `LocalPolicyEngine` wired as the interceptor (rather +# than the no-op interceptor auto-detection would install). +adapter: MicrosoftAgentFrameworkAdapter | None = None +if not mock: + adapter = MicrosoftAgentFrameworkAdapter() + adapter.set_process_agent_id("microsoft-agent-framework-demo-agent") + adapter.register_hooks(policy) + +try: + with init_assembly( + gateway_url=gateway_url, + api_key=api_key, + agent_id="microsoft-agent-framework-demo-agent", + mode="sdk-only", + ) as ctx: From ab8dc0474ae7d4722269ee89db2142f1fdf92ad5 Mon Sep 17 00:00:00 2001 From: Bryant Date: Mon, 13 Jul 2026 09:28:43 +0800 Subject: [PATCH 14/22] =?UTF-8?q?=F0=9F=93=9D=20(quickstart):=20Add=20Open?= =?UTF-8?q?AI=20Agents=20SDK=20govern=20snippet?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Vendored verbatim `quickstart` region slice from the examples repo's python/openai-agents-sdk entrypoint (AAASM-4512) — init_assembly() plus that framework's governance wiring. Partial by design (cut at the region boundary), which is why the dir is hook-excluded and drift-gated rather than linted. Refs AAASM-4513 Co-Authored-By: Claude Opus 4.8 (1M context) Claude-Session: https://claude.ai/code/session_01Rk7iwwAuGv6HbwK8hY8Fbr --- quickstart_snippets/openai-agents-sdk.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 quickstart_snippets/openai-agents-sdk.py diff --git a/quickstart_snippets/openai-agents-sdk.py b/quickstart_snippets/openai-agents-sdk.py new file mode 100644 index 00000000..076c3bf9 --- /dev/null +++ b/quickstart_snippets/openai-agents-sdk.py @@ -0,0 +1,13 @@ +with init_assembly( + gateway_url=gateway_url, + api_key=api_key, + agent_id="openai-agents-demo", + mode="sdk-only", +) as ctx: + print(f" Agent: {ctx.client.agent_id}") + print(f" Gateway: {ctx.client.gateway_url}") + print(f" Mode: {ctx.network_mode} (offline demo)") + print() + + policy = LocalPolicyEngine() + handler = AssemblyCallbackHandler(interceptor=policy) From 3ce16fac74c4f09cb9592388a0b100bdfb507765 Mon Sep 17 00:00:00 2001 From: Bryant Date: Mon, 13 Jul 2026 09:28:43 +0800 Subject: [PATCH 15/22] =?UTF-8?q?=F0=9F=93=9D=20(quickstart):=20Add=20Pyda?= =?UTF-8?q?ntic=20AI=20govern=20snippet?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Vendored verbatim `quickstart` region slice from the examples repo's python/pydantic-ai entrypoint (AAASM-4512) — init_assembly() plus that framework's governance wiring. Partial by design (cut at the region boundary), which is why the dir is hook-excluded and drift-gated rather than linted. Refs AAASM-4513 Co-Authored-By: Claude Opus 4.8 (1M context) Claude-Session: https://claude.ai/code/session_01Rk7iwwAuGv6HbwK8hY8Fbr --- quickstart_snippets/pydantic-ai.py | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 quickstart_snippets/pydantic-ai.py diff --git a/quickstart_snippets/pydantic-ai.py b/quickstart_snippets/pydantic-ai.py new file mode 100644 index 00000000..20043aa5 --- /dev/null +++ b/quickstart_snippets/pydantic-ai.py @@ -0,0 +1,11 @@ +adapter = PydanticAIAdapter() +adapter.set_process_agent_id("pydantic-ai-demo-agent") +adapter.register_hooks(LocalPolicyEngine()) + +try: + with init_assembly( + gateway_url=gateway_url, + api_key=api_key, + agent_id="pydantic-ai-demo-agent", + mode="sdk-only", + ) as ctx: From 3fab9f274dae312bd652eaa86d6e3f6b478c7b2c Mon Sep 17 00:00:00 2001 From: Bryant Date: Mon, 13 Jul 2026 09:28:43 +0800 Subject: [PATCH 16/22] =?UTF-8?q?=F0=9F=93=9D=20(quickstart):=20Add=20Sema?= =?UTF-8?q?ntic=20Kernel=20govern=20snippet?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Vendored verbatim `quickstart` region slice from the examples repo's python/semantic-kernel-tool-policy entrypoint (AAASM-4512) — init_assembly() plus that framework's governance wiring. Partial by design (cut at the region boundary), which is why the dir is hook-excluded and drift-gated rather than linted. Refs AAASM-4513 Co-Authored-By: Claude Opus 4.8 (1M context) Claude-Session: https://claude.ai/code/session_01Rk7iwwAuGv6HbwK8hY8Fbr --- quickstart_snippets/semantic-kernel-tool-policy.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 quickstart_snippets/semantic-kernel-tool-policy.py diff --git a/quickstart_snippets/semantic-kernel-tool-policy.py b/quickstart_snippets/semantic-kernel-tool-policy.py new file mode 100644 index 00000000..7d5feb30 --- /dev/null +++ b/quickstart_snippets/semantic-kernel-tool-policy.py @@ -0,0 +1,13 @@ +with init_assembly( + gateway_url=gateway_url, + api_key=api_key, + agent_id="semantic-kernel-demo-agent", + mode="sdk-only", +) as ctx: + print(f" Agent: {ctx.client.agent_id}") + print(f" Gateway: {ctx.client.gateway_url}") + print(f" Mode: {ctx.network_mode} (offline demo)") + print() + + policy = LocalPolicyEngine() + kernel = build_kernel() From 9fa9c2fcafad18092b5fc8cd141da67010134e53 Mon Sep 17 00:00:00 2001 From: Bryant Date: Mon, 13 Jul 2026 09:28:44 +0800 Subject: [PATCH 17/22] =?UTF-8?q?=F0=9F=93=9D=20(quickstart):=20Add=20smol?= =?UTF-8?q?agents=20govern=20snippet?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Vendored verbatim `quickstart` region slice from the examples repo's python/smolagents-tool-policy entrypoint (AAASM-4512) — init_assembly() plus that framework's governance wiring. Partial by design (cut at the region boundary), which is why the dir is hook-excluded and drift-gated rather than linted. Refs AAASM-4513 Co-Authored-By: Claude Opus 4.8 (1M context) Claude-Session: https://claude.ai/code/session_01Rk7iwwAuGv6HbwK8hY8Fbr --- quickstart_snippets/smolagents-tool-policy.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 quickstart_snippets/smolagents-tool-policy.py diff --git a/quickstart_snippets/smolagents-tool-policy.py b/quickstart_snippets/smolagents-tool-policy.py new file mode 100644 index 00000000..e83897ad --- /dev/null +++ b/quickstart_snippets/smolagents-tool-policy.py @@ -0,0 +1,12 @@ +policy = LocalPolicyEngine() +patch = SmolagentsPatch(policy) +patch.apply() + +print(f"Initializing Agent Assembly (gateway: {gateway_url}, sdk-only mode)...") + +with init_assembly( + gateway_url=gateway_url, + api_key=api_key, + agent_id="smolagents-demo-agent", + mode="sdk-only", +) as ctx: From 86d0856fe6917baaea70f9c03b218c329f64d980 Mon Sep 17 00:00:00 2001 From: Bryant Date: Mon, 13 Jul 2026 09:28:44 +0800 Subject: [PATCH 18/22] =?UTF-8?q?=F0=9F=93=9D=20(quickstart):=20Add=20Stra?= =?UTF-8?q?nds=20Agents=20govern=20snippet?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Vendored verbatim `quickstart` region slice from the examples repo's python/strands-agents-tool-policy entrypoint (AAASM-4512) — init_assembly() plus that framework's governance wiring. Partial by design (cut at the region boundary), which is why the dir is hook-excluded and drift-gated rather than linted. Refs AAASM-4513 Co-Authored-By: Claude Opus 4.8 (1M context) Claude-Session: https://claude.ai/code/session_01Rk7iwwAuGv6HbwK8hY8Fbr --- quickstart_snippets/strands-agents-tool-policy.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 quickstart_snippets/strands-agents-tool-policy.py diff --git a/quickstart_snippets/strands-agents-tool-policy.py b/quickstart_snippets/strands-agents-tool-policy.py new file mode 100644 index 00000000..11f82087 --- /dev/null +++ b/quickstart_snippets/strands-agents-tool-policy.py @@ -0,0 +1,12 @@ +with init_assembly( + gateway_url=gateway_url, + api_key=api_key, + agent_id="strands-demo-agent", + mode="sdk-only", +) as ctx: + print(f" Agent: {ctx.client.agent_id}") + print(f" Gateway: {ctx.client.gateway_url}") + print(f" Mode: {ctx.network_mode} (offline demo)") + print() + + policy = LocalPolicyEngine() From 8f5b6e51b8698e953ea804b2a848e97a7def9a30 Mon Sep 17 00:00:00 2001 From: Bryant Date: Mon, 13 Jul 2026 09:29:09 +0800 Subject: [PATCH 19/22] =?UTF-8?q?=E2=9C=A8=20(scripts):=20Add=20quick-star?= =?UTF-8?q?t=20framework-tabs=20generator?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Turns the vendored quickstart_snippets/ (manifest + per-framework .py slices) into the pymdownx.tabbed block inside a bounded BEGIN/END GENERATED region of docs/quick-start.md. Data-driven from manifest.json's frameworks array in listed order, stdlib-only, and idempotent so a CI drift check can re-run it and diff. A `--check` mode fails without writing when the doc is stale. Refs AAASM-4513 Co-Authored-By: Claude Opus 4.8 (1M context) Claude-Session: https://claude.ai/code/session_01Rk7iwwAuGv6HbwK8hY8Fbr --- scripts/generate_quickstart_tabs.py | 153 ++++++++++++++++++++++++++++ 1 file changed, 153 insertions(+) create mode 100644 scripts/generate_quickstart_tabs.py diff --git a/scripts/generate_quickstart_tabs.py b/scripts/generate_quickstart_tabs.py new file mode 100644 index 00000000..6e29e28d --- /dev/null +++ b/scripts/generate_quickstart_tabs.py @@ -0,0 +1,153 @@ +#!/usr/bin/env python3 +"""Generate the per-framework quick-start tab block in ``docs/quick-start.md``. + +WHY THIS EXISTS +--------------- +Quick-start §3 "Govern your first agent" (AAASM-4513) shows one ``pymdownx.tabbed`` +tab per supported Python framework, and every tab's code must be the *governance +slice of a runnable example* — not a hand-maintained copy that silently rots (the +AAASM-4451 stale-sample class). The snippets are vendored from the +``ai-agent-assembly/examples`` repo into ``quickstart_snippets/`` (committed, +because ``examples`` is a separate repo and the MkDocs build must stay hermetic). +This script is the single point that turns those vendored inputs into the tab +block, written into a bounded ``BEGIN/END GENERATED`` region of the doc so the tab +list is data-driven from ``quickstart_snippets/manifest.json`` — never hand-listed. + +A CI drift check (``.github/workflows/quickstart-tabs-check.yml``) re-runs this +script and fails if the committed ``docs/quick-start.md`` differs, so the doc can +never drift from the vendored snippets + manifest. + +DESIGN CONSTRAINTS (mirrors the examples ``extract_snippets.py`` gate) +---------------------------------------------------------------------- +* Standard library only — the CI drift check needs no install step. +* Idempotent: running twice produces no diff. The drift check enforces this. +* Data-driven: the tab list is exactly ``manifest.json``'s ``frameworks`` array, + in listed order, so a framework added upstream appears once it is re-vendored. +""" + +from __future__ import annotations + +import argparse +import json +import sys +from pathlib import Path + +# Region markers delimiting the generated block inside docs/quick-start.md. The +# markers must already exist in the doc; this script only rewrites what is +# between them, leaving the surrounding prose under human control. +BEGIN_MARKER = "" +END_MARKER = "" + + +class GenerateError(RuntimeError): + """A missing input or a malformed target region — fail the drift check loudly.""" + + +def _repo_root_from_script() -> Path: + # This file lives at /scripts/generate_quickstart_tabs.py. + return Path(__file__).resolve().parent.parent + + +def _indent_tab_body(line: str) -> str: + """Indent one tab-body line by 4 spaces, keeping blank lines empty. + + ``pymdownx.tabbed`` treats 4-space-indented content as the tab body (the same + convention the pip/uv install tabs in this file already use). Blank lines stay + empty so the output carries no trailing whitespace. + """ + + return f" {line}" if line else "" + + +def render_tab(label: str, code: str) -> str: + """Render one ``=== "