❯ Guillaume Laforge

Monday Morning SDK Maintenance with Antigravity Scheduled Automations

Monday Morning SDK Maintenance with Antigravity Scheduled Automations

Maintaining an unofficial SDK (or two) means playing a constant game of catch-up with upstream APIs. Over the past few months, I have been maintaining two Java libraries: the Gemini Interactions API Java SDK and the unofficial Antigravity SDK for Java.

Both upstream targets move fast. The Gemini Interactions API updates its public OpenAPI specification (interactions.openapi.json) whenever new agent capabilities, voice synthesis options, or webhook parameters land. Meanwhile, the official Antigravity Python SDK publishes new wheels to PyPI containing updated native Go localharness binaries, compiled Protobuf descriptors, and new Python classes.

Instead of manually checking both upstream sources every week, I configured two scheduled automations in Google Antigravity. Every Monday morning at 9:00 AM, Antigravity wakes up, spins up two fresh agent sessions in the background, audits the upstream changes against my local repositories, and prepares a complete implementation plan waiting for my review when I sit down with my coffee.

This morning (Monday, November 2), both automations found upstream changes, drafted their plans, implemented the code and unit tests after my review, and allowed me to release new versions of both SDKs to Maven Central before 10:00 AM.

Note

Both releases are live on GitHub and Maven Central:

How Antigravity Scheduled Automations Work

Antigravity includes a customization system built around rules (GEMINI.md, AGENTS.md), on-demand skills (SKILL.md), MCP servers, plugins, and background sidecars. A scheduled automation is a built-in schedule sidecar that runs on a standard 5-field cron expression and invokes agentapi new-conversation to start a new agent turn on each trigger.

Because each scheduled run starts a brand-new conversation with no memory of previous weeks, my setup splits the responsibility into two layers:

  1. A lightweight sidecar.json automation file in ~/.gemini/config/sidecars/<id>/sidecar.json that defines the cron schedule (0 9 * * 1 for Monday at 9:00 AM) and a short trigger prompt.
  2. Detailed, repository-specific instructions checked into git (AGENTS.md, UPDATING_SDK.md, and helper scripts like sync-harness.sh) that tell the agent how the project is structured and how to audit upstream changes.

Keeping the detailed synchronization steps inside AGENTS.md in each git repository keeps the cron prompt short and ensures that any manual conversation in that workspace benefits from the exact same instructions.

Setting Up the Two Monday Morning Automations

You can create a scheduled automation interactively by typing /automation in Antigravity, or visually from Antigravity 2.0’s Automations panel in the left sidebar (clicking Create with Prompt or + New). The built-in automation skill walks you through choosing a cadence (hourly, daily, weekly, or custom cron), picking the day and time, writing the prompt, and running a test turn to record the required file and URL permissions.

Antigravity Automations panel
Antigravity Automations panel

Under the hood, Antigravity writes a sidecar.json file for each automation. Here are the two configurations I use.

1. Checking the Gemini Interactions OpenAPI Spec

The first automation lives in ~/.gemini/config/sidecars/check-remote-api/sidecar.json:

{
  "builtin": "schedule",
  "restart_policy": "always",
  "args": [
    "0 9 * * 1",
    "agentapi",
    "new-conversation",
    "--",
    "Check if there's an update on the remote OpenAPI JSON file, and prepare an implementation plan with the needed changes"
  ],
  "display_name": "Interactions API > Check remote API"
}

When this automation runs in the gemini-interactions-api-sdk workspace, the agent automatically reads the repository’s AGENTS.md file, which points it to UPDATING_SDK.md. That document gives the agent its step-by-step runbook:

  1. Fetch the official OpenAPI specification from https://ai.google.dev/static/api/interactions.openapi.json.
  2. Diff it against the local openapi.json copy stored at the root of the repository:
    curl -s https://ai.google.dev/static/api/interactions.openapi.json > remote_openapi.json
    diff -u openapi.json remote_openapi.json
    
  3. Analyze any added, modified, or removed schemas, endpoints, query parameters, and Server-Sent Events (SSE) delta types.
  4. Keep backward compatibility in mind: when the upstream OpenAPI spec removes an experimental field or enum constant, mark the corresponding Java record or enum value as @Deprecated rather than deleting it and breaking existing callers.
  5. Plan the updates across the Java 21 model records, builders, GeminiInteractionsClient, unit tests, README.md, and the bundled Agent Skill (skills/gemini-interactions-java-api/SKILL.md).

2. Checking the Antigravity Python SDK and Native Go Harness

The second automation is defined in ~/.gemini/config/sidecars/check-new-version/sidecar.json:

