Security review + code review of the asset-gen feature surfaced concrete issues;
this fixes them and adds regression tests (request-shaping layer, FakeHttpTransport).
Security
- SafeZipExtractor enforces an extension allowlist; ModelImportPipeline passes an
inert model/texture allowlist so a provider archive can't drop a .cs/.dll under
Assets/ and have the Editor compile/load it (code execution on import).
- AssetGenJobManager refuses non-http(s) download URLs before fetching
(file:// SSRF / local-file read into the project).
Provider correctness
- Meshy image->3D polls /openapi/v1/image-to-3d/{id} (was the v2 text URL).
- Meshy text->3D honors texture=true via the preview->refine two-phase flow.
- OpenRouter image->image attaches the reference image (content image_url part).
- fal image->image uses the /edit endpoint + image_urls array; width/height
forwarded as image_size.
- Sketchfab search forwards categories/count/cursor/downloadable; preview doc
corrected (returns metadata, not a base64 thumbnail).
- Job import calls AssetDatabase.Refresh() before importing a freshly written file.
Local image input (image_path)
- New LocalImage helper; image_path is read and sent inline as a base64 data URI
for Meshy / fal / OpenRouter. Tripo rejects local images with a clear error
(needs a hosted image_url; its upload flow is not wired).
Cleanup (no behavior change)
- Shared AssetGenPaths + ProviderHttp helpers, HttpResult.Ok, MissingKeyMessage,
cached glTFast probe, dead-field / per-frame-alloc removal, CLI _emit.
Docs: README + manual-verification updated (image_path support; transparency is
import-flag-only; width/height fal-only).
Verified: package compiles clean; Python 1306 passed / 3 skipped. Meshy refine,
fal /edit, and image_path data-URI paths are unit-tested at the request layer
only -- live smoke per provider (real keys) still pending.
Claude-Session: https://claude.ai/code/session_015DAUrMR5UaSEzEn2wNPrEP
Drop Hunyuan (Tencent TC3-HMAC) — high effort and not testable by the maintainer:
delete HunyuanAdapter + TencentCloud3Signer (+tests), unwire from AssetGenProviders,
SecureKeyStoreConstants, the GUI provider list, and Python/CLI/README/manual-verify docs.
Refresh model defaults to current (2026) SOTA, verified against provider docs:
- fal: fal-ai/flux/dev -> fal-ai/flux-2 (FLUX.2 dev; cheaper and higher quality)
- Tripo: v2.5-20250123 -> v3.1-20260211 (current recommended model)
- Meshy: drop deprecated art_style (errors on Meshy-6); pin ai_model=meshy-6
- OpenRouter: gemini-2.5-flash-image-preview (delisted) -> gemini-2.5-flash-image
Fix two tests that asserted the now-implemented 'meshy' provider throws (repointed to the
now-removed 'hunyuan'). They were silently wrong: the headless --full runs never executed
EditMode tests (Unity -quit exits before -runTests; compile-only). A real -runTests run
now passes 62/62 AssetGen EditMode tests; Python suite 1299 passed.
Claude-Session: https://claude.ai/code/session_01Tjpb5gYgUe2AUJuRdXr7Lv
Add glTFast (com.unity.cloud.gltfast) as an optional dependency in the Dependencies tab
(detect + Install/Remove + bulk Install-All), so GLB generation/import is one click away.
README 'AI Asset Generation' section (providers, BYO-key in the Asset Gen tab, OS secure
store, manage_tools to enable, async tool usage). docs/asset-gen-manual-verification.md
checklist for live validation (real keys + licensed editor; Hunyuan TC3 two-header
caveat). Drop an incidental 'manage_tools' mention from a Phase 0 test comment so the
tool-symmetry quarantine guard stays honest.
Claude-Session: https://claude.ai/code/session_01Tjpb5gYgUe2AUJuRdXr7Lv
Partial — phases 0,2-7 to be authored just-in-time during incremental execution
(plan-authoring workflow rate-limited on those phases). Phase 1 is fully detailed
and serves as the task-granularity template.
Claude-Session: https://claude.ai/code/session_01Tjpb5gYgUe2AUJuRdXr7Lv
README-zh.md edits + .meta/uv.lock churn on brand-distribution-analytics, committed
(not stashed) so the feature branch can be checked out in the main worktree for editor
testing. Untracked .agents/ and AGENTS.md intentionally left out.
Claude-Session: https://claude.ai/code/session_01Tjpb5gYgUe2AUJuRdXr7Lv
Mirror the EN restructure on the zh README — one-line English↔中文 | Discord↔Wiki
nav with the brand connector, Recent Updates moved to the top, brief 它能做什么 with
the tool catalog behind a link, combined ## 快速开始, dropped badges/工作原理/Asset Store/
the giant inline tool list. Kept the zh-only detail sections (手动配置, 多实例, Roslyn,
故障排除, 贡献, 遥测) and rewrote the authored copy to read like native dev Chinese,
not machine translation.
- favicon now rasterized from the full logo-mark.svg (was the simplified
favicon-safe mini, which dropped the inner edges, 3rd rung, and node dots);
remove the now-unused logo-mark-mini.svg
- add docs/images/connector.svg (blue node — cyan bridge — teal node, echoing the
split-cube 'two things connected') and use it between English/中文 and Discord/Wiki
- bump the Recent Updates summary to an <h3> (one size up)
Claude-Session: https://claude.ai/code/session_01XFiuAUxNS9riUJFFBEHvui
- center the full hero (language switcher + sponsor + badges + tagline + gif) via
<div align=center>, matching the centered logo (EN + zh)
- replace the repetitive all-stdio clients table with a clean inline client list
- replace the plain mermaid 'How it works' with a one-line flow + architecture link
- remove 'How it compares' (the hand-rolled-scripting comparison added nothing; the
Aura framing already lives in the 'Unity AI Tools by Aura' section)
Claude-Session: https://claude.ai/code/session_01XFiuAUxNS9riUJFFBEHvui
Center the <picture> logo header (EN + zh) via <p align=center>, and remove the
Website/Visit and PyPI-version badges from the badge row (kept Downloads, Release,
CI, OpenUPM, Stars, Docs, Discord, Unity, Python, MCP, MIT).
Claude-Session: https://claude.ai/code/session_01XFiuAUxNS9riUJFFBEHvui
- docs-deploy.yml: map GOATCOUNTER_CODE into the Build env so the cookieless
beacon actually activates when the maintainer sets the variable (was a
producer-without-consumer gap that silently disabled docs traffic)
- MAINTAINER_ACTIONS.md: rewrite the stats section to match the shipped private
design (drop the stale 'grant Actions write + commit data.json' over-priv item,
add the required STATS_GITHUB_TOKEN PAT, fix the social-preview note)
- docusaurus.config.js: wire favicon-32.png (was an orphaned generated asset)
- lowercase 3 stray 'MCP For Unity' refs in roslyn.md / migrations/v6.md
- remove internal docs/superpowers/ spec+plan scratch from the PR (contradicted
the shipped Ocean/private design); gitignore superpowers artifacts
Whole-branch review: 6/6 Critical/Important confirmed, 0 refuted, all docs/config
hygiene — build/tests/security/correctness all clean. These are the fixes.
Claude-Session: https://claude.ai/code/session_01XFiuAUxNS9riUJFFBEHvui
Swap the wide hero banner for a compact mark + wordmark lockup, served via
<picture> so the wordmark flips light/dark with the GitHub theme (the colored
Ocean mark is theme-independent). Applies to EN + zh READMEs. Removes the
now-orphaned docs/images/logo.png banner copy (the docs site keeps its own).
Claude-Session: https://claude.ai/code/session_01XFiuAUxNS9riUJFFBEHvui
Spec for the brand-standardization / README+distribution / unified-analytics
program, informed by a 4-agent inventory+research pass. Decomposes into 3
focused upstream PRs. Awaiting review before implementation.
Claude-Session: https://claude.ai/code/session_01XFiuAUxNS9riUJFFBEHvui
15 existing markdown files moved with git mv (history preserved), wired
into the Docusaurus sidebar across Guides, Architecture, Contributing,
and Migrations categories. Images that the migration docs reference
copied into website/static/img/ and refs rewritten to /img/...
Brand-neutral URL strategy holds: every slug omits "mcp-for-unity" /
"unity-mcp" — e.g. /guides/cli, /architecture/remote-auth,
/migrations/v8. A future rename touches docusaurus.config.js, not URLs.
Deferred to a follow-up:
- docs/development/README-DEV-zh.md (Chinese dev guide)
- docs/i18n/README-zh.md (Chinese overview)
These need Docusaurus i18n config (defaultLocale + zh in locales) and
a full translation pass before they make sense on the site.
Minor edits limited to making files build-clean (M2 plan: no content
rewrites):
- Front-matter added to cli-examples.md and cursor.md (no top-level H1)
- v8.md's [CUSTOM_TOOLS.md] link repointed at /guides/custom-tools
- dev-setup.md's broken language switcher repointed at GitHub source
- install.md's M2 forward-reference link now goes to /guides/client-configurators
Root README still points at docs/images/ for its hero gif — that path
keeps working because the images remain in docs/images/ for now and
will move only when README is slimmed in M5.
The two Antigravity apps coexist on the same machine and use different
MCP config paths, so they need to be separate configurators in the
client list.
- AntigravityConfigurator: renamed display label to "Antigravity 2.0"
(path now ~/.gemini/config/mcp_config.json, already changed in the
parent commit) and updated the installation-steps copy to match.
- AntigravityIdeConfigurator (new): same JsonFile shape with serverUrl
HTTP property, points at ~/.gemini/antigravity-ide/mcp_config.json
where the IDE build keeps both its runtime state and its mcp_config.
IsInstalled checks for that dedicated dir.
Manual configuration guidance updated in README, README-zh, and
MCP_CLIENT_CONFIGURATORS.md to reflect both clients side by side and
spell out which path each one writes to.
Closes the asymmetry between python-tests.yml (auto-fires on every PR
via pull_request) and unity-tests.yml (only fires on labeled
pull_request_target, or on push events). Same-repo PRs now get a
unity-tests status check immediately on open; fork PRs also get the
check but run in the fork's secret-less context, so the existing
detect step writes unity_ok=false and the job exits clean with a
"missing license secrets" notice. The status appears but signals the
fork-PR contributor that a maintainer needs to apply 'safe-to-test'
for a real run (existing gating pattern preserved).
Three changes:
1. Add 'pull_request: branches: [main, beta]' to the workflow triggers
with the same path filter as pull_request_target. The job-level if:
gates already pass through non-pull_request_target events, so no
gate edits are needed.
2. Extend the matrix selector to honor 'full-matrix' label on
pull_request events too, not just pull_request_target. Lets
in-repo PR contributors opt into the wide matrix at PR-open time
without waiting for the labeled-pull_request_target event.
3. Add a workflow-level concurrency group keyed on
`github.head_ref || github.ref`. Same-repo PRs would otherwise
fire both push (on the branch SHA) and pull_request (on the PR
SHA) and run the matrix twice; concurrency dedupes them.
Selector dry-run across the seven trigger cases confirms correct
behavior: default leg on unlabeled PR open / feature push; FULL on
labeled PR open + pr_target / push to beta / workflow_call /
workflow_dispatch.
Doc update in docs/development/README-DEV.md explains the new PR
status-check behavior and the fork-PR caveat.
Shorter, less verb-y. Same semantics: applying the label to a PR opts
into the 4-version matrix on the next pull_request_target event, on top
of safe-to-test for fork PRs and standalone for in-repo PRs.
Also renames the related internal identifiers for consistency:
- WIDE_LABEL env var -> FULL_MATRIX_LABEL
- "wide matrix" wording in comments + echo output -> "full matrix"
- docs/development/README-DEV.md prose mirrors the new name
Selector dry-run verified for both label states:
pull_request_target + FULL_MATRIX_LABEL=true -> full matrix
pull_request_target + FULL_MATRIX_LABEL=false -> default only
- Adds a `run-wide-matrix` label trigger. Applying it to any PR (in-repo or
fork) causes pull_request_target to re-run the workflow with the full
4-version matrix. Mirrors the existing `safe-to-test` opt-in pattern.
- Fork PRs still need `safe-to-test` as the base gate for secret safety;
`run-wide-matrix` layers on top.
- In-repo PRs need only the new label (the push-event run already covered
the default leg).
- Adds `defaultVersion` field to tools/unity-versions.json and points it at
6000.0.75f1 (Unity 6.0 LTS). The narrow-matrix path now runs Unity 6 on
PRs and feature-branch pushes instead of the 2021.3 floor. The 'floor'
role still identifies the package minimum and is exercised in the wide
matrix; it just no longer doubles as the default-leg version.
- Documents both changes in docs/development/README-DEV.md.
Dry-run verification of the matrix selector:
- push to feature branch (no label) -> ["6000.0.75f1"]
- push to beta -> all 4 versions
- pull_request_target + WIDE_LABEL -> all 4 versions
Pre-existing CI ran only Unity 2021.3.45f2, so compile errors gated behind
#if UNITY_2022_*_OR_NEWER / #if UNITY_6000_*_OR_NEWER slipped through (#1100,
#1105). PRs now run only the floor version; pushes to beta, workflow_call from
release pipelines, and manual dispatch run the wide matrix (2022.3.62f1,
6000.0.75f1, 6000.4.8f1 added). All four versions verified present as GameCI
Docker images.
The existing publish gate (update_unity_beta_version, publish_pypi_prerelease)
already 'needs: [unity_tests]', so widening the matrix automatically widens the
gate — no changes to beta-release.yml or release.yml.
tools/check-unity-versions.{sh,ps1} let developers reproduce CI locally via
Unity Hub install OR --docker mode (no install required, runs the same GameCI
containers CI uses). Opt-in pre-push hook (tools/install-hooks.sh) runs the
compile-only check when a push touches Unity code paths.
Shared source of truth: tools/unity-versions.json — consumed by both the CI
matrix preamble job and the local scripts.
Coverage gap documented in JSON \$coverageGap: UNITY_6000_5_OR_NEWER and
UNITY_6000_6_OR_NEWER branches in UnityObjectIdCompat.cs are not exercised
because GameCI has not yet published 6000.5+/6000.6+ images. Bump the
'rolling' row when available.
Reflect the new onboarding wizard, multi-client one-click flow,
Claude Desktop transport auto-coercion, and startup config rewrite
in the README. Update the Claude Desktop section of the configurator
guide to drop the stale "throw on HTTP" language.
PlayMode tests require entering play mode which triggers a domain reload.
On large projects this can take >15s, causing the hardcoded 15s init
timeout to auto-fail the test job before tests actually start.
This adds an `init_timeout` parameter to `run_tests` that flows through
the Python server → C# RunTests handler → TestJobManager. When set, the
per-job timeout overrides the 15s default. The value is persisted across
domain reloads via SessionState.
Changes:
- Python: Add `init_timeout` param to `run_tests()` function signature
- C# RunTests: Read `initTimeout` param and pass to `StartJob()`
- C# TestJobManager: Per-job `InitTimeoutMs` field with fallback to
`DefaultInitializationTimeoutMs` (15s), persisted in SessionState
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>