Repository navigation
feat(agents): ADK runtime core, provider switch and /v1 service - #12111
Merged
Merged
Conversation
anthropic[vertex] gives ADK's anthropic_llm module its AsyncAnthropicVertex credentials; the lock gains only the google-auth[requests] extra marker. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GdMSqLR5ww4ji74EUSmk9M
AGENT_PROVIDER selects anthropic-vertex (default) or gemini; model ids must match the provider. Adds the project, judge timeout, render image and concurrency, idle sweep, accepted audiences and the development-only fixture seed. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GdMSqLR5ww4ji74EUSmk9M
make_model and make_content_config build every agent's model from the settings; VertexClaude sends a response schema as a forced tool call, which ADK 2.11 leaves out for Claude. make_judge_client returns the scope and dataset judge with a 4 s budget and one retry, plus a scripted fake. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GdMSqLR5ww4ji74EUSmk9M
root, adapter, reviewer, scope and dataset judge prompts plus fixed refusals in English and German. policy.py composes each static instruction from them and the catalogue sources read verbatim: the style guide, the library rules and excerpts cut by heading from the repair prompt, the quality criteria and the review prompt. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GdMSqLR5ww4ji74EUSmk9M
RenderJob and RenderResult with the RuntimeAdapter and RenderBackend protocols; the probe harness; R1-R3 host gates with PNG hardening and the padding fallback, G3/G5/G7/G8 as advisory lines; the fake and local Docker backends, and the sandbox backend as a stub until spike S. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GdMSqLR5ww4ji74EUSmk9M
…est ledger The ledger is a context variable the route binds per request; plugins and the pipeline share it. ScopeGuard judges free text and fails closed, Budget halts only the root, ToolSafety allowlists tools per agent, validates arguments, caps results and maps errors to fixed codes. Attribution lines carry no content. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GdMSqLR5ww4ji74EUSmk9M
The root talks to the user with a constant static instruction plus server-set session facts; the adapters and the tool-less reviewer run inside the bounded pipeline (two attempts, one review) under their own isolation scope. App carries the plugins so adk web finds it. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GdMSqLR5ww4ji74EUSmk9M
AGENT_DEV_FIXTURE seeds new sessions from agents/evals/fixtures/cases in development only; scatter-basic/matplotlib and bar-grouped/seaborn ship with small synthetic CSVs and build their snapshots from plots/. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GdMSqLR5ww4ji74EUSmk9M
FastAPI without docs, the routes the BFF calls, in-memory session and artifact services, the caller check outside development, and a translator that emits only sanitised anyplot/1 events. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GdMSqLR5ww4ji74EUSmk9M
Fake renderer, scripted judge, ScriptedLlm for Gemini and VertexClaude over a fake Anthropic client; covers ok, repair, failed, padded canvas, not_ready, refusal, guard_unavailable and the route errors. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GdMSqLR5ww4ji74EUSmk9M
…ary and fixtures Unit tests with fakes: Claude structured output through the forced tool, the judge budget, verbatim prompt composition, PNG hardening and gates, a real harness render of the adapted scatter-basic file, plugin guardrails, the agent registry, the anyplot/1 translator, the caller check, a log canary and ADK's loader finding the app. The harness counts only scatter marks for G8, and the service's content-capture switch is a function the tests call. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GdMSqLR5ww4ji74EUSmk9M
agents/README.md covers what is built, every AGENT_* variable, adk web with the fixture seed (and ADK_DISABLE_LOAD_DOTENV, since adk web walks up to the root .env) and the service; the design doc's status line and the repository tree name the runtime core. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GdMSqLR5ww4ji74EUSmk9M
…eline unit tests The pipeline still yields one PlotResult when the render store refuses the shipped PNGs. Unit tests cover the result rules, the soft deadline and the fenced adapter request. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GdMSqLR5ww4ji74EUSmk9M
…aches the root ADK treated the never-closed pipeline-<hex> scope as a paused task: the next user message was stamped with it, hidden from the root, and the next plot_pipeline call replayed the first run's result. The pipeline now ends its scope with a finish_task response. A two-turn /v1 regression test covers both providers. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GdMSqLR5ww4ji74EUSmk9M
…p a late review An adapter plan or verdict that failed its output schema raised inside ADK, ended the run as failed/error with no repair, and put the model's answer into an ERROR log record. An after_model_callback now blanks such an answer, so the adapter gets repair feedback and an unread review ships as needs_attention with its reason. The review is also skipped when the soft deadline cannot fit it. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GdMSqLR5ww4ji74EUSmk9M
…osed on ENVIRONMENT Haiku 5.5 thinks by default and ADK 2.11 sends the signature-only block back as redacted_thinking, which would break the root's reply after a tool call; every Claude config now disables thinking. AGENT_MODEL for anthropic-vertex is limited to models that accept a forced tool_choice. ENVIRONMENT now defaults to production, and development is refused when K_SERVICE shows a Cloud Run revision, so a missing variable can no longer skip the caller check. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GdMSqLR5ww4ji74EUSmk9M
…e datasets An active-run entry whose stream never started was never cleared, wedging the session at 409 and hiding it from the sweeper. Runs now carry a start time and expire after the request deadline plus a margin, and a background task clears them too. A user gets one active run across sessions, and the dataset judge refuses every verdict but in_scope. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GdMSqLR5ww4ji74EUSmk9M
…iling the run Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GdMSqLR5ww4ji74EUSmk9M
…se plot notes Column names, the spec brief, set_bindings errors and the model-written changes and residual defects now reach the adapter, the reviewer and the root only inside fences after the data preamble. The dataset judge sees every header. The stream sanitiser also omits tilde-fenced and indented code, protocol-relative and mailto links and unclosed tags, and the plot event's notes go through it. get_dataset_profile trims itself to its cap, plot_pipeline gets a cap that fits a full PlotResult, and a pipeline call that ToolSafety refuses no longer counts as a daily run. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GdMSqLR5ww4ji74EUSmk9M
…t after any plan The overlap check only covered edits that touched a protected line: a full_code plan on attempt 2, or an edit adding PAGE_BG = "#FF00FF" further down, shipped as ok. apply_plan now compares the protected statements of the result with the base and refuses any further binding of THEME or a theme token. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GdMSqLR5ww4ji74EUSmk9M
…iterals The design's per-plan literal budget was missing; only the 200-character cap per literal existed. The pipeline now blocks a plan whose new string literals exceed it and hands the repair a line with the overshoot. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GdMSqLR5ww4ji74EUSmk9M
… cache breakpoints After a library switch, base=previous and get_current_code picked the other library's last version; versions now carry their library and both filter by it. Claude calls now carry ADK's prompt-cache breakpoints (App context-cache config, Claude only, 5-minute lifetime), so the adapter's static instruction is billed as a cache read after its first use. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GdMSqLR5ww4ji74EUSmk9M
…cancelled Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GdMSqLR5ww4ji74EUSmk9M
…le agent codes
The BFF's binding role pattern ^[a-z_]{1,32}$ rejected roles of 12 catalogue specs
(temperature_K, X1, lead_aVR, ...); it now copies the agents contract's pattern, and a
test keeps the copies equal. guard_unavailable, capacity, no_dataset and
rate_limited now reach the browser instead of upstream or invalid.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GdMSqLR5ww4ji74EUSmk9M
…he fragment Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GdMSqLR5ww4ji74EUSmk9M
ADK labels the dynamic instruction as an instruction, but Haiku 5.5 still replied about the session block in the live smoke. The root prompt now forbids mentioning, quoting, confirming or comparing it. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GdMSqLR5ww4ji74EUSmk9M
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GdMSqLR5ww4ji74EUSmk9M
Contributor
There was a problem hiding this comment.
🟡 Changes recommended
Unresolved prompt-injection paths, incomplete render artifacts, and unsafe stderr handling affect security and correctness.
7 open findings
Unfenced catalogue title enables root-agent prompt injection · New Unfenced model-authored fields enable adapter prompt injection · New Unbounded stderr buffering allows host memory exhaustion · New Exception feedback can leak arbitrary dataset cell contents · New Partial padding discards valid PNGs for other themes · New Completeness check must require every configured theme · New Design document has inconsistent runtime status and model provider · New
What changed in this PR
Adds the local runtime core for the ADK-based “Use with my data” agent network.
Changes:
- Adds provider-switched agents, guardrail plugins, and the adaptation pipeline.
- Adds the private
/v1service, SSE translation, rendering, and PNG hardening. - Updates BFF contracts, dependencies, fixtures, documentation, and tests.
| File | Description |
|---|---|
.dockerignore |
Includes agent prompt inputs. |
agents/README.md |
Documents the agent runtime. |
agents/anyplot/__init__.py |
Exposes the ADK app. |
agents/anyplot/agent.py |
Defines agents and plugins. |
agents/anyplot/briefs.py |
Builds bounded model briefs. |
agents/anyplot/code/edits.py |
Hardens generated edits. |
agents/anyplot/dev_fixture.py |
Adds development seeding. |
agents/anyplot/models.py |
Implements provider-backed models. |
agents/anyplot/opening.py |
Handles session initialization. |
agents/anyplot/pipeline.py |
Implements the plot pipeline. |
agents/anyplot/plugins/__init__.py |
Describes runtime plugins. |
agents/anyplot/plugins/budget.py |
Enforces usage budgets. |
agents/anyplot/plugins/ledger.py |
Tracks request usage. |
agents/anyplot/plugins/scope_guard.py |
Adds scope classification. |
agents/anyplot/plugins/tool_safety.py |
Validates tool traffic. |
agents/anyplot/policy.py |
Composes prompts and fences. |
agents/anyplot/prompts/adapter.md |
Defines adapter behavior. |
agents/anyplot/prompts/data_judge.md |
Defines dataset screening. |
agents/anyplot/prompts/refusals.yaml |
Adds fixed refusals. |
agents/anyplot/prompts/reviewer.md |
Defines render review. |
agents/anyplot/prompts/root.md |
Defines root behavior. |
agents/anyplot/prompts/scope_judge.md |
Defines scope screening. |
agents/anyplot/render/__init__.py |
Selects render backends. |
agents/anyplot/render/backends/__init__.py |
Documents backends. |
agents/anyplot/render/backends/fake.py |
Adds fixture rendering. |
agents/anyplot/render/backends/local.py |
Adds Docker rendering. |
agents/anyplot/render/backends/sandbox.py |
Adds sandbox stub. |
agents/anyplot/render/contract.py |
Defines render contracts. |
agents/anyplot/render/gates.py |
Evaluates render outputs. |
agents/anyplot/render/harness.py |
Runs sandboxed plots. |
agents/anyplot/render/png.py |
Hardens PNG output. |
agents/anyplot/render/runtimes/__init__.py |
Documents runtimes. |
agents/anyplot/render/runtimes/python.py |
Implements Python rendering. |
agents/anyplot/render/store.py |
Stores session renders. |
agents/anyplot/services.py |
Provides runtime services. |
agents/anyplot/session_state.py |
Defines validated state. |
agents/anyplot/settings.py |
Adds runtime configuration. |
agents/anyplot/sub_agents/__init__.py |
Documents sub-agents. |
agents/anyplot/sub_agents/adapter.py |
Implements adapters. |
agents/anyplot/sub_agents/reviewer.py |
Implements the reviewer. |
agents/anyplot/tools/__init__.py |
Documents agent tools. |
agents/anyplot/tools/session.py |
Adds session tools. |
agents/evals/fixtures/README.md |
Documents fixture cases. |
agents/evals/fixtures/cases/bar-grouped-seaborn/case.json |
Adds grouped-bar fixture metadata. |
agents/evals/fixtures/cases/bar-grouped-seaborn/data.csv |
Adds grouped-bar fixture data. |
agents/evals/fixtures/cases/scatter-basic-matplotlib/case.json |
Adds scatter fixture metadata. |
agents/evals/fixtures/cases/scatter-basic-matplotlib/data.csv |
Adds scatter fixture data. |
agents/main.py |
Adds the private service. |
agents/stream.py |
Translates and sanitizes SSE. |
api/routers/agent.py |
Aligns BFF roles and errors. |
changelog.d/agents-runtime.md |
Records the feature. |
docs/concepts/agent-network.md |
Updates implementation status. |
docs/reference/api.md |
Documents contract changes. |
docs/reference/repository.md |
Documents the new structure. |
pyproject.toml |
Adds agent dependencies. |
tests/unit/agents/code/test_edits.py |
Tests edit hardening. |
tests/unit/agents/runtime/__init__.py |
Defines runtime tests. |
tests/unit/agents/runtime/conftest.py |
Adds shared fixtures. |
tests/unit/agents/runtime/fakes.py |
Adds scripted model fakes. |
tests/unit/agents/runtime/test_canary.py |
Tests log privacy. |
tests/unit/agents/runtime/test_dev_fixture.py |
Tests development seeding. |
tests/unit/agents/runtime/test_models.py |
Tests provider models. |
tests/unit/agents/runtime/test_pipeline.py |
Tests pipeline behavior. |
tests/unit/agents/runtime/test_plugins.py |
Tests guardrail plugins. |
tests/unit/agents/runtime/test_policy.py |
Tests prompt composition. |
tests/unit/agents/runtime/test_registry.py |
Tests agent registration. |
tests/unit/agents/runtime/test_render.py |
Tests rendering and gates. |
tests/unit/agents/runtime/test_service_flow.py |
Tests end-to-end service flow. |
tests/unit/agents/runtime/test_stream.py |
Tests SSE translation. |
tests/unit/agents/test_adk_pin.py |
Tests dependency pins. |
tests/unit/agents/test_schemas.py |
Tests shared contracts. |
tests/unit/agents/test_settings.py |
Tests runtime settings. |
tests/unit/api/test_agent_router.py |
Tests BFF compatibility. |
uv.lock |
Locks new dependencies. |
🧠 Review effort: Balanced
💡 Configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
CodeQL flagged five polynomial-backtracking patterns in agents/anyplot/data/roles.py (py/polynomial-redos) on this PR. Possessive quantifiers and run-start lookbehinds remove the ambiguity; a 200,000-space bullet now parses in under 3 ms and the roles tests are unchanged. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GdMSqLR5ww4ji74EUSmk9M
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
- R1 now compares the hardened PNGs with THEMES instead of the outputs the backend returned, so a result with only one theme fails the host gates with a blocking line instead of passing. - A padded canvas replaces only the theme that missed: GateReport gains shipped_pngs (the hardened set with the padded copies over it), which the pipeline ships, so a one-theme padding keeps the other theme's PNG and both image artifacts exist. - error_summary returns the exception class (a builtin, or a qualified class from the plotting stack) and the plot.py line, never the message: ValueError(df["secret"].iloc[0]) carried a cell with no quotes to elide. - The local Docker backend drains stderr while the container runs and keeps only its last STDERR_LIMIT bytes, instead of buffering the whole stream through communicate() (16 MiB of stderr per theme peaked at about 50 MB of host memory before, under 1 MB now). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GdMSqLR5ww4ji74EUSmk9M
…n text - The root's session block (an InstructionProvider, sent with instruction priority) names the spec id, library, locale, counts and statuses only. The spec title started as a public issue, so a title with a newline and instruction text no longer lands there; the root reads it fenced as <spec_text> through get_spec_brief. root.md says so. - The adapter request carries the readiness hints (they quote catalogue lines), the feedback (failed edits and validator findings quote model-written code, reviewer lines are model-written) and the previous plan as one JSON object inside a <tool_notes> fence, the way the plot_pipeline result reaches the root. The reviewer's gate notes go into a <tool_notes> fence too, so both requests hold nothing but fixed headings and validated identifiers outside a fence. adapter.md and reviewer.md describe the blocks. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GdMSqLR5ww4ji74EUSmk9M
…ude default - The status line lists what is built (including bindings.apply, which the PUT bindings route and the set_bindings tool call) and what is not, dated 2026-10-09. - The Model decision records the owner's 2026-10-09 choice: Claude Haiku 5.5 on Vertex AI (eu) is the phase-1 default for every agent and the judge; Gemini 3.8 Flash, with the judge on gemini-3.5-flash-lite, is the second arm behind AGENT_PROVIDER. The agents table, the model factory and settings rules, the production environment, the baselines, the harness command, the local run block (now with ENVIRONMENT=development, which AGENT_RENDERER=local requires), GET /v1/status and the risk rows follow. - The fencing section covers <spec_text>, <tool_notes> and the class-only render error line; R3 pads only the theme that missed. - docs/index.md and the project guide drop "Gemini" and the skeleton-only status; the changelog fragment names the render-error and both-themes rules. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GdMSqLR5ww4ji74EUSmk9M
…ge family without a regex The render error summary now accepts only builtin exceptions and an exact list of plotting-library exception classes, so a made-up dotted name such as pandas.Alice_Smith_4111Error can no longer carry data into the repair feedback. A canvas miss of one theme names that theme instead of (both). The role-range family (y1, yn) comes from string operations instead of the last regex CodeQL flagged as polynomial. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GdMSqLR5ww4ji74EUSmk9M
Spike M, spike X, spike A' and the exit criteria name the Claude arm next to Gemini, and the harness and CI inputs carry a provider setting. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GdMSqLR5ww4ji74EUSmk9M
CodeQL moved its polynomial-regex alert to the last remaining role regex; the helper now matches the old pattern on 20,000 random names without a regex. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GdMSqLR5ww4ji74EUSmk9M
Contributor
There was a problem hiding this comment.
🟡 Changes recommended
Production can select the fake renderer, and configured library support is reported inconsistently with the actual adapter registry.
3 open findings
7 resolved since last review
Exception feedback can leak arbitrary dataset cell contents Unbounded stderr buffering allows host memory exhaustion Unfenced model-authored fields enable adapter prompt injection Unfenced catalogue title enables root-agent prompt injection Completeness check must require every configured theme Partial padding discards valid PNGs for other themes Design document has inconsistent runtime status and model provider
🧠 Review effort: Balanced
…ne library registry AGENT_RENDERER=fake returns fixture PNGs and runs no code, so the settings and make_backend refuse it unless ENVIRONMENT is development or test. AGENT_LIBRARIES is validated against the runtime registry (the normaliser's libraries), so /v1/status, eligibility and the adapter registry can no longer disagree; the adapter construction drops its silent filter. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GdMSqLR5ww4ji74EUSmk9M
…el arms Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GdMSqLR5ww4ji74EUSmk9M
This was referenced Oct 10, 2026
MarkusNeusinger
added a commit
that referenced
this pull request
Oct 10, 2026
…12115) ## Summary - Adds `anyplot-renderer` (`agents/renderer/`): a small FastAPI service with `POST /render`, `POST /render/{job_id}/cancel` and `GET /status` behind Cloud Run IAM and the ID-token claims check, that runs each theme of a job in its own Cloud Run sandbox (`sandbox do`, never `--write` or egress, explicit `PATH`), one at a time, with the limits spikes S and S2 measured: run directories on a 512 MiB in-memory volume the service requires on Cloud Run (`503 volume_missing` without it), a watchdog that kills a run past 64 MiB, 10,000 files or a 512 MiB `MemAvailable` floor, a kill that counts only once the launcher has exited, one retry when the launcher fails before the harness starts, bounded output, a cleaned probe, rlimits, a stop on client disconnect or cancel, JSON `500 internal` without the message, and a self-restart when a launcher outlives its kill. It only runs code; the host gates stay in the agents service. The image installs the plotting libraries without ADK; `agents/renderer/cloudbuild.yaml` builds, deploys a `candidate`, smokes and promotes it like the API, and creates the service on the first build only when `describe` reports it missing. - Adds the `remote` render backend, now the default (`AGENT_RENDERER=remote`, `AGENT_RENDER_URL`), so a local `adk web` and the deployed agents service render in the same sandboxes: an ID token from the metadata server when deployed, or from `AGENT_RENDER_TOKEN` or the developer's Application Default Credentials in development; retries over a cold start (1, 3, 8, 15 s within 30 s) for connection errors and front-end 429/5xx only, never for the renderer's own answers; a cancel call for abandoned renders; limit and measured value in the repair feedback. - Hardens the caller check in both services: `X-Serverless-Authorization` wins whenever it is present, because Cloud Run then checks only that header and passes `Authorization` through unverified. The harness prints a start and an end line and sets soft and hard limits alike. CI gains a `renderer-image` job (`ci-image.yml`) that builds the image and renders a seaborn plot through the harness with no network. ## Plan Design: `docs/concepts/agent-network.md` ("Render", "Serving and infrastructure"; status line and service table updated). The renderer split for phase 1 was decided on 2026-10-10 (one renderer for the deployed agents and for `adk web`). Follows #12111 and #12112. ## Test plan - [x] `uv run ruff check . && ruff format --check .`, `mypy api core agents` (101 files), `pytest tests/unit tests/integration` (7935 passed; executor tests run the real harness against a fake launcher: kill path, cancel, disconnect, stuck launcher, retry once and twice, byte, file and memory watchdog, bounded output, refused output files; service tests cover the caller check, the slot, low memory, the volume check and `/status`; the `remote` backend is tested against the in-process app and once end to end through the real executor and host gates), changelog check, `uv lock --check` - [x] Deployed to Cloud Run as a throwaway service (`anyplot-renderer-spike`, a role-less service account, Cloud Build `5b86cc7a` from this head with `_SMOKE_RENDER=false`, 3 min 51 s): the first-build path created the service; `gcloud beta run deploy --sandbox-launcher` and the repeated `--add-volume` flags are accepted by the Cloud Build gcloud image (587.0.0); an anonymous call gets 403; `/status` with the owner's gcloud token reports `sandbox: true`, `volume: true` (the in-memory volume reports its 512 MiB limit), 3,524 MiB available; a real matplotlib render answered in 2.9 s with a 108 KB PNG; spike X (122 cases) renders through it from a laptop with `--gcloud-token` - [x] Configured build on the throwaway with `_SMOKE_RENDER=true` (Cloud Build `a89a46b2` from `09f50b62d`, 3 min 47 s): the `--no-traffic` candidate path, the smoke as the service's own account (impersonated; a build cannot mint an ID token for itself, which builds `da6f53f5` and `a9d3ac7f` showed and the two fix commits address), `/status` with the volume in place, a real render in 3.2 s, promote. The `renderer-image` CI job ran and passed on every push. - [x] `/tmp` fill through the promoted revision (the review's open question): code writing 40 MiB files into the sandbox's private `/tmp` ends after 4.6 s with `OSError: [Errno 28] No space left on device`; the instance stays up (3,451 MiB available before, 3,328 after), the memory watchdog never fires. Decided in the design doc: 4 GiB with one sandbox at a time, no `RENDERER_BIND_TMP`, no 8 GiB instance. The throwaway service, its service account and its images are deleted. - [ ] Not verifiable before merge: the Application Default Credentials token path against Cloud Run IAM; `.github/workflows/` has no pre-merge loop - [ ] After merge nothing deploys: the real `anyplot-renderer` service account, the first build and the `deploy-renderer` trigger are owner tasks (design doc, task 14) --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
MarkusNeusinger
added a commit
that referenced
this pull request
Oct 10, 2026
## Summary - Adds the admin-only "Use with my data" chat page at `/debug/agent?spec=&library=&language=` (`app/src/pages/AgentChatPage.tsx`): data panel with a 200 KB counter, 20-row preview, a column dropdown for every spec role (series families get one dropdown per member), the chat thread, a progress timeline that follows the `anyplot/1` stream, a queue-position counter in the chat's corner, `.stop()` through the cancel route, and library pills. Built only with `VITE_ENABLE_AGENT_CHAT=true` (off in `app/cloudbuild.yaml`, so production ships without the chunk) and opens only in local development or for a browser carrying the admin hint. - Adds the result card (`app/src/sections/agent-chat/ResultCard.tsx`) with the plot page's overlay actions (copy image, download PNG, open full size), a light/dark switch through the theme toggle route, the adapted `plot.py` with copy and download, `data.csv` download, the change list and residual notes, a refine composer and the quick-feedback slot; plus the `.adapt()` overlay button on the plot page, gated by build flag, admin hint and the BFF's eligibility route so public visitors never call the debug API. - Two small contract changes on the service side so the page can address versions and roles reliably: the `plot` event carries `version` (agents translator and BFF allowlist), and the dataset answer lists the spec's `roles` with a 422 that keeps the binding check's lines. Analytics events (enum properties only) are documented in `docs/reference/plausible.md`; a loopback mock of the BFF (`app/scripts/agent-bff-mock.mjs`) drives the whole flow without the API, the agents service or a model. ## Plan Design: `docs/concepts/agent-network.md` (sections "Frontend", "Result card", "Analytics"); this is the frontend PR of the phase-1 roadmap, built on the merged runtime core (#12111) and run queue (#12112). ## Test plan - [x] `cd app && yarn lint && yarn fm:check && yarn type-check && yarn test` (81 files, 750 tests) and `yarn build` with the flag on and off; the default build contains no agent client - [x] `uv run --extra dev ruff check . && ruff format --check .`, `mypy api core agents`, `pytest tests/unit tests/integration` (7816 passed), changelog check, `uv lock --check` - [x] Driven in the browser against the mock at 1280 px and 390 px, light and dark: parse, bindings incl. series families and a refused set, queued counter, progress timeline, result card actions (clipboard PNG, downloads named `plot.py`/`data.csv`, open full size, copy code), theme switch, repair round, refusal, error, stop then a new turn without a 409, ineligible pair; no horizontal scroll, 16 px gutter at 390 px - [ ] After merge: `deploy-app` and `deploy-api` Cloud Builds succeed; production stays unchanged (`_VITE_ENABLE_AGENT_CHAT` false, `AGENT_ENABLED` false) - [ ] Once the agents service is deployed: open `/debug/agent?spec=scatter-basic&library=matplotlib` as admin and run one "Create plot" end to end --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.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.