{
  "builtin": "schedule",
  "restart_policy": "always",
  "args": [
    "0 9 * * 1",
    "agentapi",
    "new-conversation",
    "--",
    "Check if there's a new version of the Antigravity Python SDK wheel, and embedded Go localharness. \nYou can also check the official website changelog to see if they mention new versions with detailed release notes here: https://antigravity.google/docs/changelog?tab=sdk\nIf there's an update, prepare an implementation plan detailing the needed changes."
  ],
  "display_name": "AGY Java SDK > Check new version"
}

Synchronizing the Antigravity Java SDK is more involved because there is no single OpenAPI file to diff. As I explained when introducing the Antigravity SDK for Java, the official Python SDK wraps a native Go binary called localharness and communicates with it over standard input/output and WebSockets using Protobuf messages.

To make this repeatable for the agent, I documented a 6-step upstream synchronization workflow in the repository’s AGENTS.md:

StepWhat the Agent Does
1. Check PyPI & Sync BinariesQueries https://pypi.org/pypi/google-antigravity/json via ./sync-harness.sh, compares the upstream version against antigravity-sdk-harness/.../.version, downloads the 6 platform wheels (linux-x86_64, linux-aarch64, osx-aarch64, osx-x86_64, windows-x86_64, windows-aarch64), and unpacks the Python source into /tmp/antigravity_upstream/.
2. Extract Compiled Protobuf DescriptorsDecompiles the binary FileDescriptorProto directly from localharness_pb2.py and content_pb2.py in Python so it never has to guess wire field names (Go’s localharness rejects unknown JSON fields immediately).
3. Audit Python Source & TestsDiffs the unpacked Python package (types.py, connection_config.py, hooks/policy.py, workflows.py, and *_test.py) against the previous version, cross-referencing the public changelog at antigravity.google/docs/changelog?tab=sdk.
4. Update Code & Parity TestsUpdates localharness.proto, Java builders, and records, and creates a dedicated FeatureParity<version>Test.java unit test class covering every new capability.
5. Verify Wire CompatibilityRuns fast unit tests (./mvnw test), formats code (./mvnw spotless:apply), and runs a live smoke test against the new localharness binary.
6. Sync Docs & Agent SkillUpdates README.md and skills/antigravity-sdk-java/SKILL.md so coding agents using the Java SDK know about the new APIs.

For step 2, having this tiny Python snippet right inside AGENTS.md saves the agent from ever guessing a Protobuf field name:

import google.protobuf.descriptor_pb2 as d_pb2
from google.antigravity.proto import localharness_pb2

proto = d_pb2.FileDescriptorProto()
localharness_pb2.DESCRIPTOR.CopyToProto(proto)
print(proto)

Enabling the Automations and Project Permissions