Summary
anthropic-vertexwith Claude Haiku 5.5 as the default,geminiwith Gemini 3.8 Flash), the root agent, one adapter per enabled library, the reviewer, theplot_pipelineworkflow, the ScopeGuard, Budget and ToolSafety plugins with the request ledger, the/v1FastAPI service with theanyplot/1SSE translator, the render contract, harness, gates and PNG hardening with fake, local Docker and sandbox-stub backends, and a development fixture soadk web agentsruns offline.THEMEand the theme tokens survive full-code plans,ENVIRONMENTfails closed, abandoned runs expire, one run per user, Claude runs with thinking disabled and forced tool use for structured output, Claude prompts get cache breakpoints, a plan may add at most 2,000 characters of new literals.Binding.roleuses the agents contract pattern (12 catalogue specs have roles liketemperature_K), and the retryable agent codesguard_unavailable,capacity,no_datasetandrate_limitedpass through by name;docs/reference/api.mdupdated.PATH, retry on launcher exec failure); the local renderer has no image yet; the request token budget still counts cache reads at full weight.Plan
docs/concepts/agent-network.md(design doc) and the session plan; the implementation follows the "Architecture" and "Guardrail layers" sections. ADK behaviours that differ from the plan are documented inagents/README.mdand the module docstrings (noresponse_schemafor Claude,effortinstead of thinking level, isolation scopes for sub-agents, content-free progress events).Test plan
uv run ruff check .anduv run ruff format --check .cleanuv run --extra typecheck --extra agents mypy api core agents: no issues in 92 filesENVIRONMENT=test uv run pytest tests/unit tests/integration: 7,689 passed, 1 skipped (existing MonoLisa skip)uv run python -m tools.changelog check --base origin/mainanduv lock --checkpass/v1in-process for both providers with scripted models and the fake renderer; a two-turn session; reviewer rejection plus repair;not_ready, 413/404/409/422/403/503 routes; a real matplotlib render through normalise, edits, loader, harness and gates at exactly 3200x1800; a log canary shows no message or cell content in any log recordadk web agentsdiscovers the app with the fixture (/list-appsreturns["anyplot"]);uvicorn agents.main:appanswers/v1/status,/docsis 404euwith owner ADC: Haiku 5.5 adapter and reviewer with forced tool use and thinking disabled, two turns, about 0.03 USD; Gemini 3.8 Flash one root calladk web agentslocally peragents/README.md(withADK_DISABLE_LOAD_DOTENV=1) and try one "Create plot" on a fixture case