Once the sidecar.json files are created, you enable them in the Antigravity Automations dashboard (sidecar://dashboard) and bind each automation to its target project workspace.

Because scheduled automations run unattended at 9:00 AM, they only have access to the permissions granted to their project (plus global permissions in ~/.gemini/config/config.json). Any unapproved shell command or external domain fetch is automatically denied during a background run. Running a test turn when setting up the automation lets you approve read_url(ai.google.dev), read_url(antigravity.google), curl -s https://pypi.org/pypi/google-antigravity/json, and ./mvnw once, so the Monday morning check can complete its analysis without getting stuck on a permission prompt.

Notice also what the automation prompt asks the agent to do: “prepare an implementation plan detailing the needed changes”. I intentionally stop the unattended run at the planning stage. I want the agent to do all the research, diffing, and design work on its own, and then pause with an implementation plan artifact so I can review the proposed changes before it touches the code or triggers a release.

What Happened This Morning: Two Automated Audits, Two Releases

When I opened Antigravity this morning shortly after 9:10 AM, both Monday automations had just finished running and had produced detailed implementation plans.

1. Gemini Interactions API Java SDK v2.1.1

In the Gemini Interactions SDK workspace, the agent spotted that the remote interactions.openapi.json specification had changed across 22 schemas (with 4 newly added schemas and 29 legacy schemas removed) and prepared the implementation plan. I reviewed the plan, clicked Proceed, and let the agent update 21 files, add a 314-line unit test suite (OpenApiUpdateNov02Test.java), verify that ./mvnw test and ./mvnw javadoc:javadoc passed without warnings, and update both README.md and SKILL.md.

Click to see what changed in Gemini Interactions API Java SDK v2.1.1
  • Interaction continuation and metadata: A new REQUIRES_CONTINUATION status value on Interaction.Status (used alongside continuationToken when resuming paused interactions), plus per-interaction webhookConfig, custom labels (Map<String, String>), maxTotalTokens, and initialInputTokens in Usage.
  • Model environments: ModelInteractionParams now supports attaching a BaseEnvironment or environmentId directly, matching CreateModelInteractionParams in the spec.
  • Speech configuration and voice remixing: GenerationConfig renamed speech_config to structured_speech_config (which we mapped with @JsonProperty("structured_speech_config") and @JsonAlias("speech_config") so existing JSON payloads and builder methods keep working), and PromptedVoice added a base_voice property to remix an existing voice from a natural-language prompt:
    Voice remixedVoice = client.createVoice(Voice.builder()
        .model(ModelOption.GEMINI_3_8_FLASH_TTS)
        .displayName("Faster Astronomer")
        .prompted("Speak at a brisk, energetic pace", astronomer.id())
        .build());
    
  • Client query parameter overloads: New overloads on GeminiInteractionsClient for createAgent(agent, parent), listTriggerExecutions(triggerId, pageSize, pageToken, filter), and createWebhook(webhook, webhookId).
  • Non-breaking deprecations: Rather than deleting the 29 removed schemas (such as legacy RAG/Retrieval steps and CodeMenderAgentConfig), the agent marked them with @Deprecated so existing users upgrading to v2.1.1 will not hit compilation errors.

2. Antigravity SDK for Java v0.3.3 (Upstream v0.1.23 Parity)

In parallel, the second automation detected that the upstream google-antigravity package on PyPI had moved from 0.1.22 to 0.1.23. Once I approved its 6-phase upgrade plan, the agent synchronized the native Go binaries, updated 21 files, wrote 369 lines of parity tests in FeatureParity0123Test.java, ran Spotless and the test suite, and updated the documentation and skill files.

Click to see what changed in Antigravity SDK for Java v0.3.3
  • Native Go localharness v0.1.23: Updated the binaries across all 6 OS/architecture slices and added the new Protobuf fields in localharness.proto (SectionHeaderFormat, CustomSystemInstructions.Part.title, ToolOutputTruncation, SkillsConfig options, and BASH in CodeExecutionCallArguments.Language).
  • Workflow callTool, multi-turn WorkflowAgent, and runtime args: You can now invoke parent agent tools directly inside workflows (callTool / callToolExpr), define persistent multi-turn subagents inside a workflow (defineAgent and callAgent), and pass a Map<String, Object> of runtime arguments to agent.beta().runWorkflow(...):
    AgentConfig config = AgentConfig.builder()
        .customInstructions("Enforce strict null-safety and immutable records.", "CodingStandards")
        .capabilities(CapabilitiesConfig.builder()
            .enableSubagents(true)
            .enableRunWorkflow(true)
            .build())
        .build();
    
    WorkflowDefinition workflow = Workflows.builder("pr_review_workflow")
        .description("Read target file and run multi-turn review")
        .phase("Read")
        .callToolExpr("file_content", "view_file", Map.of("Path", "args.get('target', 'README.md')"))
        .phase("Review")
        .defineAgent("reviewer", "You are a principal Java reviewer.", "Reviewer", "self", "inherit")
        .callAgent("initial_pass", "reviewer", "Summarize key risks in the file", String.class)
        .callAgent("final_verdict", "reviewer", "Provide final pass/fail verdict", String.class)
        .logExpr("initial_pass", "final_verdict")
        .build();
    
    try (Agent agent = new Agent(config)) {
        WorkflowResult result = agent.beta()
            .runWorkflow(
                workflow,
                Map.of("target", "pom.xml"),
                progress -> System.out.println("Progress: " + progress.output()))
            .get(120, TimeUnit.SECONDS);
        System.out.println("Workflow result:\n" + result.output());
    }
    
  • Titled custom system instructions: CustomSystemInstructions.of(text, title) and .customInstructions(text, title) wrap custom instructions in a named section header tag instead of a generic <System> block.
  • JSON Schema readOnly / writeOnly and utilities: Added readOnly and writeOnly support to @Param and SchemaGenerator, canonical path normalization on ToolCall.canonicalPath(), ~ home directory expansion on AgentInput.Media.fromFile(...), and DiffFormatter.formatDiffBlocks(...) for formatting edit_file diff blocks into unified diffs.

3. Human Review and Manual Release

By 9:50 AM, both workspaces had clean git diffs, passing test suites, and updated documentation. I did a final review of the commits, pushed them to main, and manually triggered the GitHub Actions release workflows (which use JReleaser to sign and publish the artifacts to Maven Central and create the GitHub releases).

Keeping a human gate at two points in the loop (approving the implementation plan before code generation, and manually triggering the Maven Central release after inspecting the diff) gives me the best balance between speed and control. What used to take several hours of tedious spec diffing and Protobuf reverse-engineering now takes about twenty minutes of reviewing plans and diffs on Monday morning.

Wrap-up

If you maintain an SDK, an API client, or an integration that tracks an external specification or upstream repository, pairing a cron-triggered Antigravity automation (sidecar.json) with a well-structured AGENTS.md runbook is a very practical workflow to set up. You can start small by typing /automation in Antigravity, pointing an agent at your upstream changelog or OpenAPI spec every Monday morning, and letting it hand you an implementation plan before you even finish your first cup of coffee.