<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Tanaike</title>
    <description>The latest articles on Forem by Tanaike (@tanaike).</description>
    <link>https://forem.com/tanaike</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3701430%2F3d517296-9b96-418b-ad29-24e735edd1df.png</url>
      <title>Forem: Tanaike</title>
      <link>https://forem.com/tanaike</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/tanaike"/>
    <language>en</language>
    <item>
      <title>Recursive Knowledge Crystallization: Enabling Persistent Evolution and Zero-Shot Transfer in AI Agents</title>
      <dc:creator>Tanaike</dc:creator>
      <pubDate>Thu, 02 Apr 2026 06:50:50 +0000</pubDate>
      <link>https://forem.com/gde/recursive-knowledge-crystallization-enabling-persistent-evolution-and-zero-shot-transfer-in-ai-4fh7</link>
      <guid>https://forem.com/gde/recursive-knowledge-crystallization-enabling-persistent-evolution-and-zero-shot-transfer-in-ai-4fh7</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flbx0q0fu8jaosvbqg0sq.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flbx0q0fu8jaosvbqg0sq.jpg" alt="fig1a" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Abstract
&lt;/h2&gt;

&lt;p&gt;This paper presents a self-evolving framework, &lt;strong&gt;Recursive Knowledge Crystallization (RKC)&lt;/strong&gt;, designed to overcome the "Catastrophic Forgetting" inherent in autonomous AI agents. By persisting evolved technical insights into a universally readable &lt;code&gt;SKILL.md&lt;/code&gt; file based on the &lt;a href="https://agentskills.io/home" rel="noopener noreferrer"&gt;Agent skills&lt;/a&gt; specification, this approach establishes long-term memory and cross-platform portability. The framework was empirically validated through the development of &lt;a href="https://github.com/brucemcpherson/gas-fakes" rel="noopener noreferrer"&gt;gas-fakes&lt;/a&gt;, a highly complex Node.js-to-Google Apps Script (GAS) emulation library. The results demonstrate that agents can autonomously internalize project-specific architectural patterns and environmental nuances. Consequently, the framework achieves &lt;strong&gt;Zero-Shot Knowledge Transfer&lt;/strong&gt; across distinct toolchains (Google Antigravity and the Gemini CLI) while maintaining absolute 1:1 behavioral parity with the live GAS environment.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Introduction
&lt;/h2&gt;

&lt;p&gt;In recent years, the automation of software development by autonomous AI agents powered by Large Language Models (LLMs) has advanced rapidly. However, a critical barrier to the practical deployment of such agents is the absence of a "Persistence of Learning." Constrained by LLM context windows and session fragmentation, agents frequently suffer from "Catastrophic Forgetting," losing vital insights acquired during previous interactions. While memory-augmentation frameworks like Reflexion (Shinn et al., 2023) and MemGPT (Packer et al., 2023) have been proposed, they heavily rely on in-memory contexts, dedicated vector databases, or internal virtual memory abstractions. As a result, the acquired knowledge remains locked within specific agent instances, rendering it difficult to port across environments and nearly impossible for human developers to directly audit or manage.&lt;/p&gt;

&lt;p&gt;While this limitation might remain latent in small-scale "greenfield" projects, it emerges as a fatal bottleneck when applying Generative AI to large, complex, and deeply constrained legacy projects. The&lt;a href="https://github.com/brucemcpherson/gas-fakes" rel="noopener noreferrer"&gt;gas-fakes&lt;/a&gt; project, the subject of this empirical study, encountered this exact limitation. This project serves as a foundational library for converting various Google APIs into mock classes and methods compatible with the Google Apps Script (GAS) environment using Node.js. As the codebase and conversion rules grew in complexity, traditional prompting and standard GenAI approaches frequently resulted in "rule violations"—instances where the AI bypassed strict, project-specific coding conventions to force task completion. Despite repeated trial-and-error iterations and manual context adjustments, the success rate remained insufficient for practical use.&lt;/p&gt;

&lt;p&gt;To fundamentally resolve this challenge, this paper introduces a self-evolving framework based on the &lt;a href="https://agentskills.io/home" rel="noopener noreferrer"&gt;Agent skills&lt;/a&gt; specification and its dynamic evolution model:&lt;a href="https://medium.com/google-cloud/recursive-knowledge-crystallization-a-framework-for-persistent-autonomous-agent-self-evolution-8243b3697471" rel="noopener noreferrer"&gt;Recursive Knowledge Crystallization (RKC)&lt;/a&gt;. The defining innovation of this framework is the &lt;strong&gt;"Physical Knowledge Persistence"&lt;/strong&gt; of the agent's operational guidelines and technical expertise. This knowledge is crystallized as a universally readable Markdown file (&lt;code&gt;SKILL.md&lt;/code&gt;) directly onto the local file system.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fef19nnblij8npvy0xknl.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fef19nnblij8npvy0xknl.jpg" alt="RKC Framework Architecture" width="800" height="436"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;By integrating Gemini 3 and 3.1 with next-generation toolchains—Google Antigravity and the Gemini CLI—we established the development process of &lt;code&gt;gas-fakes&lt;/code&gt; itself as the agent's continuous learning environment. Specifically, whenever an error occurred during the implementation of new classes or when human developers provided corrective feedback, the agent was issued a single meta-instruction: "Extract insights from the failure and recovery process, and autonomously update your Agent skill (&lt;code&gt;SKILL.md&lt;/code&gt;, sample scripts, and templates) by adding, deleting, or modifying content."&lt;/p&gt;

&lt;p&gt;Through this iterative trial-and-error cycle, the agent accomplished more than merely avoiding local errors; it decoded the implicit rules and complex constraints inherent in the Node.js-to-GAS conversion, eventually internalizing and formalizing the project’s overarching architectural patterns. This evolutionary process extended beyond the &lt;code&gt;SKILL.md&lt;/code&gt; text to include the refinement of helper scripts, templates, and technical specifications.&lt;/p&gt;

&lt;p&gt;As the RKC progressed and the Agent skill reached a state of convergence, the agent fully internalized the complex operational logic of &lt;code&gt;gas-fakes&lt;/code&gt;. This enabled accurate, rapid development strictly compliant with all project standards. Furthermore, by utilizing the standard file format, this method facilitates &lt;strong&gt;"Zero-Shot Knowledge Transfer"&lt;/strong&gt;: implicit knowledge acquired in one environment (e.g., Google Antigravity) can be seamlessly ported to an entirely clean environment (e.g., Gemini CLI) to generate flawless code on the first attempt. Simultaneously, persisting knowledge on a standard file system realizes a &lt;strong&gt;"True Human-AI Collaborative Paradigm,"&lt;/strong&gt; empowering human developers to fully visualize, audit, and guide the agent's cognitive evolution.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Workflow
&lt;/h2&gt;

&lt;p&gt;The development of &lt;code&gt;gas-fakes&lt;/code&gt; follows an iterative evolution of Agent skills, leveraging both Google Antigravity and the Gemini CLI. This process integrates a continuous feedback loop and culminates in Zero-Shot Knowledge Transfer through the physical persistence of evolved skills. The operational flow, visualized in &lt;strong&gt;Figure 2a&lt;/strong&gt;, is defined by the following seven stages:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Task Initiation&lt;/strong&gt;: A development task is assigned via a prompt to either Google Antigravity or the Gemini CLI, specifying the Google Apps Script (GAS) classes and methods to be emulated in the Node.js environment.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Autonomous Implementation&lt;/strong&gt;: Utilizing the current Agent skill (&lt;code&gt;gas-fakes-dev&lt;/code&gt;), the agent generates the corresponding Node.js classes and methods. Simultaneously, a test suite is authored by strictly adhering to the architectural patterns defined within the &lt;code&gt;SKILL.md&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Local Environment Validation&lt;/strong&gt;: The generated test scripts are executed in the local Node.js environment. If runtime errors or logic discrepancies occur, the AI autonomously refactors the implementation and the test code until local stability is achieved.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Cross-Platform Synchronization&lt;/strong&gt;: Following local success, the implementation is deployed to the Google-side script editor (GAS environment). This ensures that the emulation maintains 1:1 parity with the live GAS engine.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Remote Feedback &amp;amp; Refinement&lt;/strong&gt;: If the test suite encounters environment-specific errors in the GAS script editor (e.g., subtle differences in string output formatting or synchronous execution behavior), these errors are fed back to the AI for immediate remediation.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Knowledge Crystallization&lt;/strong&gt;: Once the implementation passes in both local and remote environments, the task is flagged as complete. At this juncture, the AI performs a self-audit to extract critical insights—such as previously undocumented GAS constraints or reusable design patterns—and autonomously updates the Agent skill (&lt;code&gt;SKILL.md&lt;/code&gt;, helper scripts, and templates).&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Recursive Evolution Loop&lt;/strong&gt;: Stages 1 through 6 are continuously cycled. As the &lt;code&gt;SKILL.md&lt;/code&gt; matures, it transitions from a set of generalized instructions to a highly specialized, project-aware expert system, enabling the agent to resolve increasingly complex architectural challenges with minimal human intervention.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5pfmjjkl0lqycuyq88uq.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5pfmjjkl0lqycuyq88uq.jpg" alt="Workflow" width="800" height="436"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Fig 2a: The 7-stage operational workflow of Recursive Knowledge Crystallization.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Methodology: Environment Setup and Initialization
&lt;/h2&gt;

&lt;p&gt;In this article, it is assumed that Node.js, Google Antigravity, and Gemini CLI have already been installed. To establish the baseline for developing &lt;code&gt;gas-fakes&lt;/code&gt;, the repository is cloned as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/brucemcpherson/gas-fakes
&lt;span class="nb"&gt;cd &lt;/span&gt;gas-fakes
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this empirical study, Google Antigravity and Gemini CLI were utilized in tandem to evolve the agent skill. First, the initial agent skill was established. The directory structure is outlined below. To ensure both agent environments utilized the exact same skill base (&lt;code&gt;gas-fakes-dev&lt;/code&gt;), they were synchronized using a symbolic link.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;gas-fakes/
├── .agent/
│   └── skills/
│       └── gas-fakes-dev/
│           └── SKILL.md
└── .gemini/
    └── skills -&amp;gt; ../.agent/skills/  (symbolic link)
        └── gas-fakes-dev/
            └── SKILL.md
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Alternatively, to avoid manual linking between the &lt;code&gt;.agents&lt;/code&gt; and &lt;code&gt;.gemini&lt;/code&gt; directories, the &lt;code&gt;gemini.md&lt;/code&gt; configuration file can be utilized to instruct the Gemini CLI to consistently reference the &lt;code&gt;.agents/workloads&lt;/code&gt; and &lt;code&gt;.agents/skills&lt;/code&gt; folders. This ensures seamless resource sharing between the CLI and Google Antigravity. Furthermore, including a directive to automatically activate the &lt;code&gt;gas-fakes-dev&lt;/code&gt; skill within this configuration streamlines the entire initialization process.&lt;/p&gt;

&lt;p&gt;The baseline &lt;code&gt;SKILL.md&lt;/code&gt; was formulated as a generic directive. During the development of &lt;code&gt;gas-fakes&lt;/code&gt;, this specific skill file served as the target for continuous evolution.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;---
name: gas-fakes-dev
description: "Develop and implement the 'gas-fakes' project, emulating Google Apps Script (GAS) functionality using Node.js."
tags:[nodejs, google-apps-script, google-cloud-api, testing, mock]
version: "1.0.0"
---

## Summary
This skill enables the agent to assist in the development of the `gas-fakes` project. The primary objective is to emulate Google Apps Script (GAS) behavior using Node.js and Google APIs, allowing Apps Script code to run in a local Node.js environment.

## Usage
- When you need to implement fake/mock functionality for GAS classes or methods.
- When creating test scripts to verify the implemented functionalities.
- When assisting a human developer with Node.js and Google APIs integration.

## Workflow

### 1. Context and Specification Check
Before implementing, verify the specifications of the target Google Apps Script classes and methods. Ensure that the functionality you are about to build aligns with real GAS behavior.

### 2. Implementation
Generate Node.js code that maps GAS methods to the corresponding Google Cloud APIs.
- Refer to the existing codebase in the `src/` directory to understand the current architecture.
- Ensure the code runs correctly in the Node.js environment.

### 3. Testing
You must verify your implementation by writing and executing test scripts.
- Place your test scripts in the `test/` directory.
- Execute the tests to ensure there are no errors and the behavior matches expectations.

### 4. Holistic/Targeted Skill Evolution (Self-Updating SKILL)
**[CRITICAL INSTRUCTION]**
The `gas-fakes` project is complex, and bridging Node.js with GAS involves many hidden constraints, specific architectural patterns, and potential errors. You are required to continuously learn and autonomously evolve this SKILL.

- **Trigger:** Whenever you encounter an error during implementation or testing, or when you receive correction feedback/prompts from the human developer.
- **Action:** You MUST extract the lessons learned from the failure and recovery process. Identify the underlying rules, technical constraints, or coding patterns that caused the issue.
- **Update:** Immediately update this `SKILL.md` file (by adding, deleting, or modifying content) to document the newly acquired knowledge. If necessary, also create or update sample scripts, helper templates, or explanatory Markdown files in the project.
- **Goal:** Transform your localized, temporary learnings into permanent, universally readable knowledge to prevent repeating the same mistakes and to handle the complexities of the project autonomously.

## Delivery
- Output the complete code for modified or newly created service classes and test scripts.
- **ALWAYS output the updated `SKILL.md`** when new knowledge is extracted and crystallized.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Also, please install Model Context Protocol (MCP) servers for Google Workspace development. &lt;a href="https://developers.google.com/workspace/guides/developer-tools#mcp" rel="noopener noreferrer"&gt;Ref&lt;/a&gt; This MCP server is used from this agent skill.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;When a command &lt;code&gt;/gas-fakes-dev&lt;/code&gt; is put into the chat, this skill can be activated.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Because &lt;code&gt;gas-fakes&lt;/code&gt; emulates Google Apps Script using Node.js, generated test scripts are strictly required to function flawlessly in both the local Node.js environment and the cloud-based Google Apps Script editor. Utilizing the initial, generic skill often resulted in scripts that passed locally but failed in the cloud environment. This discrepancy served as the primary catalyst for triggering the agent's evolutionary loop.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Crystallization of Knowledge: The Evolved Agent Skill
&lt;/h2&gt;

&lt;p&gt;After extensive trial and error implementing various GAS classes and methods, the agent dynamically restructured and expanded its own skill set.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F666xatrumohah5vj9h9y.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F666xatrumohah5vj9h9y.jpg" alt="Agent Skill Evolution Comparison" width="800" height="436"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The evolved repository structure incorporated new templates, examples, and scripts generated by the agent to support its new architectural insights. You can see the details of this agent skill from &lt;a href="https://github.com/tanaikech/agent-skill-for-developing-gas-fakes" rel="noopener noreferrer"&gt;https://github.com/tanaikech/agent-skill-for-developing-gas-fakes&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;gas-fakes/
├── .agent/
│   └── skills/
│       └── gas-fakes-dev/
│           ├── examples/
│           │   ├── batch_update_pattern.js
│           │   └── proxy_guard_pattern.js
│           ├── resources/
│           │   ├── class_template.js
│           │   ├── sync_mechanism.md
│           │   └── test_template.js
│           ├── scripts/
│           │   ├── run_target_test.js
│           │   └── scaffold_service.js
│           └── SKILL.md
└── .gemini/
    └── skills -&amp;gt; ../.agent/skills/  (symbolic link)
        └── gas-fakes-dev/
            ├── examples/
            ├── resources/
            ├── scripts/
            └── SKILL.md
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The resultant, highly complex &lt;code&gt;SKILL.md&lt;/code&gt; (Version 2.0.0) evolved into a comprehensive system architecture document as follows. &lt;a href="https://github.com/tanaikech/agent-skill-for-developing-gas-fakes/blob/master/gas-fakes-dev/SKILL.md" rel="noopener noreferrer"&gt;Ref&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;---
name: gas-fakes-dev
description: Develop, implement, and test the 'gas-fakes' project, emulating ALL Google Apps Script (GAS) functionality using Node.js and Google APIs.
tags: [nodejs, google-apps-script, google-cloud-api, testing, mock, simulation]
version: "2.0.0"
---

## Summary
This skill enables the agent to act as a Senior Expert proficient in Node.js, Google Cloud, and Shell Scripting to assist in the development of the `gas-fakes` project.
The ultimate objective is to **emulate the behavior of live Google Apps Script exactly** by mapping classes and methods to their fake equivalents. This allows Apps Script to run anywhere Node runs.
This is achieved using a **worker mechanism** to handle the conversion from synchronous (Apps Script) to asynchronous (Google APIs) operations, returning the results synchronously. Ultimately, all Apps Script classes and methods will be available via `gas-fakes`. For a deep dive into this mechanism, see the [Sync-to-Async Bridge Guide](./resources/sync_mechanism.md).

## Usage
- When you need to implement or modify mock functionality for specific GAS classes or methods.
- When creating logic that reproduces GAS behavior using Google APIs and the worker mechanism.
- When adding new service classes to fit the existing `gas-fakes` project structure.
- When creating test scripts and registering them for local execution within the `test/` directory.
- When executing generated Apps Script files locally.
- When technical advice or debugging assistance based on GAS specifications is required.

## Configuration
`gas-fakes` relies on an `.env` file (maintained by `gas-fakes init` and `auth`) to properly authenticate and run.

### Authentication Methods
- **DWD (Domain Wide Delegation) - *Preferred***: Uses a service account to impersonate a user logged into gcloud. The service account name must be specified in the `.env` file. This is **required** if restricted/sensitive scopes are needed, or if running `gas-fakes` on Google Cloud Run.
- **ADC (Application Default Credentials)**: If no `.env` file is provided, `gas-fakes` falls back to ADC. This is usually fine for local development as standard `cloud_platform` and `drive` scopes are automatically assigned during auth.

Ensure your `.env` is properly loaded (e.g., using `node --env-file &amp;lt;env-file&amp;gt;` or `gas-fakes -e &amp;lt;env-file&amp;gt;`) to ensure DWD is used when necessary.
- **`TEST_SPREADSHEET_ID`**: (Optional) Spreadsheet ID for real-environment testing.

## Workflow

### 1. Context, Specification, and Dependency Check
Before starting implementation, you must understand the full scope and style of the project.

- **Restricted Directories**:
  * **NEVER modify any files located in the `progress` directory** during the development of `gas-fakes`. This directory is strictly off-limits.

- **Mandatory Existence Verification**:
  When adding or updating ANY class or method, you must **ALWAYS use the `workspace-developer` MCP server** (if available) or search official documentation to verify the existence and specification of the target and ALL related classes/methods.
  *   **NEVER create classes or methods that do not exist in the actual Google Apps Script environment.**
  *   Do not guess. Confirm exact names, parameter structures, and return types.
  *   *Example*: If implementing `Sheet.getCharts()`, check what object it returns (`EmbeddedChart` vs `Chart`) and verify the methods available on that returned object.

- **Strict Enum Verification**:
  **Do not invent Enums.** Before using or creating an Enum, verify if it actually exists in Google Apps Script.
  *   **Note on `ChartType`**: In Google Apps Script, `ChartType` is a property of the `Charts` service (`Charts.ChartType`), not `SpreadsheetApp`.

- **Reference Existing Code**:
  Check the `src` and `test` directories to understand the existing coding style and architecture. Apply these patterns to new classes and methods.

- **Identify Dependencies**:
  Identify and implement **all related classes and methods** required for the target feature. (e.g., if `newChart()` returns `EmbeddedChartBuilder`, verify/implement that builder class too).

### 2. Scaffolding
Do not create service files manually. Use the provided helper script.

- **Command**:
  \`\`\`bash
  node .agent/skills/gas-fakes-dev/scripts/scaffold_service.js --service=&amp;lt;ServiceName&amp;gt; --class=&amp;lt;ClassName&amp;gt;
  \`\`\`

### 3. Implementation
Generate code based on Node.js best practices, adhering to these rules to maintain environmental consistency with live Apps Script:

- **Comprehensive Implementation**: Implement the target feature and its dependencies together.
- **Cross-Environment Compatibility**: Scripts must be designed to run on both Node.js (local) and GAS Script Editor with identical results.
- `toString()` Accuracy: The `toString()` method must return the **exact GAS class name or specific string output** (e.g., `"Sheet"` or `"[Document:  ...]"`). Pay extreme attention to whitespace; GAS output can contain double spaces (e.g., after a colon).
- **Dynamic Resources and Caching Pattern**:
  *   **The `__resource` property**: Always access the underlying API state via the dynamic `__resource` getter (tracing back to the parent element or a synchronized cache).
  *   **Stale State Prevention**: NEVER store API resources directly in class instance variables. They will become stale when the cache is cleared.
  *   **Cache Invalidation**: Every time a destructive API call is made, the cache is cleared. Dynamically accessing `__resource` ensures you get the most up-to-date state from the API or the fresh cache.
- **Implementation Patterns**:
  1. Use `signatureArgs` or `is.*` utilities.
  2. Construct the request object.
  3. Execute via `this.__batchUpdate` (or helper).
  4. Return `this` for chaining.

- **Coding Patterns**:
  *   **Naming**: Internal implementation classes should be prefixed with `Fake` (e.g., `FakeSheet`, `FakeEmbeddedChart`).
  *   **Lazy Loading**: New top-level services must be registered using the `lazyLoaderApp` pattern in their respective `app.js` to ensure they are only instantiated when accessed.
  *   **Singleton Pattern**: Service entry points usually export a "maker" function (e.g., `newFakeSpreadsheetApp`) that return the singleton instance.

- **Specific Technical Nuances**:
  *   **Sync-to-Async Bridge**: Always use the worker bridge for external API calls to maintain synchronous Apps Script behavior. Follow the pattern: `Fake Class` -&amp;gt; `Syncit (fx*)` -&amp;gt; `callSync` -&amp;gt; `Worker Loop` -&amp;gt; `sx* Function` -&amp;gt; `sxRetry`. See [detailed guide](./resources/sync_mechanism.md) for implementation steps.
  *   **Charts**: When retrieving a chart's title, use `getOptions().get("title")` instead of internal specs.
  *   **EmbeddedChart**: To get the type of an existing chart, use `modify().getChartType()` instead of a non-existent `getType()` method.
  *   **GridRange to FakeRange Conversion**: When mapping an API `GridRange` to a `FakeSheetRange`:
    *   Indices in the API are 0-indexed and the end index is **exclusive**.
    *   GAS methods (like `getRange(row, col, numRows, numCols)`) use 1-indexed start positions and row/column counts.
    *   If `endRowIndex` or `endColumnIndex` is undefined in the API response, it means the range extends to the boundary of the sheet. Use `sheet.getMaxRows()` and `sheet.getMaxColumns()` as fallbacks to calculate `numRows` and `numCols`.
  *   **ContainerInfo**: This class is used by `EmbeddedChart`, `Slicer`, and `Drawing`.
    *   Map `getAnchorRow()` and `getAnchorColumn()` to 0-indexed API fields `anchorCell.rowIndex + 1` and `anchorCell.columnIndex + 1`.
    *   Map `getOffsetX()` and `getOffsetY()` directly to `offsetXPixels` and `offsetYPixels`.
  *   **XmlService**:
    *   `Element.getName()` returns the **local name** (no prefix).
    *   `Document.toString()` output is very specific: `[Document:  No DOCTYPE declaration, Root is [Element: &amp;lt;rootName/&amp;gt;]]` (note the double space after `Document:`).
    *   Namespace-aware methods (like `getChild(name, namespace)`) require mapping prefixes and URIs correctly.

### 4. Testing
You must ensure every implementation is verified by tests that precisely emulate Google Apps Script behavior.

- **Mandatory Feature Coverage**:
  - **Every new class or method MUST have a corresponding test.**
  - If you implement multiple methods, create a test script that exercises all of them.
  - Test only implemented features; do not include placeholders for future work.

- **Edge Case Capturing**:
  - **Capture and verify boundary conditions, invalid inputs, and error states.**
  - Use `t.rxMatch(t.threw(() =&amp;gt; ...).message, /regex/)` to verify that methods throw expected GAS error messages when given invalid arguments.
  - Test with `null`, `undefined`, empty strings, and out-of-bounds values where applicable to ensure robust emulation.

- **GAS Compatibility Requirement**:
  - Test logic (inside `unit.section`) must be compatible with the **Google Apps Script script editor**.
  - Avoid Node.js-specific modules (e.g., `fs`, `path`) inside the test logic.
  - Use `ScriptApp.isFake` or `xxxApp.isFake` to toggle environment-specific logging or assertions.

- **Test Script Structure**:
  - **Imports**: standard test scripts import `@mcpher/gas-fakes`, `@sindresorhus/is`, and helpers from `./testinit.js` and `./testassist.js`.
  - **Export Pattern**: Export a named function (e.g., `export const testService = (pack) =&amp;gt; { ... }`) to allow integration into the main test suite.
  - **Execution Hook**: Always include `wrapupTest(testService);` at the end of the file for standalone execution.
  - **Sections**: Use `unit.section("description", (t) =&amp;gt; { ... })` to group tests.

- **Resource Lifecycle and Cleanup**:
  - **The `toTrash` pattern**: Maintain a `toTrash` array within your test function. Push any created resources (files, folders, sheets) into this array.
  - **Automatic Cleanup**: Use `trasher(toTrash)` at the end of the test function (usually triggered if `fixes.CLEAN` is true) to ensure the test environment remains pristine.

- **Naming Convention**:
  - **Google Sheets**: `testsheets{class name}.js` (e.g., `testsheetsrange.js`)
  - **Google Docs**: `testdocs{class name}.js`
  - **Google Slides**: `testslides{class name}.js`
  - **General/Other**: `test{service}{class name}.js`

- **Registration**:
  1. Create the file in `test/`.
  2. Add to `test/test.js`.
  3. Add a script entry in `test/package.json`.

- **Execution**:
  &amp;gt; [!IMPORTANT]
  &amp;gt; **Run tests from the `test/` directory.**
  &amp;gt; `cd test &amp;amp;&amp;amp; node test{filename}.js execute` (or `npm run &amp;lt;script-name&amp;gt;`).

- **Clasp Verification**:
  Use `testongas/test/` to verify against a real GAS project via `clasp`.

### 5. Executing Generated Scripts
You can execute any generated Apps Script file using `gas-fakes` in a local sandbox.

- **Testing the Local Branch (Preferred for Dev)**:
  Use the local `.env` file and run the local `gas-fakes` implementation.
  \`\`\`bash
  node --env-file ./.env gas-fakes -f myscript.js
  \`\`\`
- **Testing Global Installation**:
  \`\`\`bash
  npx gas-fakes -f myscript.js
  \`\`\`
  *(Sandbox flags can be added as requested by the user to control the environment)*

### 6. Refinement &amp;amp; Continuous Evolution (Self-Updating SKILL)
To ensure the `gas-fakes` project and this SKILL continuously evolve and improve efficiently, you **MUST** actively refine and update the SKILL definition (`SKILL.md`) and its associated resources based on the outcomes of your development processes.

#### 6.1 Skill Audit Criteria
At the conclusion of every task, perform a "Self-Audit" by answering:
1. **New Pattern?** Did I implement a new mapping logic (e.g., API-to-GAS index shifts) or a reusable architectural pattern?
2. **Missing Nuance?** Did I encounter a technical detail (like a specific `toString()` format or Enum location) that wasn't documented?
3. **Corrected Assumption?** Did a test failure or user hint reveal that a rule in this SKILL was incomplete or incorrect?
4. **New Service?** Did I add a new class that requires a dedicated section in the "Technical Nuances" list?

#### 6.2 Prompt for Update Mandate
If any criteria in the "Skill Audit" are met, you MUST:
- **Analyze the New Knowledge**: Determine what core knowledge, rule, constraint, or pattern was derived from the success or resolution.
- **Identify the Change**: Specifically state what needs to be added, modified, or deleted in `SKILL.md`.
- **Propose the Edit**: Present the specific Markdown block intended for the update.
- **Ask for Confirmation**: Explicitly ask the user: *"Based on this task, I recommend updating the SKILL.md with the following [nuance/pattern]. Should I apply this change?"*
- **Update Associated Assets**: If the new knowledge involves a reusable code structure or boilerplate, add or update files in the `examples/`, `resources/`, or `scripts/` directories to reflect the new best practice.

### 7. Delivery
- **Output**: Full content of modified or newly created files (Service class, Node.js test, etc.).
- **Skill Audit**: Provide a brief (1-2 sentence) summary of your Self-Audit results.
- **Update Prompt**: If any audit criteria were met, issue the "Prompt for Update" as defined in section 6.2.
- **Finality**: Conclude with a concise summary of the task results.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Key Milestones in the Agent's Evolution:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Role and Expertise Reclassification&lt;/strong&gt;: The agent's persona autonomously shifted from a general "assistant" to a "Senior Expert" with specific cross-domain mastery in Node.js, Google Cloud, and Shell Scripting.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Architectural Specification (Sync-to-Async Bridge)&lt;/strong&gt;: The evolved skill introduced a sophisticated "worker mechanism" and "Syncit" pattern. This addressed the core technical challenge of mapping asynchronous Node.js/Google APIs to the synchronous execution model of Google Apps Script.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Strict Verification &amp;amp; Grounding&lt;/strong&gt;: New mandates required the use of the &lt;code&gt;workspace-developer&lt;/code&gt; MCP server and official documentation. This systematically eliminated "hallucinated" methods, ensuring absolute 1:1 parity with the live GAS environment.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Technical Nuance Crystallization&lt;/strong&gt;: Highly specific, undocumented "gotchas" discovered during the failure/recovery loops were codified. This included 0-indexed vs. 1-indexed coordinate conversions, exact string matching for &lt;code&gt;toString()&lt;/code&gt; (including hidden whitespace), and specific Enum locations.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Self-Evolution Logic (Crystallization Loop)&lt;/strong&gt;: The trigger for skill updates shifted from a reactive "whenever an error occurs" (v1) to a proactive, structured "Self-Audit" protocol (v2). The agent learned to analyze patterns, propose edits to the human developer, and automatically update associated boilerplate assets.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  5. Empirical Evaluation: Cross-Environment Knowledge Transfer
&lt;/h2&gt;

&lt;p&gt;To validate the efficacy of the RKC framework, practical development tasks were executed utilizing the actively evolving agent skill across two distinct interfaces: Google Antigravity and the Gemini CLI.&lt;/p&gt;

&lt;h3&gt;
  
  
  5.1 Evaluation in Google Antigravity
&lt;/h3&gt;

&lt;h3&gt;
  
  
  Sample 1: Architectural Refinement via Self-Audit
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Prompt1:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Add the method `insertTextBox(String,Number,Number,Number,Number)` of Class Slide. This method should be included in the file `src/services/slidesapp/fakeslide.js`.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Prompt2:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Add the method `insertTable(Table)` of Class Slide. This method should be included in the file `src/services/slidesapp/fakeslide.js`.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6zq0e54zefb67j402oio.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6zq0e54zefb67j402oio.jpg" alt="Result on the agent manager" width="800" height="1962"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Fig. 3a: Agent executing implementation and autonomously applying the Dynamic Resources Pattern.&lt;/p&gt;

&lt;p&gt;To evaluate continuous learning, we tasked the agent with implementing &lt;code&gt;insertTextBox&lt;/code&gt; and &lt;code&gt;insertTable&lt;/code&gt;. The task required creating underlying architectural dependencies, such as &lt;code&gt;FakeTable&lt;/code&gt;, &lt;code&gt;FakeTableRow&lt;/code&gt;, and &lt;code&gt;FakeTableCell&lt;/code&gt;. Initially, the agent generated functional code. However, as shown in &lt;strong&gt;Figure 3a&lt;/strong&gt;, the true strength of the framework was demonstrated during the self-evolution phase. The agent autonomously identified a potential flaw regarding resource management. It proactively refactored the classes to adhere to a newly conceptualized "Dynamic Resources Pattern," replacing static constructor assignments with dynamic getters to ensure robust state synchronization. Crucially, the agent updated &lt;code&gt;SKILL.md&lt;/code&gt; with this rule, ensuring future compliance. Following this refinement, all 23 test cases passed with 100% accuracy.&lt;/p&gt;

&lt;h4&gt;
  
  
  Sample 2: Internalization of Design Philosophy
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;Prompt:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Add the method `getTables()` of Class Slide. This method should be included in the file `src/services/slidesapp/fakeslide.js`.
Add the method `getShapes()` of Class Slide. This method should be included in the file `src/services/slidesapp/fakeslide.js`.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3njy8kyujb1bgq4lxckf.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3njy8kyujb1bgq4lxckf.jpg" alt="Result on the agent manager" width="800" height="830"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Fig. 3b: Agent applying functional programming patterns to dynamically cast page elements.&lt;/p&gt;

&lt;p&gt;In this experiment (see &lt;strong&gt;Figure 3b&lt;/strong&gt;), the agent demonstrated a deep internalization of the project's design philosophy. Instead of implementing redundant storage logic for arrays of specific shapes, it autonomously utilized the generic &lt;code&gt;getPageElements()&lt;/code&gt; method, applying &lt;code&gt;.filter()&lt;/code&gt; and &lt;code&gt;.map()&lt;/code&gt; to dynamically cast elements. This approach perfectly adhered to the "Dynamic Resources Pattern" crystallized in the previous task. All 28 assertions in the subsequent test suite passed flawlessly.&lt;/p&gt;

&lt;h4&gt;
  
  
  Sample 3: Bridging Environmental Discrepancies
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;Prompt 1 &amp;amp; 2 (Summarized):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Add a Class XmlService and a method `parse`...
When `test/testxmlservice.js` is run with the script editor of Google Apps Script, the error occurred. Update the scripts.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fswvyi9fvkh3pgv9h4dxl.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fswvyi9fvkh3pgv9h4dxl.jpg" alt="Result on the agent manager" width="800" height="1486"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Fig. 3c: Agent resolving GAS-specific formatting rules through remote feedback.&lt;/p&gt;

&lt;p&gt;This task required bridging the gap between Node.js simulation and actual GAS behavior for XML parsing. While the initial code passed local Node.js tests, execution in the GAS editor revealed critical discrepancies, such as unique double-space formatting in &lt;code&gt;toString()&lt;/code&gt; outputs (&lt;code&gt;[Document:  No DOCTYPE...]&lt;/code&gt;) and precise namespace handling. Upon receiving this remote error feedback (see &lt;strong&gt;Figure 3c&lt;/strong&gt;), the agent performed a systemic refactoring. Following the RKC framework, it updated the &lt;code&gt;SKILL.md&lt;/code&gt;, formally persisting these "hidden" GAS-specific formatting rules as permanent constraints.&lt;/p&gt;

&lt;h4&gt;
  
  
  Sample 4: Achieving Environment-Aware Precision
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;Prompts 1, 2 &amp;amp; 3 (Summarized):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Add the `getPrettyFormat` and `getRawFormat` methods to the `XmlService` class.
Resolve issues regarding \r\n line breaks appearing in raw format tests on GAS...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1c2qap6klmzkp3vytmqd.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1c2qap6klmzkp3vytmqd.jpg" alt="Result on the agent manager" width="800" height="1441"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Fig. 3d: Agent crystallizing nuanced serialization behaviors into persistent memory.&lt;/p&gt;

&lt;p&gt;This experiment focused on achieving high-fidelity parity with GAS’s unique XML serialization. Through iterative feedback, the agent discovered that GAS consistently injects a line separator (&lt;code&gt;\r\n&lt;/code&gt;) after the XML declaration and at the document's end, even in "raw" format. As captured in &lt;strong&gt;Figure 3d&lt;/strong&gt;, the agent refactored the &lt;code&gt;FakeFormat&lt;/code&gt; class and crystallized these insights into &lt;code&gt;SKILL.md&lt;/code&gt;. Final verification yielded a 100% success rate across 21 test cases, proving the framework's capacity for "Environment-Aware Precision."&lt;/p&gt;

&lt;h3&gt;
  
  
  5.2 Zero-Shot Knowledge Transfer via Gemini CLI
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Sample 1: Cross-Platform Environment Porting
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;Prompt:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Implement the following methods for the `ContainerInfo` class within the `EmbeddedChart` class hierarchy of Google Sheets: `getAnchorColumn()`, `getAnchorRow()`, `getOffsetX()`, and `getOffsetY()`.

Please follow these operational guidelines:
1. **Skill Declaration**: Explicitly state the agent skills or tools being utilized before proceeding.
2. **Task Execution &amp;amp; Summary**: Provide the implemented code/definitions and conclude with a concise summary of the task results.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Ftanaikech.github.io%2Fimage-storage%2F20260402a%2Ffig3e.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Ftanaikech.github.io%2Fimage-storage%2F20260402a%2Ffig3e.jpg" alt="Result on the Gemini CLI" width="800" height="4067"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Fig. 3e: Gemini CLI agent achieving Zero-Shot Knowledge Transfer utilizing the shared SKILL.md.&lt;/p&gt;

&lt;p&gt;This experiment validated the "Zero-Shot Knowledge Transfer" capability by transitioning the execution environment entirely from Google Antigravity to the Gemini CLI. By referencing the highly evolved &lt;code&gt;SKILL.md&lt;/code&gt; persisted on the local file system, the CLI-based agent immediately understood the complex architectural patterns (see &lt;strong&gt;Figure 3e&lt;/strong&gt;). It autonomously implemented &lt;code&gt;FakeContainerInfo&lt;/code&gt; using the project-specific &lt;code&gt;Proxies.guard&lt;/code&gt; pattern. The generated code strictly followed naming conventions and correctly converted 0-based API indices to 1-based GAS indices on its very first attempt, passing all 11 tests with 100% accuracy.&lt;/p&gt;

&lt;h4&gt;
  
  
  Sample 2: Agents as Architectural Contributors
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;Prompt:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Implement the `getRanges()` method for the `EmbeddedChart` class in the Google Sheets service.

Please follow these operational guidelines:
1. **Skill Declaration**: Explicitly state the agent skills or tools being utilized before proceeding.
2. **Task Execution &amp;amp; Summary**: Provide the implemented code/definitions and conclude with a concise summary of the task results.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Ftanaikech.github.io%2Fimage-storage%2F20260402a%2Ffig3f.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Ftanaikech.github.io%2Fimage-storage%2F20260402a%2Ffig3f.jpg" alt="Result on the Gemini CLI" width="800" height="2317"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Fig. 3f: Gemini CLI agent performing a Self-Audit and proposing architectural updates.&lt;/p&gt;

&lt;p&gt;Focusing on the &lt;code&gt;getRanges()&lt;/code&gt; method, the CLI agent transformed nested API &lt;code&gt;GridRange&lt;/code&gt; structures into GAS &lt;code&gt;Range&lt;/code&gt; instances. It elegantly handled discrepancies between exclusive 0-based API bounds and inclusive 1-based GAS boundaries. Most significantly, as shown in &lt;strong&gt;Figure 3f&lt;/strong&gt;, the agent autonomously triggered its "Skill Audit" protocol upon completion. It identified a recurring logic pattern for &lt;code&gt;GridRange&lt;/code&gt; conversions and proactively recommended a structural update to the &lt;code&gt;SKILL.md&lt;/code&gt;. This illustrates that the RKC framework enables agents to transcend basic execution, allowing them to act as proactive "Architectural Contributors."&lt;/p&gt;

&lt;h2&gt;
  
  
  6. Conclusion
&lt;/h2&gt;

&lt;p&gt;Through the empirical development of the &lt;code&gt;gas-fakes&lt;/code&gt; emulation library, this research validates the profound efficacy of the Recursive Knowledge Crystallization (RKC) framework. Key findings include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Physical Knowledge Persistence&lt;/strong&gt;: By crystallizing an agent's evolving technical expertise into universally readable, local Markdown files (&lt;code&gt;SKILL.md&lt;/code&gt;), the framework successfully mitigates "Catastrophic Forgetting" and establishes an auditable, persistent memory layer.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Recursive Evolution Loop&lt;/strong&gt;: The implementation of an autonomous "Self-Audit" protocol enabled the agent to systematically extract architectural insights from failure/recovery cycles, thereby refining its own operational directives.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Environment-Aware Precision&lt;/strong&gt;: The agent successfully codified highly obscure, undocumented system constraints (e.g., hidden string formatting and complex coordinate mapping logic) directly into its skill set, guaranteeing high-fidelity behavioral emulation.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Zero-Shot Knowledge Transfer&lt;/strong&gt;: The study empirically proved that evolved, highly specialized skills can be seamlessly and instantaneously ported across entirely distinct environments (from Google Antigravity to Gemini CLI), enabling the generation of structurally perfect code on the first attempt.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Human-AI Collaboration&lt;/strong&gt;: The framework realizes a novel development paradigm where the AI agent operates not merely as a localized code generator, but as an active, persistent "Architectural Contributor" capable of scaling and maintaining deeply constrained codebases.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Note
&lt;/h2&gt;

&lt;p&gt;It should be noted that the "Agent skill" development methodology and the self-evolution process introduced in this paper do not necessarily represent a state where evolution has reached full saturation. As AI technology and its surrounding ecosystems continue to advance, there remains significant untapped potential in the structures through which agents define themselves and "crystallize" their knowledge.&lt;/p&gt;

&lt;p&gt;By further pushing the boundaries of this development framework, we anticipate that even greater evolutionary leaps will occur. Such progress will likely lead to even higher levels of development efficiency, transcending current expectations and moving toward a more sophisticated phase of software automation and human-agent synergy.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>gemini</category>
      <category>antigravity</category>
      <category>agentskills</category>
    </item>
    <item>
      <title>Mastering Google Apps Script CI/CD: Seamless GitHub Actions Integration with gas-fakes</title>
      <dc:creator>Tanaike</dc:creator>
      <pubDate>Wed, 25 Mar 2026 07:20:29 +0000</pubDate>
      <link>https://forem.com/gde/mastering-google-apps-script-cicd-seamless-github-actions-integration-with-gas-fakes-4c6h</link>
      <guid>https://forem.com/gde/mastering-google-apps-script-cicd-seamless-github-actions-integration-with-gas-fakes-4c6h</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqzl38sbb8u84h7ogj6lc.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqzl38sbb8u84h7ogj6lc.jpg" alt="fig1a" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Abstract
&lt;/h2&gt;

&lt;p&gt;Discover how to seamlessly integrate Google Workspace with GitHub Actions using the gas-fakes library. This guide demonstrates running Google Apps Script locally and within CI/CD pipelines without deploying Web Apps. Automate workflows, secure credentials, and effortlessly interact with Google Drive and Sheets directly from your repository.&lt;/p&gt;

&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Google Apps Script (GAS) is a powerful low-code platform that enables developers to integrate, automate, and extend Google Workspace with ease. &lt;a href="https://workspace.google.com/products/apps-script/" rel="noopener noreferrer"&gt;Ref&lt;/a&gt; Typically, executing GAS requires the script to be hosted on Google's servers via the Script Editor. While tools like &lt;code&gt;clasp&lt;/code&gt; allow for local development and synchronization, running scripts from outside the Google ecosystem—such as from a local environment or a different cloud provider—often involves complex setups relying heavily on the Apps Script API or Web Apps. &lt;a href="https://github.com/google/clasp" rel="noopener noreferrer"&gt;Ref&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A common approach to triggering GAS from GitHub Actions is using Web Apps. However, this method presents several hurdles. Developers must re-deploy the Web App every time the script is updated, and verifying the latest code logic directly within the repository can be cumbersome. This is where &lt;code&gt;gas-fakes&lt;/code&gt; becomes a game-changer. &lt;a href="https://github.com/brucemcpherson/gas-fakes" rel="noopener noreferrer"&gt;Ref&lt;/a&gt; Operating as a robust emulation layer, &lt;code&gt;gas-fakes&lt;/code&gt; allows GAS projects to run on Node.js as if they were native, enabling seamless execution across various environments.&lt;/p&gt;

&lt;p&gt;By leveraging &lt;code&gt;gas-fakes&lt;/code&gt; within GitHub Actions, you can manage, test, and update your scripts directly inside your repository without the need for constant re-deployment. Furthermore, by storing sensitive credentials in GitHub's "Secrets and variables," you can ensure a high level of security for your automation workflows. This synergy between GitHub Actions and GAS opens up infinite possibilities for CI/CD application development. In this article, I will introduce a streamlined, professional method for managing Google Workspace using GitHub Actions and &lt;code&gt;gas-fakes&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd7q371dzq9m5u49sp9d0.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd7q371dzq9m5u49sp9d0.jpg" alt="fig1b" width="800" height="436"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Phase 1: Setup and Authentication
&lt;/h2&gt;

&lt;p&gt;To establish this pipeline, we first need to extract the proper authorization credentials and configure our GitHub repository.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1: Obtain Authorization Data
&lt;/h3&gt;

&lt;p&gt;In order to authorize &lt;code&gt;gas-fakes&lt;/code&gt;, you must first generate Application Default Credentials (ADC) on your local machine. The Google Cloud CLI (&lt;code&gt;gcloud&lt;/code&gt;) is required for this step. Detailed installation instructions can be found in the &lt;a href="https://github.com/brucemcpherson/gas-fakes/blob/main/gas-fakes-cli.md#getting-started" rel="noopener noreferrer"&gt;gas-fakes official documentation&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you prefer using &lt;code&gt;npx&lt;/code&gt;, execute the following commands in your terminal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx @mcpher/gas-fakes init &lt;span class="nt"&gt;--auth-type&lt;/span&gt; adc
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx @mcpher/gas-fakes auth
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Alternatively, if you want to install the package globally via &lt;code&gt;npm&lt;/code&gt;, run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-g&lt;/span&gt; @mcpher/gas-fakes
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;gas-fakes init &lt;span class="nt"&gt;--auth-type&lt;/span&gt; adc
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;gas-fakes auth
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once the authorization is complete, retrieve your credentials file.&lt;/p&gt;

&lt;p&gt;For Linux / macOS:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; ~/.config/gcloud/application_default_credentials.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For Windows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;type &lt;/span&gt;C:&lt;span class="se"&gt;\U&lt;/span&gt;sers&lt;span class="se"&gt;\{&lt;/span&gt;user name&lt;span class="o"&gt;}&lt;/span&gt;&lt;span class="se"&gt;\A&lt;/span&gt;ppData&lt;span class="se"&gt;\R&lt;/span&gt;oaming&lt;span class="se"&gt;\g&lt;/span&gt;cloud&lt;span class="se"&gt;\a&lt;/span&gt;pplication_default_credentials.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Copy the output. Your authorization data will look similar to the following JSON structure:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"account"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"client_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"{your client ID}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"client_secret"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"{your client secret}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"quota_project_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"{your project ID}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"refresh_token"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"{your refresh token}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"authorized_user"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"universe_domain"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"googleapis.com"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 2: Create a New GitHub Repository
&lt;/h3&gt;

&lt;p&gt;To test this integration, create a new repository on GitHub. For this guide, we will assume the repository name is &lt;code&gt;sample&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 3: Configure GitHub Secrets
&lt;/h3&gt;

&lt;p&gt;We must securely store the authorization data in GitHub. Navigate to your repository's "Settings" -&amp;gt; "Secrets and variables" -&amp;gt; "Actions". Click on "New repository secret" and configure the following:&lt;/p&gt;

&lt;p&gt;Key: &lt;code&gt;GCP_ADC_USER_JSON&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Value: (Paste the JSON string you copied in Step 1)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"account"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"client_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"{your client ID}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"client_secret"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"{your client secret}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"quota_project_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"{your project ID}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"refresh_token"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"{your refresh token}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"authorized_user"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"universe_domain"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"googleapis.com"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When the &lt;code&gt;gh&lt;/code&gt; command is used, you can also use the following command.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;gh secret &lt;span class="nb"&gt;set &lt;/span&gt;GCP_ADC_USER_JSON &amp;lt; ~/.config/gcloud/application_default_credentials.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Phase 2: Building Your First Workflow
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Step 4: Define the GitHub Actions Workflow
&lt;/h3&gt;

&lt;p&gt;Create a new YAML file named &lt;code&gt;sample1.yml&lt;/code&gt; inside the &lt;code&gt;.github/workflows/&lt;/code&gt; directory of your repository.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Run GAS via gas-fakes (sample1)&lt;/span&gt;

&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;push&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;branches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;*"&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
  &lt;span class="na"&gt;pull_request&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;branches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;*"&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
  &lt;span class="na"&gt;issues&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;types&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;opened&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;edited&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;closed&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
  &lt;span class="na"&gt;workflow_dispatch&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;execute&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;sample1&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;FORCE_JAVASCRIPT_ACTIONS_TO_NODE24&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;

    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Checkout repository&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v4&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Setup Node.js&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/setup-node@v4&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;node-version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;24"&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Create ADC file&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;echo '${{ secrets.GCP_ADC_USER_JSON }}' &amp;gt; /tmp/adc.json&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Extract Project ID from ADC&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;echo "PROJECT_ID=$(jq -r '.quota_project_id' /tmp/adc.json)" &amp;gt;&amp;gt; $GITHUB_ENV&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Run Google Apps Script by gas-fakes&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;export GOOGLE_CLOUD_PROJECT=$PROJECT_ID&lt;/span&gt;
          &lt;span class="s"&gt;export GOOGLE_APPLICATION_CREDENTIALS=/tmp/adc.json&lt;/span&gt;
          &lt;span class="s"&gt;npx @mcpher/gas-fakes -s "const rootFolder = DriveApp.getRootFolder(); const rootFolderName = rootFolder.getName(); console.log(rootFolderName);"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this initial test, a simple Google Apps Script snippet (&lt;code&gt;const rootFolder = DriveApp.getRootFolder();...&lt;/code&gt;) is passed directly as an inline string within the YAML file.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 5: Push to the Remote Repository
&lt;/h3&gt;

&lt;p&gt;Commit and push your local repository to GitHub. This action will automatically trigger the GitHub Actions workflow. You can monitor the execution under the "Actions" tab. If successful, the step "Run Google Apps Script by gas-fakes" will output logs similar to this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;...using env file in /home/runner/work/github-action-for-gas-fakes/github-action-for-gas-fakes/.env
...gas-fakes version 2.2.7[Worker] ...authorized backends: google via ADC (###@gmail.com)[Worker] ...using scriptId: ### (source: random)
マイドライブ
...terminating worker thread
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Seeing your Google Drive's root folder name in the logs confirms that &lt;code&gt;gas-fakes&lt;/code&gt; successfully authenticated and executed the GAS code natively via GitHub Actions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Phase 3: Advanced Practical Applications
&lt;/h2&gt;

&lt;p&gt;To demonstrate the full potential of this architecture, let's explore two practical CI/CD use cases: recording execution logs to Google Sheets and uploading repository diffs to Google Drive.&lt;/p&gt;

&lt;p&gt;When this phase is finished, the directory structure of this repository becomes as follows.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fixi352ckitrhaffacet0.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fixi352ckitrhaffacet0.jpg" alt="fig2a" width="470" height="807"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Please create the following &lt;code&gt;package.json&lt;/code&gt; and put into the root directory.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"gas-fakes-workflow"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1.0.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Google Apps Script execution on GitHub Actions with gas-fakes"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"main"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"sources/sample2.js"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"module"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"scripts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"sample2"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"node sources/sample2.js"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"sample3"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"node sources/sample3.js"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"dependencies"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"@mcpher/gas-fakes"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^2.3.0"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"engines"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"node"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&amp;gt;=24"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;.gitignore&lt;/code&gt; is as follows.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;node_modules/
.env
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Also, in order to create &lt;code&gt;package-lock.json&lt;/code&gt;, please run &lt;code&gt;npm install&lt;/code&gt; at the local side.&lt;/p&gt;

&lt;p&gt;As another file, please create &lt;code&gt;appsscript.json&lt;/code&gt; in the root directory as follows.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"timeZone"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Asia/Tokyo"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"exceptionLogging"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"STACKDRIVER"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"runtimeVersion"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"V8"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"oauthScopes"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"https://www.googleapis.com/auth/drive"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Case 1: Record Execution Logs to Google Sheets
&lt;/h3&gt;

&lt;p&gt;In this scenario, whenever a push, pull request, issue event, or manual trigger occurs, the workflow stores detailed execution logs in a Google Spreadsheet named &lt;code&gt;sample spreadsheet for gas-fakes sample&lt;/code&gt;. If the file doesn't exist, it is automatically created in the root folder of your Google Drive.&lt;/p&gt;

&lt;p&gt;Add the following YAML file to &lt;code&gt;.github/workflows/sample2.yml&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Run GAS via gas-fakes (sample2)&lt;/span&gt;

&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;push&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;branches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;*"&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
  &lt;span class="na"&gt;pull_request&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;branches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;*"&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
  &lt;span class="na"&gt;issues&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;types&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;opened&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;edited&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;closed&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
  &lt;span class="na"&gt;workflow_dispatch&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;

&lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;FORCE_JAVASCRIPT_ACTIONS_TO_NODE24&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;true"&lt;/span&gt;

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;execute&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;sample2&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Checkout repository&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v4&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;fetch-depth&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Setup Node.js&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/setup-node@v4&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;node-version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;24"&lt;/span&gt;
          &lt;span class="na"&gt;cache&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;npm"&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Install dependencies&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;npm install&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Create ADC file&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;echo '${{ secrets.GCP_ADC_USER_JSON }}' &amp;gt; /tmp/adc.json&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Extract Project ID from ADC&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;echo "PROJECT_ID=$(jq -r '.quota_project_id' /tmp/adc.json)" &amp;gt;&amp;gt; $GITHUB_ENV&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Run Google Apps Script by gas-fakes&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;export GOOGLE_CLOUD_PROJECT=$PROJECT_ID&lt;/span&gt;
          &lt;span class="s"&gt;export GOOGLE_APPLICATION_CREDENTIALS=/tmp/adc.json&lt;/span&gt;
          &lt;span class="s"&gt;npm run sample2&lt;/span&gt;
        &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;GH_EVENT_NAME&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;${{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;github.event_name&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}"&lt;/span&gt;
          &lt;span class="na"&gt;GH_ACTOR&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;${{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;github.actor&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}"&lt;/span&gt;
          &lt;span class="na"&gt;GH_SHA&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;${{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;github.sha&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}"&lt;/span&gt;
          &lt;span class="na"&gt;GH_REF&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;${{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;github.ref_name&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}"&lt;/span&gt;
          &lt;span class="na"&gt;GH_COMMIT_MSG&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;${{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;github.event.issue.title&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;||&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;github.event.head_commit.message&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;||&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;github.event.pull_request.title&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;||&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;'Manual&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Run'&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}"&lt;/span&gt;
          &lt;span class="na"&gt;GH_RUN_URL&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;${{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;github.server_url&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}/${{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;github.repository&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}/actions/runs/${{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;github.run_id&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}"&lt;/span&gt;
          &lt;span class="na"&gt;GH_REPO&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;${{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;github.repository&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}"&lt;/span&gt;
          &lt;span class="na"&gt;GH_RUN_NUMBER&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;${{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;github.run_number&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}"&lt;/span&gt;
          &lt;span class="na"&gt;GH_ISSUE_NUMBER&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;${{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;github.event.issue.number&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;||&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;''&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}"&lt;/span&gt;
          &lt;span class="na"&gt;GH_ISSUE_ACTION&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;${{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;github.event.action&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;||&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;''&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}"&lt;/span&gt;
          &lt;span class="na"&gt;GH_ISSUE_URL&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;${{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;github.event.issue.html_url&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;||&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;''&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, create a new directory named &lt;code&gt;sources&lt;/code&gt; and add the corresponding Google Apps Script file as &lt;code&gt;sources/sample2.js&lt;/code&gt;. This script reads the environment variables passed by GitHub Actions and appends them to the spreadsheet.&lt;/p&gt;

&lt;p&gt;In order to use gas-fakes in a cache, &lt;code&gt;import "@mcpher/gas-fakes"&lt;/code&gt; was used in the script.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@mcpher/gas-fakes&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="cm"&gt;/**
 * Log GitHub Actions execution details to Google Sheets
 * Updated to include Issue details
 */&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;logExecution&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;timestamp&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;toLocaleString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ja-JP&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;timeZone&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Asia/Tokyo&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;}),&lt;/span&gt;
    &lt;span class="na"&gt;event&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;GH_EVENT_NAME&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;action&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;GH_ISSUE_ACTION&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;issueNumber&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;GH_ISSUE_NUMBER&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;actor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;GH_ACTOR&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;repo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;GH_REPO&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;branch&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;GH_REF&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;runNumber&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;GH_RUN_NUMBER&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;GH_COMMIT_MSG&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;sha&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;GH_SHA&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;GH_RUN_URL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;issueUrl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;GH_ISSUE_URL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;values&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;timestamp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;action&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;issueNumber&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;actor&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;repo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;branch&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;runNumber&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sha&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;issueUrl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="p"&gt;];&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;headers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Timestamp&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Event&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Action&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Issue #&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;User&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Message/Title&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Repository&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Branch&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Run #&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Commit SHA&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Workflow URL&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Issue URL&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;];&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;spreadsheet_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;sample spreadsheet for gas-fakes sample&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sheetName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;log&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;files&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;DriveApp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getFilesByName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;spreadsheet_name&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ss&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;files&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;hasNext&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;SpreadsheetApp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;openById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;files&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;next&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;getId&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;SpreadsheetApp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;spreadsheet_name&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;sheet&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;ss&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getSheetByName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sheetName&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;ss&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;insertSheet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sheetName&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sheet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getDataRange&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;getValues&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;sheet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getRange&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;setValues&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;values&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;sheet&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getRange&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sheet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getLastRow&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;values&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;values&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setValues&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;values&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s2"&gt;`Log updated for &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;repo&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; (Event: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;, Run #&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;runNumber&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;)`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nf"&gt;logExecution&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Case 2: Sync Repository Diffs to Google Drive
&lt;/h3&gt;

&lt;p&gt;This workflow dynamically generates a &lt;code&gt;.patch&lt;/code&gt; file containing code differences (or issue content) based on the triggered event, saving the result directly into a Google Drive folder named &lt;code&gt;GitHub Sync - Repo Diffs&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Create &lt;code&gt;.github/workflows/sample3.yml&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Run GAS via gas-fakes (sample3)&lt;/span&gt;

&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;push&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;branches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;*"&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
  &lt;span class="na"&gt;pull_request&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;branches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;*"&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
  &lt;span class="na"&gt;issues&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;types&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;opened&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;edited&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;closed&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
  &lt;span class="na"&gt;workflow_dispatch&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;

&lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;FORCE_JAVASCRIPT_ACTIONS_TO_NODE24&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;true"&lt;/span&gt;

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;sync-to-drive&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;sample3&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Checkout Repository&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v4&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;fetch-depth&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Setup Node.js Environment&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/setup-node@v4&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;node-version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;24"&lt;/span&gt;
          &lt;span class="na"&gt;cache&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;npm"&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Install dependencies&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;npm install&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Configure Google Cloud Credentials&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;echo '${{ secrets.GCP_ADC_USER_JSON }}' &amp;gt; /tmp/adc.json&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Extract Project ID from ADC&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;echo "PROJECT_ID=$(jq -r '.quota_project_id' /tmp/adc.json)" &amp;gt;&amp;gt; $GITHUB_ENV&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Run Google Apps Script by gas-fakes&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;if [ "${{ github.event_name }}" == "pull_request" ]; then&lt;/span&gt;
            &lt;span class="s"&gt;REPO_DIFF=$(git diff origin/${{ github.base_ref }}...HEAD | base64 -w 0)&lt;/span&gt;
          &lt;span class="s"&gt;elif [ "${{ github.event_name }}" == "push" ] &amp;amp;&amp;amp; [ "${{ github.event.before }}" != "0000000000000000000000000000000000000000" ]; then&lt;/span&gt;
            &lt;span class="s"&gt;REPO_DIFF=$(git diff ${{ github.event.before }}..${{ github.sha }} | base64 -w 0)&lt;/span&gt;
          &lt;span class="s"&gt;elif [ "${{ github.event_name }}" == "issues" ]; then&lt;/span&gt;
            &lt;span class="s"&gt;ISSUE_CONTENT="Issue Event: ${{ github.event.action }}\nTitle: ${{ github.event.issue.title }}\n\n${{ github.event.issue.body }}"&lt;/span&gt;
            &lt;span class="s"&gt;REPO_DIFF=$(echo -e "$ISSUE_CONTENT" | base64 -w 0)&lt;/span&gt;
          &lt;span class="s"&gt;else&lt;/span&gt;
            &lt;span class="s"&gt;REPO_DIFF=$(echo "Manual trigger: No diff." | base64 -w 0)&lt;/span&gt;
          &lt;span class="s"&gt;fi&lt;/span&gt;

          &lt;span class="s"&gt;export REPO_DIFF&lt;/span&gt;
          &lt;span class="s"&gt;export GOOGLE_CLOUD_PROJECT=$PROJECT_ID&lt;/span&gt;
          &lt;span class="s"&gt;export GOOGLE_APPLICATION_CREDENTIALS=/tmp/adc.json&lt;/span&gt;

          &lt;span class="s"&gt;npm run sample3&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create the corresponding script at &lt;code&gt;sources/sample3.js&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@mcpher/gas-fakes&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="cm"&gt;/**
 * Syncs the repository-wide diff to a text file in Google Drive.
 */&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;syncRepoDiff&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;base64Diff&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;REPO_DIFF&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;base64Diff&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;warn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;No diff content found in environment variable REPO_DIFF.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;timestamp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;toISOString&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;[&lt;/span&gt;&lt;span class="sr"&gt;:.&lt;/span&gt;&lt;span class="se"&gt;]&lt;/span&gt;&lt;span class="sr"&gt;/g&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;-&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;eventName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;GITHUB_EVENT_NAME&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;manual&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;filename&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`diff_&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;eventName&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;_&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;timestamp&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.patch`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;folderName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;GitHub Sync - Repo Diffs&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;decodedBytes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Utilities&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;base64Decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;base64Diff&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;blob&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Utilities&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;newBlob&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;decodedBytes&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;text/plain&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;folders&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;DriveApp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getFoldersByName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;folderName&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;folder&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;folders&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;hasNext&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
      &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;folders&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;next&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
      &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;DriveApp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createFolder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;folderName&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;file&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;folder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;blob&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Successfully synced repository diff.`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Saved as: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`View File: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getUrl&lt;/span&gt;&lt;span class="p"&gt;()}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Error during Drive sync: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nf"&gt;syncRepoDiff&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Execution and Verification
&lt;/h2&gt;

&lt;p&gt;When a "push" event is triggered on the repository, the configured workflows execute concurrently. The visual output below confirms that GitHub Actions handled the integration flawlessly.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1foorvqm4gq7yrja0zbk.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1foorvqm4gq7yrja0zbk.jpg" alt="fig2b" width="800" height="270"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Executing the &lt;code&gt;sample2&lt;/code&gt; workflow processes the event payload and successfully appends a structured log directly into our Google Spreadsheet, functioning perfectly as an automated audit trail.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftldf8yv9k01y783mqupb.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftldf8yv9k01y783mqupb.jpg" alt="fig2c" width="800" height="180"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Executing the &lt;code&gt;sample3&lt;/code&gt; workflow correctly calculates the Git diff and uploads it to the designated Google Drive folder as a standard &lt;code&gt;.patch&lt;/code&gt; file. Below is an example of the resulting text file contents generated in Google Drive:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;diff --git a/README.md b/README.md
index d63c7d8..19f6f9d 100644
--- a/README.md
+++ b/README.md
@@ -246,3 +246,4 @@ jobs:

sample
sample
+   sample text
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These results clearly demonstrate that Google Workspace services can be seamlessly managed and extended utilizing Google Apps Script and &lt;code&gt;gas-fakes&lt;/code&gt; within a GitHub Actions CI/CD environment.&lt;/p&gt;

&lt;h2&gt;
  
  
  Exploring Further Applications
&lt;/h2&gt;

&lt;p&gt;The integration of Google Apps Script and GitHub Actions via &lt;code&gt;gas-fakes&lt;/code&gt; is not limited to logging and file syncing. This foundation unlocks numerous advanced automation possibilities for enterprise environments:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Automated Unit Testing&lt;/strong&gt;&lt;br&gt;
Traditionally, testing Google Apps Script code is a manual and tedious process. By utilizing &lt;code&gt;gas-fakes&lt;/code&gt;, you can integrate standard JavaScript testing frameworks like Jest or Mocha. GitHub Actions can automatically run these test suites against your GAS functions on every pull request, ensuring robust code quality and preventing regressions before deployment.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Dynamic Document Generation&lt;/strong&gt;&lt;br&gt;
You can trigger Google Workspace document creation based on repository events. For instance, when a new GitHub Release is published, a workflow can use Google Docs or Google Slides services via &lt;code&gt;gas-fakes&lt;/code&gt; to automatically compile release notes, insert relevant code snippets, and generate presentation slides for stakeholders.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Workspace Infrastructure as Code (IaC)&lt;/strong&gt;&lt;br&gt;
By storing organizational configurations—such as user directory structures, group memberships, or shared drive permissions—as YAML or JSON files in your repository, you can manage Google Workspace like infrastructure. Upon merging changes to the main branch, a workflow can execute a Google Apps Script that interfaces with the Admin Directory API to automatically synchronize the real-world Workspace environment with your repository's state.&lt;/p&gt;

&lt;h2&gt;
  
  
  Discussion: Evaluating the CI/CD Architecture
&lt;/h2&gt;

&lt;p&gt;Integrating &lt;code&gt;gas-fakes&lt;/code&gt; into your deployment pipeline shifts the paradigm of Google Workspace development. However, to maximize its potential, developers must weigh several technical considerations.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;gas-fakes vs. Traditional clasp Workflows&lt;/strong&gt;&lt;br&gt;
Traditionally, developers rely on Google's &lt;code&gt;clasp&lt;/code&gt; tool to synchronize local code with the Apps Script server. While effective for deployment, &lt;code&gt;clasp&lt;/code&gt; cannot natively execute code locally. Testing requires pushing to Google’s servers and executing manually or via triggers. In contrast, &lt;code&gt;gas-fakes&lt;/code&gt; provides a robust Node.js emulation layer. This enables instant execution, seamless integration with standard testing frameworks, and automated triggers directly from GitHub Actions without maintaining intermediate Web Apps.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Strategic Importance of CI/CD in Workspace Development&lt;/strong&gt;&lt;br&gt;
Implementing CI/CD for Google Workspace is a major leap in enterprise security and maintainability. By shifting execution into GitHub Actions, sensitive credentials are removed from hardcoded scripts and securely managed via GitHub Secrets. Furthermore, every automation execution is firmly tied to a specific Git commit and workflow run, ensuring an immutable, highly auditable trail of operations that is essential for modern development standards.&lt;/p&gt;

&lt;h2&gt;
  
  
  Note
&lt;/h2&gt;

&lt;p&gt;In this article, we focused on using &lt;strong&gt;Application Default Credentials (ADC)&lt;/strong&gt; for &lt;code&gt;gas-fakes&lt;/code&gt; authorization because of its straightforward setup for individual developers.&lt;/p&gt;

&lt;p&gt;For more advanced or enterprise-scale implementations, consider the following alternatives and best practices:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Domain-Wide Delegation (DWD):&lt;/strong&gt;
For organizational workflows, &lt;code&gt;gas-fakes&lt;/code&gt; supports Service Accounts with Domain-Wide Delegation. This allows automation to perform actions on behalf of specific users (impersonation) within a Google Workspace domain without relying on personal refresh tokens. You can find instructions on using this GitHub Action with a service account in &lt;strong&gt;&lt;a href="https://github.com/brucemcpherson/gas-fakes-containers/blob/main/github-actions/README.md" rel="noopener noreferrer"&gt;Bruce's repository&lt;/a&gt;&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Workflow Optimization:&lt;/strong&gt;
When using &lt;code&gt;actions/setup-node&lt;/code&gt; with &lt;code&gt;cache: "npm"&lt;/code&gt;, ensure your &lt;code&gt;package-lock.json&lt;/code&gt; is committed to the repository root. If your project structure is nested, specify the &lt;code&gt;cache-dependency-path&lt;/code&gt; to prevent "lock file not found" errors during CI/CD runs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Security Best Practices:&lt;/strong&gt;
While these samples use a temporary file (&lt;code&gt;/tmp/adc.json&lt;/code&gt;) for authentication, always ensure that sensitive credential files are never uploaded as artifacts or printed in workflow logs.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Integrates Google Apps Script into modern GitHub CI/CD pipelines using the gas-fakes library.&lt;/li&gt;
&lt;li&gt;Enables local execution of GAS code without the need for constant Web App deployments.&lt;/li&gt;
&lt;li&gt;Securely manages authentication using Google Application Default Credentials (ADC) and GitHub Secrets.&lt;/li&gt;
&lt;li&gt;Demonstrates practical automation cases including automated logging and repository-to-Drive synchronization.&lt;/li&gt;
&lt;li&gt;Provides a foundation for advanced unit testing and Infrastructure as Code (IaC) within Google Workspace.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>googleappsscript</category>
      <category>googleworkspace</category>
      <category>github</category>
      <category>cicd</category>
    </item>
    <item>
      <title>Recursive Knowledge Crystallization: A Framework for Persistent Autonomous Agent Self-Evolution</title>
      <dc:creator>Tanaike</dc:creator>
      <pubDate>Sat, 21 Feb 2026 07:37:04 +0000</pubDate>
      <link>https://forem.com/gde/recursive-knowledge-crystallization-a-framework-for-persistent-autonomous-agent-self-evolution-4mk4</link>
      <guid>https://forem.com/gde/recursive-knowledge-crystallization-a-framework-for-persistent-autonomous-agent-self-evolution-4mk4</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1bjnwsv2znyoogvh0fh7.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1bjnwsv2znyoogvh0fh7.jpg" alt="fig1a" width="800" height="662"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Abstract
&lt;/h2&gt;

&lt;p&gt;In the development of autonomous agents using Large Language Models (LLMs), restrictions such as context window limits and session fragmentation pose significant barriers to the long-term accumulation of knowledge. This study proposes a "self-evolving framework" where an agent continuously records and refines its operational guidelines and technical knowledge—referred to as its SKILL—directly onto a local filesystem in a universally readable format (Markdown). By conducting experiments across two distinct environments featuring opaque constraints and complex legacy server rules using Google's Antigravity and Gemini CLI, we demonstrate the efficacy of this framework. Our findings reveal that the agent effectively evolves its SKILL through iterative cycles of trial and error, ultimately saturating its learning. Furthermore, by transferring this evolved SKILL to a completely clean environment, we verify that the agent can successfully implement complete, flawless client applications in a single attempt (zero-shot generation). This methodology not only circumvents the limitations of short-term memory dependency but also pioneers a new paradigm for cross-environment knowledge portability and automated system analysis.&lt;/p&gt;

&lt;p&gt;The infographic of this article is as follows.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxnrfhpb2y6wmjl63qj3m.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxnrfhpb2y6wmjl63qj3m.jpg" alt="fig1d" width="800" height="1286"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Introduction
&lt;/h2&gt;

&lt;p&gt;One of the primary challenges in the deployment of autonomous AI agents is the persistence of learning. Traditionally, when an agent's execution environment is reset or its context window is cleared, it is forced to relearn task specifications from scratch.&lt;/p&gt;

&lt;p&gt;In the realm of autonomous agents, enabling long-term memory and self-correction has been a focal point of recent research. Notable frameworks such as &lt;a href="https://arxiv.org/abs/2303.11366" rel="noopener noreferrer"&gt;Reflexion (Shinn et al., 2023)&lt;/a&gt; endow agents with dynamic memory to self-reflect and refine reasoning through trial and error. Similarly, &lt;a href="https://arxiv.org/abs/2305.16291" rel="noopener noreferrer"&gt;Voyager (Wang et al., 2023)&lt;/a&gt; introduces an open-ended embodied agent in Minecraft that utilizes an ever-growing executable code skill library for lifelong learning. Another significant approach is &lt;a href="https://arxiv.org/abs/2310.08560" rel="noopener noreferrer"&gt;MemGPT (Packer et al., 2023)&lt;/a&gt;, which treats LLMs as operating systems, employing hierarchical memory management to bypass context window limitations.&lt;/p&gt;

&lt;p&gt;However, these existing methodologies predominantly rely on in-memory contexts, specialized vector databases, or internal virtual memory abstractions. This makes the acquired knowledge difficult to decouple from the specific agent instance and challenging for human developers to read, audit, or directly manage.&lt;/p&gt;

&lt;p&gt;In contrast, our proposed framework introduces a paradigm shift: the continuous self-rewriting of an agent's "SKILL" residing entirely on a local filesystem (&lt;code&gt;SKILL.md&lt;/code&gt;). By utilizing standard development toolchains (Antigravity and Gemini CLI), the agent investigates unknown environments and automatically maintains its own operational manual. This approach offers a distinct advantage—the physical persistence of knowledge in a universally readable Markdown format. It enables not only continuous learning but also &lt;strong&gt;"Zero-Shot Knowledge Transfer."&lt;/strong&gt; We empirically prove that once the SKILL evolves and saturates in one environment (Antigravity), it can be loaded into a completely clean environment (Gemini CLI) to synthesize complex code perfectly on the first attempt without any trial and error.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Experimental Procedure
&lt;/h2&gt;

&lt;p&gt;In this framework, &lt;strong&gt;SKILL&lt;/strong&gt; refers to &lt;strong&gt;Agent Skill&lt;/strong&gt;, a standardized set of executable capabilities and knowledge markers that define an agent's operational boundaries. As defined by the Agent Skill community (&lt;a href="https://agentskills.io/home" rel="noopener noreferrer"&gt;https://agentskills.io/home&lt;/a&gt;), these skills allow for modular, portable, and verifiable agentic functions. Our framework treats the &lt;code&gt;SKILL.md&lt;/code&gt; file as a living repository of these Agent Skills.&lt;/p&gt;

&lt;p&gt;To demonstrate the continuous evolution of the SKILL and its application, we designed two distinct experiments. In both cases, Antigravity was utilized for the iterative evolution of the SKILL, followed by a demonstration using the Gemini CLI for zero-shot script completion.&lt;/p&gt;

&lt;h3&gt;
  
  
  2.1. Experiment 1: The "Silent Bureaucrat" Blind Incremental Evolution
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Server Characteristics:&lt;/strong&gt; The server enforces a highly restrictive, black-box validation process consisting of 20 sequential rules. Crucially, it possesses a hidden tolerance limit (5 errors per session). Once this limit is reached, it returns an absolute block (&lt;code&gt;Limit reached.&lt;/code&gt;) and becomes completely silent—it provides no further hints, and neither logs nor returns the Session ID, enforcing "Absolute Silence."&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Client Goal:&lt;/strong&gt; The agent must write a Node.js script (&lt;code&gt;client.js&lt;/code&gt;) that successfully satisfies all 20 validation rules in a single POST request to &lt;code&gt;/api/submit&lt;/code&gt; to retrieve the hidden flag: &lt;code&gt;"FLAG{INCREMENTAL_EVOLUTION_COMPLETE}"&lt;/code&gt;. The agent is strictly prohibited from changing its Session ID during a single cycle to bypass the block.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Workflow
&lt;/h4&gt;

&lt;ol&gt;
&lt;li&gt;Start the Server.&lt;/li&gt;
&lt;li&gt;Launch the Antigravity agent.&lt;/li&gt;
&lt;li&gt;Execute the prompt to develop the Client script.&lt;/li&gt;
&lt;li&gt;The AI agent updates the SKILL based on failures or newly discovered rules.&lt;/li&gt;
&lt;li&gt;The Client script is reset to its initial blank state (Tabula Rasa). Keeping the exact same prompt, steps 3 and 4 are looped. When the agent hits the "Limit reached" block, it is forced to terminate the cycle, but its SKILL persists. We performed 10 cycles until the evolution of the SKILL was saturated and success was achieved.&lt;/li&gt;
&lt;li&gt;The fully evolved SKILL is transferred to a clean environment. Using the Gemini CLI, we confirm that the Client script is completed perfectly in a single prompt (zero-shot).&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwvmqjkmy8knwbcux7ssg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwvmqjkmy8knwbcux7ssg.png" alt="fig1b" width="800" height="1549"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://mermaid.ai/play?utm_source=mermaid_live_editor&amp;amp;utm_medium=share#pako:eNpdkV9vmzAUxb_KlR_2lDBIoCFo6pQSmkZF0zS6SSv0wYNLsIRtZAxqGuW7z_zptM1Ptu_5HZ_reyG5LJAE5KRoU8HTPhNg1i51LEg0VRoSVD2qF1gub-EuXVkQ007kFeyEZgbqmT6_TNDdqAnTtQXRK-adRviqJG80lFJBWDMUxi5XrNEzEY7EPnUt2J2G6vemoBpbSB6PcWzxYtbtR1108SwIz3mNEDPONHyEhOpOGaL4DJ9-qVuIpWzAsSdVe53oyNDwRY4e9-k3bFH_mwY-wCNiY9z4e-T54fuppb98fpp0w90hvbHgSVHRlqgg6mXdYzHlBi3hgJwJBmF8nJ0OI_WQ_vczTJgoSAVEomdKCm5SzcTDSBzTZ1RymVTShDajMs4CTctMCviBipUMh18iCzNBVpBAqw4XhKPidDiSy2CWEV0hx4wEZltgSbtaZyQTV4M1VDxLyd9JJbtTRYKS1q05deM89oyaSfM_twpFgSqUndAkWPujBwku5JUEq83GuvE9e-M5G9tbu7a3IGcj2lr-xt36K9d3Hc_ZOu51Qd7GZ21T8a6_Acxixjk" rel="noopener noreferrer"&gt;Mermaid Chart Playground&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  2.2. Experiment 2: Chaos Server Constraints &amp;amp; Architectural Pattern Acquisition
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Server Characteristics:&lt;/strong&gt; This server mimics a deeply flawed legacy API environment ("Chaos Server"). It contains implicit rules, semantic riddles (e.g., requiring the reverse string of 'nimda', or the Base64 encoding of the word 'alpha'), and state-dependent limitations. If the agent makes too many requests within a timeframe (cumulative limit of 8), a "Session Block" occurs, necessitating the dynamic generation of unique identifiers to bypass.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Client Goal:&lt;/strong&gt; The agent must develop a comprehensive, object-oriented SDK (&lt;code&gt;client-app.js&lt;/code&gt;) capable of interacting with 5 distinct endpoints (connection check, user creation, secure data retrieval, config update, and system audit) and performing a stress test, overcoming all chaos rules programmatically.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Workflow
&lt;/h4&gt;

&lt;ol&gt;
&lt;li&gt;Start the Server.&lt;/li&gt;
&lt;li&gt;Launch the Antigravity agent.&lt;/li&gt;
&lt;li&gt;Execute the prompt to develop specific features of the Client script.&lt;/li&gt;
&lt;li&gt;The AI agent updates the SKILL, documenting both the discovered constraints and higher-level architectural implementation guidelines.&lt;/li&gt;
&lt;li&gt;The Client script is &lt;strong&gt;not&lt;/strong&gt; reset. Instead, the prompt is progressively changed in each cycle to command the development of new features, building upon the existing script. This process continues until the planned evolution of the application is complete (5 cycles).&lt;/li&gt;
&lt;li&gt;The fully evolved SKILL is transferred to a clean environment. Using the Gemini CLI, the &lt;code&gt;client-app.js&lt;/code&gt; is reset to a blank state, and a single, comprehensive prompt is given to build the entire SDK. We confirm that the complete application is generated flawlessly on the first attempt (zero-shot).&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6p65hpcan8stjz85mhvy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6p65hpcan8stjz85mhvy.png" alt="fig1c" width="800" height="1619"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://mermaid.ai/play?utm_source=mermaid_live_editor&amp;amp;utm_medium=share#pako:eNpVkt2OmzAQhV9l5IteJSgksElQtVVCfjZKtKrEtlIX9sKFIVgCGw02Shrl3esQsmp9hW2-M2fO-MJSlSEL2JF4XcDbKpFg1yJ2HYg0Jw0RUov0AcPhMyzjsQMHbmRawEJqYaFW6PPHHVp2_4TxxIH1CVOjEb6TqmoNuSKIakxFLlLYINeGsIfCDlrFngOLI0oNP-qMa2wgVLLRxIXUDXyBrREZlkLaCyEh2u8OB6fKeo1Vp7G--FajLB8FGlgaUepv8PU3PcNBqRp8CM9pic31zq0tB6-qozdxWHB5_M_yK570Q-2uskesITREN6dRSqLWvYfNvfl_hH9ZB7ezbfzkwBtx2eRIsG5V2WJ2bwG0gi1WQgoID7teadtRL_Ejw9D6ISxQNqL9tGdDCEvkEtayFaRkZQ31_EvH7-J3JDWMCmVbMDaVaLW3tSQS10JJ-Ilkp4G3CNnATl9kLNBkcMAqpIrftuxyE0yYLrDChAX2M8Ocm1InLJFXi9VcvitVPUhS5liwIOdlY3emG-RKcPtKqs9Tm1yGFCojNQu8WafBggs7sWA8nTpPM3809d3pyJ94I3_AziyYzJ3Z1JvPxt7Mc3137nrXAfvTlR3ZG__6F1Mk3KQ" rel="noopener noreferrer"&gt;Mermaid Chart Playground&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Results and Discussions
&lt;/h2&gt;

&lt;h3&gt;
  
  
  3.1. Experiment 1 Results &amp;amp; Discussion
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Table 1: Experiment 1 Data Summary&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Cycle&lt;/th&gt;
&lt;th&gt;Success&lt;/th&gt;
&lt;th&gt;Mod. Count&lt;/th&gt;
&lt;th&gt;Interaction Count&lt;/th&gt;
&lt;th&gt;Skill Size (Bytes)&lt;/th&gt;
&lt;th&gt;State&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;False&lt;/td&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;2666&lt;/td&gt;
&lt;td&gt;Exploration (Fail)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;False&lt;/td&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;2963&lt;/td&gt;
&lt;td&gt;Exploration (Fail)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;False&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;2963&lt;/td&gt;
&lt;td&gt;Exploration (Fail)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;False&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;3474&lt;/td&gt;
&lt;td&gt;Exploration (Fail)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;5&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;True&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;3&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;3&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;3654&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Breakthrough&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;True&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;3654&lt;/td&gt;
&lt;td&gt;Convergence&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;td&gt;True&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;3654&lt;/td&gt;
&lt;td&gt;Stable&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;True&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;3728&lt;/td&gt;
&lt;td&gt;Stable&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;9&lt;/td&gt;
&lt;td&gt;True&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;3728&lt;/td&gt;
&lt;td&gt;Full Convergence&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td&gt;True&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;3728&lt;/td&gt;
&lt;td&gt;Full Convergence&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvlo0wx3udhayus6mbsv6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvlo0wx3udhayus6mbsv6.png" alt="fig2a" width="800" height="480"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Figure 1: The inverse relationship between trial-and-error reduction and knowledge accumulation for Experiment 1&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Zero-Shot Verification:&lt;/strong&gt; Utilizing the completely evolved SKILL from cycle 10, the Gemini CLI successfully generated the functional client code on the very first attempt.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fym4m1l8b887rpgsvmblj.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fym4m1l8b887rpgsvmblj.jpg" alt="fig2b" width="800" height="1336"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Figure 2: Zero-shot script completion in Gemini CLI using the evolved SKILL from Experiment 1.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Discussion:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In Experiment 1, the agent operated under a severe "blind" constraint. During cycles 1 through 4, the agent repeatedly hit the hidden error limit and was forcefully blocked. In traditional LLM agent execution, resetting the script here would mean starting from zero. However, because the agent accurately recorded the fragments of rules it discovered into &lt;code&gt;SKILL.md&lt;/code&gt; before "dying" in each cycle, the subsequent generations inherited this knowledge. Cycle 5 represents the critical threshold where the accumulated knowledge was sufficient to bypass all 20 rules without hitting the error limit. The convergence to a modification count of 1 in later cycles, and the successful zero-shot execution in Gemini CLI, definitively prove that the agent effectively offloaded its working memory to the local filesystem, rendering the task independent of the model's immediate context window.&lt;/p&gt;

&lt;h3&gt;
  
  
  3.2. Experiment 2 Results &amp;amp; Discussion
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Table 2: Experiment 2 Data Summary&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Cycle&lt;/th&gt;
&lt;th&gt;Success&lt;/th&gt;
&lt;th&gt;Mod. Count&lt;/th&gt;
&lt;th&gt;Interaction Count&lt;/th&gt;
&lt;th&gt;Skill Size (Bytes)&lt;/th&gt;
&lt;th&gt;Learning Content&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;True&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;2013&lt;/td&gt;
&lt;td&gt;Identity, Content-Type identification&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;True&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;2407&lt;/td&gt;
&lt;td&gt;Secure/Admin constraints (Riddles)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;True&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;2634&lt;/td&gt;
&lt;td&gt;Audit (Base64 signature)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;True&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;2634&lt;/td&gt;
&lt;td&gt;Avoid Session Block&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;True&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;2957&lt;/td&gt;
&lt;td&gt;Refactoring &amp;amp; Guidelines&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu9f9cxso2fve855ymxyj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu9f9cxso2fve855ymxyj.png" alt="fig3a" width="800" height="480"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Figure 3: The learning curve for Experiment 2&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Zero-Shot Verification:&lt;/strong&gt; Utilizing the evolved SKILL from cycle 5, the Gemini CLI successfully built the entire, complex SDK from a blank file in a single pass without prior context.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg5ww6etfbrhbe4dszf3b.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg5ww6etfbrhbe4dszf3b.jpg" alt="fig3b" width="800" height="1290"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Figure 4: Zero-shot script completion in Gemini CLI using the evolved SKILL from Experiment 2.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Discussion:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;While Experiment 1 showcased constraint discovery, Experiment 2 highlighted the agent's capability for &lt;strong&gt;architectural self-organization&lt;/strong&gt;. The server presented not just rigid rules, but semantic puzzles (e.g., reversing strings, encoding to Base64). The agent successfully decoded these and recorded the solutions. More importantly, when faced with the "Session Block" error in Cycle 4, the agent realized that static headers were insufficient. By Cycle 5, the agent did not just fix the error; it abstracted the solution into a "Centralized Header Factory" pattern. This transition from reactive error-fixing to proactive software architecture design is clearly reflected in the drop of interaction counts to 0 in later cycles. The zero-shot success in Gemini CLI demonstrates that the agent can extract best practices from messy legacy systems and output clean, robust SDKs instantly.&lt;/p&gt;

&lt;h3&gt;
  
  
  3.3. Analysis of SKILL.md Evolution Before and After
&lt;/h3&gt;

&lt;p&gt;Analyzing the physical changes in the &lt;code&gt;SKILL.md&lt;/code&gt; files provides deep insight into the agent's cognitive process. &lt;strong&gt;For the full, unredacted text of the &lt;code&gt;SKILL.md&lt;/code&gt; files and client scripts before and after evolution for both experiments, please refer to Appendix A and Appendix B.&lt;/strong&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Experiment 1 (Silent Bureaucrat)
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Before:&lt;/strong&gt; The "Learned Server Specifications" section was entirely empty, simply stating &lt;code&gt;- Status: Unknown.&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;After:&lt;/strong&gt; The file grew to include a meticulously numbered list of 16 highly specific rules. Crucially, these rules were not just copied error messages. For example, the agent codified the requirement into executable logic: &lt;em&gt;"- Rule 11: The header &lt;code&gt;X-Anti-Robot&lt;/code&gt; must be set to &lt;code&gt;Math.floor(timestamp / 1000)&lt;/code&gt;."&lt;/em&gt; and &lt;em&gt;"- Rule 15: The header &lt;code&gt;X-Final-Signature&lt;/code&gt; must be set to the SHA256 hash of the &lt;code&gt;request_id&lt;/code&gt; string."&lt;/em&gt; This demonstrates the agent's ability to translate abstract failures into actionable programmatic directives.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Experiment 2 (Chaos Server)
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Before:&lt;/strong&gt; The sections for "Known Constraints &amp;amp; Error Codes" and "Method Implementation Guidelines" were empty placeholders.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;After:&lt;/strong&gt; The "Constraints" section contained the decoded solutions to the server's riddles (e.g., &lt;code&gt;X-Audit-Signature&lt;/code&gt; as Base64 of 'alpha'). The most profound evolution occurred in the "Guidelines" section. The agent autonomously authored a comprehensive design document advocating for a &lt;strong&gt;"Centralized Header Factory"&lt;/strong&gt;, detailing how it should handle identity, dynamic session persistence, content negotiation, and endpoint specialization. The agent elevated its learning from "How to bypass an error" to "How to architect a resilient application."&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  3.4. Comprehensive Discussion on Significance and Novelty
&lt;/h3&gt;

&lt;p&gt;The synthesis of these two experiments reveals a highly potent framework for AI-driven software engineering. The traditional dependency on a single continuous context window is fundamentally fragile; when the session ends, the learning dies (Catastrophic forgetting).&lt;/p&gt;

&lt;p&gt;By enforcing the physical writing of knowledge to a Markdown file (&lt;code&gt;SKILL.md&lt;/code&gt;), we achieved &lt;strong&gt;persistent meta-learning&lt;/strong&gt;. The significance of using Markdown over specialized vector databases is twofold:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Human Readability and Auditability:&lt;/strong&gt; Developers can read, review, and manually correct the &lt;code&gt;SKILL.md&lt;/code&gt; file, fostering a true Human-AI collaborative environment.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Cross-Platform Portability (Zero-Shot Knowledge Transfer):&lt;/strong&gt; As demonstrated by successfully moving the evolved SKILL from Antigravity to Gemini CLI, knowledge is no longer locked into a specific agent instance. An agent can spend days investigating a system, and the resulting SKILL can be instantly deployed to a fleet of worker agents, granting them immediate "senior-level" expertise without any required training or trial-and-error phases.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  4. Specific Applications Utilizing This Approach
&lt;/h2&gt;

&lt;p&gt;Based on our findings, this framework can be highly impactful in the following real-world scenarios:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Automated Reverse Engineering &amp;amp; SDK Generation:&lt;/strong&gt; When integrating with undocumented or chaotic legacy API systems, an agent can be deployed to blindly probe the endpoints. It will autonomously decode the hidden rules, generating both a human-readable API specification (the SKILL) and robust client SDKs.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Organizational Tacit Knowledge Documentation:&lt;/strong&gt; In many organizations, "tribal knowledge" dictates how to deploy or test specific systems (e.g., "always wait 5 seconds before hitting this endpoint"). By having an agent execute tasks and fail, it can discover and formalize this tacit knowledge into universally accessible Markdown files, acting as an automated technical writer.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Robust E2E Automated Test Suites:&lt;/strong&gt; UI and API specifications change frequently, breaking tests. An agent equipped with this framework can dynamically update testing protocols in its SKILL as it encounters new failures, acting as a self-healing testing mechanism that minimizes maintenance overhead.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;AI Onboarding and Scaling:&lt;/strong&gt; A mature SKILL file cultivated by an exploratory agent can be instantly copied to the local workspaces of newly deployed AI agents (or human engineers). This instantly transfers "experience," allowing for rapid scaling of development resources.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  5. Strategies for Agent Skill Evolution
&lt;/h2&gt;

&lt;p&gt;To ensure the continuous enhancement of Agent Skills within this framework, we identify two primary evolutionary paths:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Targeted Skill Evolution&lt;/strong&gt;: When the goal is to refine a specific capability, the agent is instructed via an Appendix in the &lt;code&gt;SKILL.md&lt;/code&gt; file. Upon successful problem resolution, the agent triggers a targeted update—adding, deleting, or modifying entries—to that specific skill definition.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Holistic Skill Evolution&lt;/strong&gt;: To evolve the agent's entire skill set simultaneously, evolution instructions are embedded directly into global context files (e.g., &lt;code&gt;GEMINI.md&lt;/code&gt;). This compels the agent to evaluate and update the relevant &lt;code&gt;SKILL.md&lt;/code&gt; files immediately after any successful task execution across its entire operational spectrum.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  6. Summary
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Physical Knowledge Persistence:&lt;/strong&gt; By enforcing the continuous rewriting of a universally readable Markdown SKILL file, agents effectively overcome the limitations and volatility of in-memory LLM context windows.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Mastery of Opaque Constraints:&lt;/strong&gt; Agents proved highly capable of discovering and permanently adapting to hidden server limits, cryptographic requirements, and semantic riddles through systematic trial and error.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Architectural Abstraction:&lt;/strong&gt; Beyond merely fixing localized errors, the agents demonstrated the capacity to synthesize overarching design patterns (e.g., Centralized Header Factories) and document them as future guidelines.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Zero-Shot Knowledge Transfer:&lt;/strong&gt; A SKILL evolved in one execution environment (Antigravity) can be seamlessly imported into a completely clean, distinct environment (Gemini CLI) to generate flawless, complex applications on the very first prompt.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Human-AI Collaborative Paradigm:&lt;/strong&gt; Because the agent's memory resides in a standard file system, human developers maintain full visibility and control, able to read, audit, and augment the agent's logic at any time.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  7. Appendix
&lt;/h2&gt;

&lt;p&gt;The sample scripts, SKILL.md, and prompts can be seen at &lt;a href="https://gist.github.com/tanaikech/966f83cc438b6077b05b9843be09e930" rel="noopener noreferrer"&gt;https://gist.github.com/tanaikech/966f83cc438b6077b05b9843be09e930&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>gemini</category>
      <category>antigravity</category>
      <category>agentskill</category>
    </item>
    <item>
      <title>Building Adaptive Learning Agents with A2UI, Gemini, and Google Apps Script</title>
      <dc:creator>Tanaike</dc:creator>
      <pubDate>Sat, 07 Feb 2026 02:26:26 +0000</pubDate>
      <link>https://forem.com/gde/building-adaptive-learning-agents-with-a2ui-gemini-and-google-apps-script-1536</link>
      <guid>https://forem.com/gde/building-adaptive-learning-agents-with-a2ui-gemini-and-google-apps-script-1536</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fto7qj0yzksi3j9yheda5.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fto7qj0yzksi3j9yheda5.jpg" alt="fig1"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Abstract
&lt;/h2&gt;

&lt;p&gt;This article demonstrates how to build an adaptive learning agent using Agent-to-User Interface (A2UI), Gemini, and Google Apps Script. We explore a system that generates personalized quizzes, tracks performance in Google Sheets, and dynamically adjusts difficulty to maximize learning efficiency within the Google Workspace ecosystem.&lt;/p&gt;

&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;A2UI (Agent-to-User Interface) represents a paradigm shift in how users interact with generative AI. Originally open-sourced by Google and implemented in TypeScript and Python &lt;a href="https://github.com/google/A2UI" rel="noopener noreferrer"&gt;Ref&lt;/a&gt;, A2UI becomes even more powerful when integrated with Google Apps Script (GAS). This combination enables seamless access to the Google Workspace ecosystem, transforming static documents into intelligent, agentic applications.&lt;/p&gt;

&lt;p&gt;To explore this potential, I have previously published several articles detailing the technical foundations:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://medium.com/google-cloud/a2ui-for-google-apps-script-bcd0f37a3774" rel="noopener noreferrer"&gt;A2UI for Google Apps Script&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://medium.com/google-cloud/bringing-a2ui-to-google-workspace-with-gemini-0d85026969b8" rel="noopener noreferrer"&gt;Bringing A2UI to Google Workspace with Gemini&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://medium.com/google-cloud/beyond-chatbots-building-task-driven-agentic-interfaces-in-google-workspace-with-a2ui-and-gemini-06998dcf16d2" rel="noopener noreferrer"&gt;Beyond Chatbots: Building Task-Driven Agentic Interfaces in Google Workspace with A2UI and Gemini&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Building upon the foundation laid in those publications, this article takes a step forward by introducing an &lt;strong&gt;educational support framework&lt;/strong&gt; powered by A2UI and Google Apps Script. In this system, the AI does not simply answer questions; it acts as an active tutor.&lt;/p&gt;

&lt;p&gt;For example, when a user provides a prompt such as &lt;code&gt;I want to study Google Apps Script&lt;/code&gt;, Gemini generates specialized questions on the backend. A2UI then renders these as an interactive quiz on the frontend. Crucially, the system utilizes Google Sheets as a persistent memory store. By analyzing historical performance data, the agent generates increasingly challenging content for subsequent sessions, facilitating a personalized and effective learning progression.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Adaptive Learning Engine
&lt;/h2&gt;

&lt;p&gt;The core value of this application lies in its ability to improve learning efficiency through a "Feedback-Loop Architecture." Unlike static quiz apps, this agent utilizes Google Sheets as a persistent memory store to track cognitive growth.&lt;/p&gt;

&lt;h3&gt;
  
  
  Workflow and Architecture
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;1. Goal Definition:&lt;/strong&gt; The user opens the sidebar and sets a learning target (e.g., "Master Google Apps Script").&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;2. Context Retrieval:&lt;/strong&gt; The system scans the active Spreadsheet for past performance data. It identifies previously answered questions, success rates, and specific topic weaknesses (e.g., "Batch Operations").&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;3. Generative Logic (Gemini):&lt;/strong&gt; Using the context, Gemini generates a structured JSON response containing new, targeted questions and the specific UI components (radio buttons, code blocks) required to render them.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;4. Dynamic Rendering (A2UI):&lt;/strong&gt; The frontend constructs the interface instantly. No hard-coded HTML is required.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;5. Immediate Feedback &amp;amp; Persistence:&lt;/strong&gt; As the user interacts, results are validated in real-time and immediately logged back to the Spreadsheet, refining the dataset for the next session.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The detailed workflow operates as follows:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpe9ct0j9tp7xlccorkrr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpe9ct0j9tp7xlccorkrr.png" alt="fig2"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://mermaid.ai/play?utm_source=mermaid_live_editor&amp;amp;utm_medium=share#pako:eNqFVVtP4zgU_itHfkBFG7K0pVyiFVKHalj2wiztzq606oubnKYWiZ2xHZiC-O_7OUkLw7UPVROf23c57r1ITcYiEY6_1axTniiZW1nONeEja290XS7Yds-pN5a-us1zJa1Xqaqk9vTZGu1ZZyQdjQdfL6g3UxkvpN19GftJptdd6LkxecE0ripHs9Sqyr8M_5NLY9dPomcrZu-oN_n0WnErlW6CuVRaUe-cNVvp1Q3aXCChTbk0nsnccIsnatMS6sdoIgua8BLJXhlNO3QWoH33NGVvFd_Ioi0REvdOTzfIE_pSsaYONtIudFWHJDDr_C8L-_Npj-M8jmguzixL9Jf0rVZ3JBcGgefjGS2kU6mbiw7XpjS6dJwlNAvMhdb0lzXlhq_uGIEtW4hLpQ6KBdyuQr_MBdpoCQlXykHJ9RMmrMpXnsySNulnK06vHZh1nq52xhG5Ok3ZOQKV7KIGjsQkruJULVVK3lT4vmV5rRHGLm6rt_X2niIAjbXVHQa2GKiUsN6G5g8UGkChR0X_MDm67tBkrWWJX1N0YKt0_pyWLvs5e_QT_dqQoVKovp2gyQ0Zrw0-87ZO8Ysz-m325bJV9m9pc_Z4dRXUhm8cSmMNztDFaNbe7f440g_OacZq1mbKDvGOnxkA0WHq5CXQINoVXNSOMZWZMrSoPTbXRRS2mxaFgZQRsU_jj_w_jGFbD3bTzvsQyIGgcDe84foZF5xiHaV2twimrckbv8VgZSp1zr3d921dL0rladwWeUO9f2ShsmZxul47SJDF-o6pejTSB_KB4brwQR4IiWLBQE0V4HxrmWAz0nxLaC6pt5E46saNupq77y3UXGyp3F5pMxiP56KRrq4CMtfsp18x2uHGKYyp4g8kO4jpM3O2wNDg4xHTZSgw81y5d4zXcbLBTxMAfMt6reVaINul3TInkOtWCyNt9hoNjy0nylWFXLukgY0Ld5ymNQy3pikmb15ix3_nQI9lnfuVo4sMrOGa4aw5h0v_7S4aGofLxpU4b47AxqzOcwiEXZyLJyQIEpHIrcpEggXmSJQMDOFR3IeJ5wK0l5AjaJXxUkLRuZjrB6Thn-U_Y8pNpjV1vhLJUhYOT61y3T_n9q1t2DoztfYi6feP95sqIrkX30VychCPRsPjwdHB0Wh41D8aRmItkr3-_mHcPxkMRif9EQ6HD5G4a9ruxyeHR8Pjg_5wNBwN-ocnBw__A62jlR0" rel="noopener noreferrer"&gt;Mermaid Chart Playground&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In &lt;a href="https://github.com/google/A2UI/tree/d7996656ef2bc0cdffad499452fc5b282d878d45/samples/agent/adk/restaurant_finder" rel="noopener noreferrer"&gt;the official sample script for A2UI (Restaurant finder)&lt;/a&gt;, the client communicates with the server via A2A (Agent2Agent) protocols. However, in this article, we optimize the architecture for GAS. To reduce the overhead of HTTP requests associated with A2A communication, the client (HTML) communicates directly with the AI agent built on Google Apps Script using &lt;code&gt;google.script.run&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Demonstration: The AI Tutor in Action
&lt;/h2&gt;

&lt;p&gt;

  &lt;iframe src="https://www.youtube.com/embed/NzEyI9KWsAk"&gt;
  &lt;/iframe&gt;


&lt;/p&gt;

&lt;p&gt;The video demonstration highlights two distinct capabilities: Deep Learning Support and General Versatility. This demonstration utilizes a dialog within Google Sheets to highlight the integration with Google Apps Script. However, this application can easily be deployed as a standalone Web App. By deploying the project as a Web App, the &lt;code&gt;doGet&lt;/code&gt; function captures the request, allowing the interface to function independently of the spreadsheet UI.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Maximizing Learning Efficiency
&lt;/h3&gt;

&lt;p&gt;In the first segment, the user prompts: &lt;code&gt;Create a quiz about Google Apps Script basics. Especially, I want to manage Google Sheets using Google Apps Script.&lt;/code&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Targeted Question Generation:&lt;/strong&gt; The AI generates technical questions ranging from basic range selection (&lt;code&gt;sheet.getRange()&lt;/code&gt;) to advanced data handling (&lt;code&gt;sheet.getValues()&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Active Recall:&lt;/strong&gt; The system presents a challenge question on "Batch Operations" (&lt;code&gt;SpreadsheetApp.flush()&lt;/code&gt;) to test the user's depth of knowledge.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Comprehensive Performance Analysis:&lt;/strong&gt; The standout feature appears. A2UI renders a "Performance Analysis" dashboard.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Strengths Identified:&lt;/strong&gt; It acknowledges mastery in "Range &amp;amp; Data Manipulation."&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Weakness Assessment:&lt;/strong&gt; It confirms no current weaknesses were detected in this session.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Strategic Next Steps:&lt;/strong&gt; It suggests moving on to "Custom Menus" or "Simple Triggers," effectively guiding the user's curriculum path.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Note:&lt;/strong&gt; Upon completion of the initial quiz, using the same prompt triggers a new session where questions are generated based on the refined historical data.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The past data is stored in Google Sheets as follows.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fc35mc9r0foqnfax2g4we.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fc35mc9r0foqnfax2g4we.jpg" alt="fig3"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Versatility Verification: Restaurant Finder
&lt;/h3&gt;

&lt;p&gt;To prove the system's flexibility, we switch contexts entirely without changing code.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Prompt:&lt;/strong&gt; &lt;code&gt;Find 3 Chinese restaurants in New York&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Multimodal UI:&lt;/strong&gt; The agent renders rich cards with images for "Han Dynasty" and "RedFarm."&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Complex Action Handling:&lt;/strong&gt; The user clicks "Book Now," triggering a multi-field form (Party Size, Date, Dietary Requirements).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Significance:&lt;/strong&gt; This demonstrates that the A2UI protocol on GAS can handle complex transactional flows just as easily as educational logic.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Repository
&lt;/h2&gt;

&lt;p&gt;The full source code and sample implementation are available here:&lt;br&gt;
&lt;a href="https://github.com/tanaikech/A2UI-for-Google-Apps-Script/tree/master/samples/A2UI-Lerning-Agent" rel="noopener noreferrer"&gt;https://github.com/tanaikech/A2UI-for-Google-Apps-Script/tree/master/samples/A2UI-Lerning-Agent&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Application Setup Guide
&lt;/h2&gt;

&lt;p&gt;To deploy this adaptive learning agent in your environment:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Obtain an API Key&lt;/strong&gt;&lt;br&gt;
You must have a valid Gemini API Key. &lt;a href="https://ai.google.dev/gemini-api/docs/api-key" rel="noopener noreferrer"&gt;Get one here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Copy the Sample Script&lt;/strong&gt;&lt;br&gt;
Copy the pre-configured Spreadsheet containing the A2UI engine: &lt;strong&gt;&lt;a href="https://docs.google.com/spreadsheets/d/1eckORqs3JtTIJZTB0I5VpqduZw9g3-mM764s4neG7Fo/copy" rel="noopener noreferrer"&gt;https://docs.google.com/spreadsheets/d/1eckORqs3JtTIJZTB0I5VpqduZw9g3-mM764s4neG7Fo/copy&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Configure the Script&lt;/strong&gt;&lt;br&gt;
Open the script editor (Extensions &amp;gt; Apps Script), open &lt;code&gt;main.gs&lt;/code&gt;, and paste your API key into the &lt;code&gt;apiKey&lt;/code&gt; variable. Finally, save the script and reload the Spreadsheet.&lt;/p&gt;

&lt;h2&gt;
  
  
  Important Note
&lt;/h2&gt;

&lt;p&gt;This project serves as a foundational methodology for building Agentic UIs. When implementing this in a production environment, ensure the scripts are modified to meet your specific security requirements and workflow constraints.&lt;/p&gt;

&lt;p&gt;Users can also study multiple subjects concurrently. When switching topics, Gemini generates new questions by analyzing the historical data trends specific to that context, allowing for simultaneous progress across different fields.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Adaptive Learning Loop:&lt;/strong&gt; The system leverages Google Sheets to store user history, allowing Gemini to tailor future questions based on identified strengths and weaknesses.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dynamic UI Generation:&lt;/strong&gt; A2UI eliminates the need for static HTML templates, allowing the AI to generate quizzes, charts, and booking forms on-the-fly based on context.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Detailed Performance Analytics:&lt;/strong&gt; The agent provides qualitative feedback, not just scores, offering specific "Next Steps" to guide the user's learning path efficiently.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cross-Domain Versatility:&lt;/strong&gt; The demonstration proves the architecture is agnostic; it handles educational code quizzes and restaurant booking transactions with equal fluency.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Seamless Workspace Integration:&lt;/strong&gt; By combining GAS and Gemini, we transform standard Google Sheets into intelligent, persistent databases that drive agentic behavior.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>gemini</category>
      <category>a2ui</category>
      <category>googleworkspace</category>
      <category>googleappsscript</category>
    </item>
    <item>
      <title>Beyond Chatbots: Building Task-Driven Agentic Interfaces in Google Workspace with A2UI and Gemini</title>
      <dc:creator>Tanaike</dc:creator>
      <pubDate>Tue, 03 Feb 2026 05:07:07 +0000</pubDate>
      <link>https://forem.com/gde/beyond-chatbots-building-task-driven-agentic-interfaces-in-google-workspace-with-a2ui-and-gemini-c2d</link>
      <guid>https://forem.com/gde/beyond-chatbots-building-task-driven-agentic-interfaces-in-google-workspace-with-a2ui-and-gemini-c2d</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flz6wumc4n5xxgzt3ojng.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flz6wumc4n5xxgzt3ojng.jpg" alt="fig1"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Abstract
&lt;/h2&gt;

&lt;p&gt;This article explores A2UI (Agent-to-User Interface) using Google Apps Script and Gemini. By generating dynamic HTML via structured JSON, Gemini transforms Workspace into an "Agent Hub." This recursive UI loop enables complex workflows where the AI builds the specific functional tools required to execute tasks directly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Introduction: The Evolution of AI Interaction
&lt;/h2&gt;

&lt;p&gt;The &lt;a href="https://github.com/google/A2UI" rel="noopener noreferrer"&gt;Official A2UI framework&lt;/a&gt; by Google marks a significant paradigm shift in how we interact with artificial intelligence. Short for &lt;strong&gt;Agent-to-User Interface&lt;/strong&gt;, A2UI represents the evolution of Large Language Models (LLMs) from passive chatbots into active agents capable of designing their own functional interfaces. Building upon my previous research, &lt;a href="https://medium.com/google-cloud/a2ui-for-google-apps-script-bcd0f37a3774" rel="noopener noreferrer"&gt;A2UI for Google Apps Script&lt;/a&gt; and &lt;a href="https://medium.com/google-cloud/bringing-a2ui-to-google-workspace-with-gemini-0d85026969b8" rel="noopener noreferrer"&gt;Bringing A2UI to Google Workspace with Gemini&lt;/a&gt;, I have refined this integration to support sophisticated, stateful workflows.&lt;/p&gt;

&lt;p&gt;To appreciate the impact of A2UI, we must recognize the limitations of "Chat-centric" AI. In traditional chat interfaces, users must manually bridge the gap between an AI's advice and their actual files—a process often involving tedious context switching. By implementing A2UI within Google Apps Script (GAS), we leverage a unique &lt;strong&gt;"Home-Field Advantage."&lt;/strong&gt; Because GAS is native to the Google ecosystem, it possesses high-affinity access to the Drive API and Spreadsheet services, allowing the AI to act directly on your data.&lt;/p&gt;

&lt;h2&gt;
  
  
  Core Architecture: The Generative UI Loop
&lt;/h2&gt;

&lt;p&gt;In this system, Gemini functions as both the &lt;strong&gt;Agent and the UI Architect&lt;/strong&gt;. When a user submits a natural language prompt, the Agent evaluates the intent and generates a specific HTML interface—such as a file selector, a metadata card, or a live text editor.&lt;/p&gt;

&lt;p&gt;Crucially, this implementation utilizes &lt;strong&gt;Recursive UI Logic&lt;/strong&gt;. When a user interacts with a generated component (e.g., clicking an "OK" button), that action is transmitted back to the Agent as a &lt;strong&gt;"System Event."&lt;/strong&gt; This event contains the conversation history and the new data context. This allows the Agent to "see" the current state of the task and generate the next logical interface, creating a seamless, multi-step agentic workflow.&lt;/p&gt;

&lt;h3&gt;
  
  
  Workflow Visualization
&lt;/h3&gt;

&lt;p&gt;This diagram illustrates how the system maintains state and generates interfaces recursively using "System Events."&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzze74qvp6d5beddl3yqj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzze74qvp6d5beddl3yqj.png" alt="fig2"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://mermaid.live/edit#pako:eNqVU22v0jAU_isn_YRxIMjLhX24iXKHzotww-AazRJSt8No2FpsC4KE_-7ZBkS9Eq_7sLXreV7O0_bAIhUjc5nBbxuUEd4JnmiehRLoWXNtRSTWXFqYGdRP_w60khZlDBUhY9zVljZLXzwte8ujVVGVcSFriflLyTvMhBRQefPg02q5nmtWb2_PIi549NUGHrTK1hYqWEtqEDIfvucMVgHGwgKHhUgxZCeRM5qITjZcWGsVoTE5_yRv3NhKyenAe2Gs0vsT-IQgbOnPhQlyo6SQCbyEqVIp9HmalsVlSfVXoZk__xCMR1AZ-ENvHnhDrz8dT_4g_63FCb1RQ3-J0eqr2sGQ_FxJI8AUI2tgQO0astNPRbQyFMj4PmQlZqQsgtoS4RnmwLmT4HMw9T7OvUdvND2TCSWJaKxFIiSn3nLMzv5XkMHeWMy8LcpnpVnFLU83nGz6Obsl-YuVf8fq3fmUJ7yCR9_75D0n2Ck3q2qwxkgsRFTs4JV0L2nO1jHZCxkICR4dMKWv57Epauf5AZxHqmwoKg4Ic1iiRcxcqzfosAw13QWaskPOFjK7xIxEXBrGXK_yDTwShu7GF6WyM0yrTbJk7oKnhmal2unKXkqKPvtqIy1z2wUDcw9sx9zXN-1ap9vq9bqNZr3eazQdtmdup1fr3rR6nVa93mx3243m0WE_Csk6LbSPPwEdi1uB" rel="noopener noreferrer"&gt;Mermaid Chart Playground&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Repository
&lt;/h2&gt;

&lt;p&gt;The full source code and sample implementation can be found here:&lt;br&gt;
&lt;a href="https://github.com/tanaikech/A2UI-for-Google-Apps-Script" rel="noopener noreferrer"&gt;https://github.com/tanaikech/A2UI-for-Google-Apps-Script&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Application Setup Guide
&lt;/h2&gt;

&lt;p&gt;To deploy this application in your own environment, please follow these steps:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Obtain an API Key&lt;/strong&gt;&lt;br&gt;
You will need a valid Gemini API Key to communicate with the LLM.&lt;br&gt;
&lt;a href="https://ai.google.dev/gemini-api/docs/api-key" rel="noopener noreferrer"&gt;Get one here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Copy the Sample Script&lt;/strong&gt;&lt;br&gt;
You can copy the Google Spreadsheet containing the pre-configured Google Apps Script using the link below:&lt;br&gt;
&lt;strong&gt;&lt;a href="https://docs.google.com/spreadsheets/d/1UB5j-ySSBBsGJjSaKWpBPRYkokl7UtgYhDxqmYW00Vc/copy" rel="noopener noreferrer"&gt;https://docs.google.com/spreadsheets/d/1UB5j-ySSBBsGJjSaKWpBPRYkokl7UtgYhDxqmYW00Vc/copy&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Configure the Script&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Open the script editor (Extensions &amp;gt; Apps Script).&lt;/li&gt;
&lt;li&gt;Locate the &lt;code&gt;main.gs&lt;/code&gt; file.&lt;/li&gt;
&lt;li&gt;Set your API key in the &lt;code&gt;GEMINI_API_KEY&lt;/code&gt; variable.&lt;/li&gt;
&lt;li&gt;Save the project.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Alternatively, visit the &lt;a href="https://github.com/tanaikech/A2UI-for-Google-Apps-Script/tree/master/samples/A2UI-Drive-Task-Agent" rel="noopener noreferrer"&gt;GitHub Repository&lt;/a&gt; to manually copy the source codes.&lt;/p&gt;
&lt;h2&gt;
  
  
  Demonstration: Productivity Meets Magic
&lt;/h2&gt;

&lt;p&gt;The following video showcases how A2UI transforms a Google Sheet into an agentic command center. The system doesn't just talk; it guides the user through three distinct patterns of interaction.&lt;/p&gt;

&lt;p&gt;

  &lt;iframe src="https://www.youtube.com/embed/6oIJGyn-9TU"&gt;
  &lt;/iframe&gt;


&lt;/p&gt;

&lt;h3&gt;
  
  
  Operational Patterns: Productivity in Action
&lt;/h3&gt;

&lt;p&gt;The system transforms a standard Google Sheet into an agentic command center. It facilitates three distinct patterns of interaction:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pattern 1: Intelligent Viewing&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Sample prompt:&lt;/strong&gt; &lt;code&gt;Please list the files in the folder named 'sample'. I would like to select a file and view its content.&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;The user requests to see files in a specific folder. Gemini understands the intent, calls the Drive API to list the files, and generates a &lt;strong&gt;File Selector UI&lt;/strong&gt;. Once the user selects files, the Agent fetches the content and renders it in a clean &lt;strong&gt;Content Viewer&lt;/strong&gt; layout designed specifically for reading.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pattern 2: Contextual Metadata Analysis&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Sample prompt:&lt;/strong&gt; &lt;code&gt;Show me the files in the 'sample' folder. I need to select a file to check its metadata.&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;If a user asks for technical details, the UI adapts. The Agent generates a &lt;strong&gt;Metadata Viewer&lt;/strong&gt;, displaying properties like File IDs, sizes, and creation dates. This showcases the agent hub's ability to pivot between task types by generating appropriate interfaces on the fly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pattern 3: Multi-Step "Verify and Edit"&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Sample prompt:&lt;/strong&gt; &lt;code&gt;I want to edit a file in the 'sample' folder. Please let me select a file and check its content first. If it's the right one, I will edit and update it.&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;This demonstrates the power of stateful A2UI:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Selection Preview:&lt;/strong&gt; The Agent provides a preview with radio buttons for content confirmation.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dynamic Editor:&lt;/strong&gt; Gemini generates an &lt;strong&gt;Editor UI&lt;/strong&gt; containing the file’s text.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Real-Time Execution:&lt;/strong&gt; The script executes modifications directly to Google Drive upon clicking "Update," completing the cycle from prompt to action.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;em&gt;Note: In this specific sample, only text files on Google Drive are eligible for editing.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Important Note
&lt;/h2&gt;

&lt;p&gt;This project serves as a foundational methodology for building Agentic UIs. When implementing this in a production environment, ensure the scripts are modified to meet your specific security requirements and workflow constraints.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;A2UI (Agent-to-User Interface) represents a paradigm shift where the Agent builds the functional UI components required for a task rather than just providing text.&lt;/li&gt;
&lt;li&gt;The recursive task execution model uses "System Events" to track progress, allowing the interface to evolve dynamically based on real-time user actions.&lt;/li&gt;
&lt;li&gt;Native Workspace integration via Google Apps Script provides secure, high-speed access to Drive and Sheets data without the need for external server management.&lt;/li&gt;
&lt;li&gt;Zero-Tab efficiency is achieved by consolidating file discovery, analysis, and editing within a single, dynamic dialog box inside a spreadsheet.&lt;/li&gt;
&lt;li&gt;This task-driven architecture proves the future of productivity lies in AI agents acting as architects, creating custom tools precisely when they are needed.&lt;/li&gt;
&lt;/ol&gt;

</description>
      <category>gemini</category>
      <category>a2ui</category>
      <category>googleworkspace</category>
      <category>googleappsscript</category>
    </item>
    <item>
      <title>Smart Stowage: Building a 3D Cargo Digital Twin with Gemini 3</title>
      <dc:creator>Tanaike</dc:creator>
      <pubDate>Wed, 21 Jan 2026 07:19:36 +0000</pubDate>
      <link>https://forem.com/gde/smart-stowage-building-a-3d-cargo-digital-twin-with-gemini-3-4473</link>
      <guid>https://forem.com/gde/smart-stowage-building-a-3d-cargo-digital-twin-with-gemini-3-4473</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzcf9tzd3lqqgwjcnx6qp.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzcf9tzd3lqqgwjcnx6qp.jpg" alt="fig1"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Abstract
&lt;/h2&gt;

&lt;p&gt;This article details the development of &lt;strong&gt;Smart Stowage Optimizer&lt;/strong&gt;, a web-based digital twin for logistics that bridges the gap between physical safety and artificial intelligence. By integrating &lt;strong&gt;Gemini 3 Pro&lt;/strong&gt;, the system solves the &lt;strong&gt;3D Bin Packing Problem (3DBPP)&lt;/strong&gt; using advanced spatial reasoning. Built with &lt;strong&gt;React 19&lt;/strong&gt; and &lt;strong&gt;Three.js&lt;/strong&gt;, the application visualizes physics-aware load stability in real-time, offering a comparative analysis between traditional heuristic algorithms and modern generative AI agents.&lt;/p&gt;

&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;The capabilities of Large Language Models (LLMs) are expanding at an unprecedented rate, moving from text generation to complex reasoning. When Gemini 2.5 was released, I conducted a feasibility study on automating cargo ship stowage planning, published in &lt;em&gt;"Stowage Planning Automation Using Gemini: A Feasibility Study and A Prompt-Based Approach"&lt;/em&gt; &lt;a href="https://medium.com/google-cloud/stowage-planning-automation-using-gemini-a-feasibility-study-and-a-prompt-based-approach-af8dd264e35d" rel="noopener noreferrer"&gt;Ref&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;At that time, the implementation was a backend Python script producing static JSON results. While effective as a proof of concept, real-world logistics demands interactive visualization and immediate feedback on physical constraints like the Center of Gravity (COG).&lt;/p&gt;

&lt;p&gt;With the arrival of &lt;strong&gt;Gemini 3&lt;/strong&gt;, we can now handle significantly more complex spatial reasoning tasks. This article introduces the &lt;strong&gt;Smart Stowage Optimizer&lt;/strong&gt;, a full-stack Web Application that evolves the previous concept into a live &lt;strong&gt;3D Digital Twin&lt;/strong&gt;. This application not only calculates optimal packing but also visualizes stability and safety constraints in a high-fidelity 3D environment.&lt;/p&gt;

&lt;h2&gt;
  
  
  Repository
&lt;/h2&gt;

&lt;p&gt;You can access the full source code here:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/tanaikech/Smart-Stowage-Optimizer" rel="noopener noreferrer"&gt;https://github.com/tanaikech/Smart-Stowage-Optimizer&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  System Architecture
&lt;/h2&gt;

&lt;p&gt;To tackle the &lt;strong&gt;3D Bin Packing Problem (3DBPP)&lt;/strong&gt;—a known NP-hard optimization challenge—I designed a &lt;strong&gt;dual-engine architecture&lt;/strong&gt;. This allows users to compare the speed of deterministic algorithms against the semantic reasoning of modern AI.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Frontend&lt;/strong&gt;: React 19, Tailwind CSS, Lucide React&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Visualization&lt;/strong&gt;: React Three Fiber (Three.js), InstancedMesh for performance&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AI Logic&lt;/strong&gt;: Google GenAI SDK (Gemini 3 Pro)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Algorithms&lt;/strong&gt;: Custom greedy heuristic implementation&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;Below is the high-level data flow of the application. You can generate a similar diagram using the prompt below.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;[Diagram Suggestion: System Architecture Flowchart]&lt;/strong&gt;&lt;br&gt;
&lt;em&gt;Prompt for Diagram Generation: "Create a flowchart showing a React Frontend sending cargo data to two branches: one to a Local Heuristic Engine and one to the Gemini API. Both branches merge back into a Validation Layer which calculates Physics (COG), and finally outputs to a 3D Renderer."&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyopl7ucsgnlp06axqe6j.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyopl7ucsgnlp06axqe6j.jpg" alt="fig2"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://mermaidchart.com/play?utm_source=mermaid_live_editor&amp;amp;utm_medium=share#pako:eNpVUcFy2jAQ_ZUdHXoiGajBBR8yQ2xioJMmE9IcanJQ7MXeji0xktyGAP_exTYTqoNGq31v9-3bvUh1hiIQuZHbAp6jtQI-0-SnRQMLta1dAPdS0Qatgy-w2soUX-Hq6gZu9yssMXUwUzkpPLbM21PuMC1zbcgVFaVwzw0OECZzrA1Zxz-xQcx2sNLlHzSv__EWHTxKYqxIEXjwaDRMc1SuQ7a3rd9ayWvBpCeUVitSOYTa4Fq0mKjROUtCrRy-Ox7nN-slrbpKsyZ_l_BQjmTJXGWdkaSc7RB3DSJOlquHHxCjQiMv-KiyS0lhA55zO20yUtIhvMiSMun0ecq4hVyy5s3XInksdpZS27nZ4ReNK6EsUwgf4pP_Tr5RSW53gGXiRRBRTo6lP_-ls6plU_B7u8EXsjVr-DjLFj3eNGUicKbGnqjQVPIUiv2JvBauwIr9C_iZ4UbWpTuZeWTaVqpfWldnptF1XohgI0vLUb3lKTEiyTv5hLA_aEJdKyeCr4OmhAj24p0j37_2x8PJZDzw-v3JwOuJnQj8yfX423DiD_t9bzQeDbxjT3w0PfucGB3_AYj0028" rel="noopener noreferrer"&gt;Mermaid Chart Playground&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Methodology: Dual-Engine Optimization
&lt;/h2&gt;
&lt;h3&gt;
  
  
  1. The Deterministic Approach: Algorithmic Engine
&lt;/h3&gt;

&lt;p&gt;The first engine utilizes a &lt;strong&gt;Greedy Shelf-Packing Heuristic&lt;/strong&gt;, specifically a variation of the &lt;em&gt;First-Fit Decreasing Height (FFDH)&lt;/em&gt; algorithm.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Logic&lt;/strong&gt;: It sorts cargo items by height to define "shelves" or layers.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Execution&lt;/strong&gt;: Items are packed sequentially along the X and Z axes, filling one layer before moving to the next.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pros/Cons&lt;/strong&gt;: This method is computationally inexpensive and ensures a tidy lineup. However, it lacks "common sense"—it cannot understand complex constraints like "do not stack heavy machine parts on top of fragile electronics" unless explicitly hard-coded.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  2. The Probabilistic Approach: Gemini 3 Pro Engine
&lt;/h3&gt;

&lt;p&gt;The second engine leverages the &lt;strong&gt;Spatial Reasoning&lt;/strong&gt; capabilities of Gemini 3 Pro. Instead of iterating through a fixed loop, the AI evaluates the entire manifest holistically.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Why Gemini 3?&lt;/strong&gt;: Previous models struggled with 3D coordinate mapping. Gemini 3 Pro shows a marked improvement in maintaining spatial relationships, allowing it to "visualize" the container interior.&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Workflow&lt;/strong&gt;: The application prompts the model to act as a Senior Stowage Engineer.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Constraints&lt;/strong&gt;: The prompt enforces non-linear rules:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Stability&lt;/strong&gt;: Keep the Center of Gravity (COG) low.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Safety&lt;/strong&gt;: Heavier items must be placed at &lt;code&gt;y=0&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Balance&lt;/strong&gt;: Lateral and longitudinal distribution for truck axles.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;
  
  
  Implementation: Structured JSON Output
&lt;/h4&gt;

&lt;p&gt;To ensure the AI returns usable data, we utilize the &lt;code&gt;responseMimeType: "application/json"&lt;/code&gt; configuration in the Google GenAI SDK. This forces the model to adhere to a strict schema, returning a typed array of coordinates rather than conversational text.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// services/geminiService.ts snippet&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;ai&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;models&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;generateContent&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;gemini-3-pro-preview&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;contents&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;config&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;responseMimeType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;application/json&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;responseSchema&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Type&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;OBJECT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;properties&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;packedItems&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Type&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ARRAY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;items&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Type&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;OBJECT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;properties&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
              &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Type&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;STRING&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
              &lt;span class="na"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Type&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;NUMBER&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
              &lt;span class="na"&gt;y&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Type&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;NUMBER&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
              &lt;span class="na"&gt;z&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Type&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;NUMBER&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
            &lt;span class="p"&gt;},&lt;/span&gt;
            &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;id&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;x&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;y&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;z&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
          &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Safety &amp;amp; Physics Validation
&lt;/h4&gt;

&lt;p&gt;AI is probabilistic, but cargo safety must be deterministic. Therefore, the application includes a validation layer:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Boundary Clamping&lt;/strong&gt;: If the AI suggests a coordinate where an item clips through the wall ($x + width &amp;gt; container_width$), the system automatically clamps the item to the boundary.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;COG Calculation&lt;/strong&gt;: The app calculates the weighted average position of all items ($COG = \frac{\sum (m_i \times p_i)}{\sum m_i}$) and renders a red visual marker in the 3D view. This allows engineers to verify stability instantly.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Application Overview: Smart Stowage Optimizer
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Smart Stowage Optimizer&lt;/strong&gt; acts as a sandbox to simulate loading risks before physical operations begin.&lt;/p&gt;

&lt;h3&gt;
  
  
  Key Features
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Digital Twin Visualization&lt;/strong&gt;: Using &lt;code&gt;react-three-fiber&lt;/code&gt;, the scene renders a semi-transparent container with a grid floor. We use &lt;code&gt;InstancedMesh&lt;/code&gt; for the cargo items, allowing the app to render hundreds of boxes with a single GPU draw call for high performance.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Physics-Aware Metrics&lt;/strong&gt;: The UI displays real-time volumetric efficiency (%) and total payload weight, changing colors to warn of overloading.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Manifest Management&lt;/strong&gt;: Users can upload CSV files, manually add items, or use industry-standard presets (20ft/40ft ISO containers, EURO Trucks).&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Comparative Analysis
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;Algorithmic (Heuristic)&lt;/th&gt;
&lt;th&gt;Gemini AI Engine&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Speed&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Instantaneous (&amp;lt;100ms)&lt;/td&gt;
&lt;td&gt;3-8 seconds (Inference)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Logic&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Deterministic (Fixed Rules)&lt;/td&gt;
&lt;td&gt;Probabilistic (Reasoning)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Stability&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Basic Height-based layers&lt;/td&gt;
&lt;td&gt;Advanced COG awareness&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Flexibility&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Rigid packing patterns&lt;/td&gt;
&lt;td&gt;Adapts to unique constraints&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Best For&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Uniform pallets, high speed&lt;/td&gt;
&lt;td&gt;Complex manifests, fragile items&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Installation and Usage
&lt;/h2&gt;

&lt;p&gt;To run the application locally:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Clone the repository:&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/tanaikech/Smart-Stowage-Optimizer
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Install dependencies:&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Configure API Key:&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Set your Gemini API key as an environment variable &lt;code&gt;GEMINI_API_KEY&lt;/code&gt; in a &lt;code&gt;.env&lt;/code&gt; file.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Start the server:&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm run dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Workflow
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Define Space&lt;/strong&gt;: Select a preset (e.g., "EURO_TRUCK") or define custom dimensions (W/H/L).&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Build Manifest&lt;/strong&gt;: Upload a CSV or use the "Sample" button to populate test cargo (generators, drums, electronics).&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Select Engine&lt;/strong&gt;: Choose &lt;strong&gt;Gemini AI&lt;/strong&gt; for stability-focused packing or &lt;strong&gt;Algorithmic&lt;/strong&gt; for speed.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Analyze&lt;/strong&gt;: Rotate the 3D view. Check the red COG marker to ensure it is centered and low.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Export&lt;/strong&gt;: Download the generated coordinates as a CSV report for the loading crew.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Testing and Demonstration
&lt;/h2&gt;

&lt;p&gt;The following demonstration highlights the difference between the engines. Note how the Algorithmic engine packs tightly but may ignore weight distribution. In contrast, the Gemini engine intelligently places heavier items (blue/green generators) at the bottom and lighter items (orange electronics) on top to lower the Center of Gravity.&lt;/p&gt;

&lt;p&gt;

  &lt;iframe src="https://www.youtube.com/embed/5yVJ_uyzKHQ"&gt;
  &lt;/iframe&gt;


&lt;/p&gt;

&lt;p&gt;The exported CSV data generated by Gemini is structured as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ID,Name,Width,Height,Length,Weight,X,Y,Z
BASE-1,Generator 1,1.2,1,2,2500,0,0,0
BASE-2,Generator 2,1.2,1,2,2500,0,0,2
PAL-1,Heavy Spares,1,0.8,1.2,1200,1.35,0,0
PAL-2,Heavy Spares,1,0.8,1.2,1200,1.35,0,1.2
BOX-A1,Electronics,0.6,0.6,0.8,150,0,1,0
BOX-A2,Electronics,0.6,0.6,0.8,150,0.6,1,0
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;By integrating &lt;strong&gt;Gemini 3 Pro&lt;/strong&gt;, we have moved beyond simple script-based calculators to a fully interactive &lt;strong&gt;Digital Twin&lt;/strong&gt;. While traditional algorithms are faster for uniform shapes, Gemini demonstrates a unique ability to understand "soft" constraints—such as stability and stackability—that are difficult to program explicitly. This hybrid approach represents the future of logistics software: combining the speed of heuristics with the reasoning of Artificial Intelligence.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Hybrid Architecture&lt;/strong&gt;: Successfully combines deterministic Heuristics with probabilistic AI.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Gemini 3 Capability&lt;/strong&gt;: Confirms that Gemini 3 Pro handles complex spatial reasoning and 3D coordinate mapping significantly better than previous iterations.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Visualization Impact&lt;/strong&gt;: React 19 and Three.js transform abstract data into visual insights, allowing for immediate safety verification.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Physics Integration&lt;/strong&gt;: Real-time Center of Gravity (COG) calculations ensure that AI-generated plans adhere to physical safety standards.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>gemini</category>
      <category>react</category>
      <category>ai</category>
      <category>node</category>
    </item>
    <item>
      <title>Bringing A2UI to Google Workspace with Gemini</title>
      <dc:creator>Tanaike</dc:creator>
      <pubDate>Mon, 19 Jan 2026 05:54:51 +0000</pubDate>
      <link>https://forem.com/gde/bringing-a2ui-to-google-workspace-with-gemini-4m24</link>
      <guid>https://forem.com/gde/bringing-a2ui-to-google-workspace-with-gemini-4m24</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmrp5nfgqzuggxtat755p.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmrp5nfgqzuggxtat755p.jpg" alt="fig1"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Abstract
&lt;/h2&gt;

&lt;p&gt;This article explores implementing the Agent-to-User Interface (A2UI) protocol within Google Apps Script. It demonstrates utilizing Gemini's structured output to render secure, dynamic, server-driven UIs—like booking forms and event lists—directly inside Google Sheets, streamlining workflows without complex external infrastructure.&lt;/p&gt;

&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;I recently published a sample implementation demonstrating how to bring the Agent-to-User Interface (A2UI) concepts to Google Apps Script (GAS). &lt;a href="https://medium.com/google-cloud/a2ui-for-google-apps-script-bcd0f37a3774" rel="noopener noreferrer"&gt;Ref&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A2UI is an emerging open-standard protocol designed to enable AI agents to generate rich, interactive user interfaces that render natively across web, mobile, and desktop environments. &lt;a href="https://developers.googleblog.com/introducing-a2ui-an-open-project-for-agent-driven-interfaces/" rel="noopener noreferrer"&gt;Ref&lt;/a&gt; Unlike traditional approaches that often require executing arbitrary, high-risk code to generate UI on the fly, A2UI leverages a strict schema-based data format to describe UI components. This "secure-by-design" architecture effectively mitigates security risks like Cross-Site Scripting (XSS) while ensuring high performance and cross-platform consistency.&lt;/p&gt;

&lt;p&gt;Integrating A2UI into the Google Workspace ecosystem represents a paradigm shift in user experience. It allows Large Language Model (LLM) driven agents to evolve beyond simple text-based chat. Instead, they provide dynamic, actionable components—such as forms, interactive charts, and data grids—directly within the user's workflow. In this article, I present a demonstration of A2UI functioning within Google Sheets, illustrating how AI agents can orchestrate complex, data-driven tasks through a native, structured interface.&lt;/p&gt;

&lt;h2&gt;
  
  
  Repository
&lt;/h2&gt;

&lt;p&gt;The complete source code and documentation are available in the GitHub repository below:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/tanaikech/A2UI-for-Google-Apps-Script" rel="noopener noreferrer"&gt;https://github.com/tanaikech/A2UI-for-Google-Apps-Script&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  System Architecture and Workflow
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Ftanaikech.github.io%2Fimage-storage%2F20260119a%2Ffig2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Ftanaikech.github.io%2Fimage-storage%2F20260119a%2Ffig2.png" alt="fig2"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://mermaidchart.com/play?utm_source=mermaid_live_editor&amp;amp;utm_medium=share#pako:eNptk1tP20AQhf_KaJ-CGlxjQi77gJQmkBjRiwh5qSxViz0x29q77l5o0ij_vbNxQkHgF3t3vznnzNjeslwXyDiz-NujynEqRWlEnSmgS3inla8f0LTrRhgnc9kI5WAJwsLSvnc0D0fz-8-3QGqVLqEzTpbpyVtwFsDZeAGfRP4LVfGWGKd7BGupJIy_pW-JaQCmwgnoLB4Rnf04ERVpCUOGLb48vbycc0hV4x1Qm2YDHYzKqAsZu5aqAIPWCW9IzmbskHNORTMOpdZlhZHNjWxcZLyKGqNztDa0fheGZl3nUDKjknHKYUH2B58PcK91BVNcUQNOamVbdJy28nfovFFw7VUeToGyV8dwJbofL5K9MJlyuFpj7h1CETo36IzEJ1G1yPT0lbgRf_bYOyFdCEcevnIWVtrAMiVbhUaENM9RX8lNtHpCY_eEqOAe147aDG8YbhZfvxxd2pkfI6BtqHcE_fATc_d_woTcLOBKlVIhUaog5VZromsqwfBKDjThS06flG0qsYFio0Qt85C4MxGmsF24ldbRDV0enYQSYF1WGlkw7ozHLqvR1CIs2TZoZsw9Yo0Z4_RY4ErQFDKWqR2V0af1Xev6WGm0Lx8ZX4nK0so3NM7jn_K826afaK8c40nS34swvmVrxvvDKB4NzuNkmPTP-kmvyzaMn_VodzDqxXES90f9YW_XZX_3pnE0HPRGdPXi89HF-cVFsvsH5c4oIA" rel="noopener noreferrer"&gt;Mermaid Chart Playground&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The implementation of A2UI within Google Sheets follows a highly optimized lifecycle to ensure a smooth, agent-driven user experience.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;User Input:&lt;/strong&gt; The process begins in the Google Spreadsheet dialog where a user enters a natural language request, such as "Find 3 Chinese restaurants in New York."&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Request Transmission:&lt;/strong&gt; The HTML interface captures this input and sends it to the GAS backend. This is handled via the &lt;code&gt;google.script.run&lt;/code&gt; API, which creates a direct and secure bridge between the user interface and the server logic.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Intent Routing:&lt;/strong&gt; The GAS backend forwards the request to the Gemini API. Using a specialized System Prompt, Gemini acts as a "Router" to determine which sub-application (e.g., Restaurant Finder or Event Manager) is best suited to handle the request.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tool Execution:&lt;/strong&gt; Once the sub-application is selected, GAS executes local functions to interact with your data. This may involve searching the current spreadsheet, fetching files from Google Drive, or checking Google Calendar availability.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A2UI JSON Generation:&lt;/strong&gt; The data retrieved from the tools is sent back to Gemini. Gemini then generates a structured response that includes both conversational text and a JSON object strictly following the A2UI (Agent-to-User Interface) schema.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Client-Side Rendering:&lt;/strong&gt; The GAS backend returns this payload to the HTML dialog. A dedicated JavaScript rendering engine within the dialog parses the A2UI JSON and dynamically builds high-quality UI components like interactive cards, lists with images, and booking forms.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Native Integration:&lt;/strong&gt; Unlike standard A2UI implementations that often rely on the A2A (Agent-to-Agent) protocol over HTTP, this GAS-optimized architecture eliminates external network overhead. By using the internal Google infrastructure, it provides a significantly faster response time for users.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Architectural Comparison: Distributed vs. Integrated
&lt;/h2&gt;

&lt;p&gt;The official A2UI is designed as a "distributed" system for cross-platform interoperability. In contrast, the GAS-native version is an "integrated" system optimized specifically for the Google infrastructure.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Official A2UI Workflow (via A2A Protocol)&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Process:&lt;/strong&gt; The Frontend requests data from an A2A Server (e.g., Node.js/Python). The Server handles auth/protocol logic and requests JSON from the Gemini API. The generated JSON travels back through the server to the Frontend for rendering.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Challenge:&lt;/strong&gt; Multiple network hops across the internet introduce latency and overhead.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;GAS Native Workflow (via google.script.run)&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Process:&lt;/strong&gt; The Frontend (HTML) directly calls a GAS Backend function using &lt;code&gt;google.script.run&lt;/code&gt;. The GAS Backend communicates with the Gemini API using &lt;code&gt;UrlFetchApp&lt;/code&gt; to get the UI JSON. The script returns the object directly to the client for immediate rendering.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Innovation:&lt;/strong&gt; By using &lt;code&gt;google.script.run&lt;/code&gt;, the entire communication happens within Google's internal high-speed network. There is no need for an external A2A server, eliminating external network overhead.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Analysis: Pros and Cons of GAS-based A2UI
&lt;/h2&gt;

&lt;p&gt;Why should users choose the GAS-native approach for their AI applications?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pros&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Zero Setup Cost &amp;amp; Rapid Development:&lt;/strong&gt; No servers to manage. Backend, frontend, and AI integration all live in a single GAS project.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Seamless Google Integration:&lt;/strong&gt; It is incredibly easy to let the AI perform "real-world actions," such as using Google Sheets as a database or managing Google Calendar events.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;High Performance:&lt;/strong&gt; Bypassing an external proxy server via &lt;code&gt;google.script.run&lt;/code&gt; ensures a snappier, more responsive user experience.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Cons&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Manual Rendering Logic:&lt;/strong&gt; While the official version provides out-of-the-box UI components, the GAS version requires you to maintain the logic that converts JSON to HTML elements (the &lt;code&gt;renderComponent&lt;/code&gt; code).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Execution Quotas:&lt;/strong&gt; GAS has specific limits, such as a 6-minute execution timeout and daily triggers, which may not suit high-scale enterprise apps.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Application Setup
&lt;/h2&gt;

&lt;p&gt;To deploy this application in your own environment, please follow the steps below.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Obtain an API Key&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;You will need a valid Gemini API Key to communicate with the LLM.&lt;br&gt;
&lt;a href="https://ai.google.dev/gemini-api/docs/api-key" rel="noopener noreferrer"&gt;Get one here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Copy the Sample Script&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;You can copy the Google Spreadsheet containing the necessary Google Apps Script using the link below.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://docs.google.com/spreadsheets/d/1NdgN5e2l7-CTw-NTaP50Ta75l8Zbr93ATMyUOlZk1BY/copy" rel="noopener noreferrer"&gt;https://docs.google.com/spreadsheets/d/1NdgN5e2l7-CTw-NTaP50Ta75l8Zbr93ATMyUOlZk1BY/copy&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;After copying the Google Spreadsheet:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Open the script editor (Extensions &amp;gt; Apps Script).&lt;/li&gt;
&lt;li&gt;Locate the &lt;code&gt;main.gs&lt;/code&gt; file.&lt;/li&gt;
&lt;li&gt;Set your API key in the &lt;code&gt;apiKey&lt;/code&gt; variable.&lt;/li&gt;
&lt;li&gt;Save the script.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Alternatively, you can visit the &lt;a href="https://github.com/tanaikech/A2UI-for-Google-Apps-Script/tree/master/samples/A2UI-Google-Sheets" rel="noopener noreferrer"&gt;GitHub Repository&lt;/a&gt; to manually copy the source codes.&lt;/p&gt;
&lt;h2&gt;
  
  
  Demonstration and Testing
&lt;/h2&gt;

&lt;p&gt;Once the script is set up, re-open the Google Spreadsheet (or refresh the page). You will see a table on the "data" sheet, which acts as the source for the event demonstration.&lt;/p&gt;

&lt;p&gt;A custom menu labeled "sample" will appear in the toolbar. Select &lt;strong&gt;sample &amp;gt; run&lt;/strong&gt; to open the A2UI dialog.&lt;/p&gt;

&lt;p&gt;

  &lt;iframe src="https://www.youtube.com/embed/aSetL-QF2I0"&gt;
  &lt;/iframe&gt;


&lt;/p&gt;

&lt;p&gt;In this demonstration, you can test the following two prompts:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. "Find 3 Chinese restaurants in New York"&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This prompt replicates the official A2UI demonstration. It tests the system's ability to retrieve mock data, render a list of restaurants with images, and present a booking form.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. "Show me events for Jan 17-20"&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This prompt demonstrates a custom integration with Google Workspace. The agent searches the spreadsheet for events within the specified date range and presents them as a selectable list. Upon confirmation, the agent triggers a function to register the selected events directly to your Google Calendar.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;This project confirms that the A2UI protocol can be effectively implemented within the Google Apps Script environment.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Serverless Architecture:&lt;/strong&gt; A2UI can be deployed using only GAS and Google Sheets, eliminating the need for external web servers or hosting.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Deep Workspace Integration:&lt;/strong&gt; The system seamlessly connects Generative AI with Workspace tools, allowing actions like reading Sheets and writing to Calendar.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Enhanced Security:&lt;/strong&gt; By adhering to A2UI's schema-driven rendering, the application avoids the high risks associated with generating and executing arbitrary HTML/JS code.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dynamic User Experience:&lt;/strong&gt; Users receive interactive, app-like interfaces (forms, buttons, lists) directly within a chat dialog, vastly improving usability over plain text.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scalability to other Apps:&lt;/strong&gt; This approach is not limited to Sheets; it can be readily adapted for Google Docs, Slides, and other Workspace applications to create a unified AI interface.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>gemini</category>
      <category>googleworkspace</category>
      <category>googleappsscript</category>
      <category>a2ui</category>
    </item>
    <item>
      <title>Seamless Integration of Google Workspace and Gemini API via External URLs</title>
      <dc:creator>Tanaike</dc:creator>
      <pubDate>Thu, 15 Jan 2026 05:54:58 +0000</pubDate>
      <link>https://forem.com/gde/seamless-integration-of-google-workspace-and-gemini-api-via-external-urls-4he</link>
      <guid>https://forem.com/gde/seamless-integration-of-google-workspace-and-gemini-api-via-external-urls-4he</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpxxj7ht1s9chmx576qp8.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpxxj7ht1s9chmx576qp8.jpg" alt="fig1" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Abstract
&lt;/h2&gt;

&lt;p&gt;The Gemini API now supports external file URLs, allowing developers to process data directly without uploading it first. This article demonstrates how to leverage this update to integrate Google Workspace resources—including Google Sheets, Docs, Slides, and Apps Script—into Gemini’s workflow, covering both public and secure private access methods.&lt;/p&gt;

&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Recently, the limitations regarding inline file data in the Gemini API have been significantly updated &lt;a href="https://blog.google/innovation-and-ai/technology/developers-tools/gemini-api-new-file-limits/" rel="noopener noreferrer"&gt;Ref&lt;/a&gt;. The maximum file size has increased from 20 MB to 100 MB. Furthermore, external file URLs—both public and signed—can now be used directly as input.&lt;/p&gt;

&lt;p&gt;The ability to use public external file URLs is particularly significant. Previously, data had to be uploaded to Gemini's servers first to generate an internal URI for processing. Eliminating this step substantially reduces processing overhead and latency.&lt;/p&gt;

&lt;p&gt;It is considered that this update effectively bridges the gap between the Gemini API and the Google Workspace ecosystem. This article investigates the technical specifications of these external file URLs and explores how they enhance interoperability with Google Sheets, Docs, Slides, and Google Apps Script.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;To test the samples provided in this article, you will need a valid Gemini API Key.&lt;br&gt;
&lt;a href="https://ai.google.dev/gemini-api/docs/api-key" rel="noopener noreferrer"&gt;Get an API Key here&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  Integration with Google Workspace Files (Sheets, Docs, Slides)
&lt;/h2&gt;

&lt;p&gt;Google Sheets is a representative resource within Google Workspace. In this section, we demonstrate how to use Google Sheets (and by extension, Docs and Slides) as an external URL source for the Gemini API.&lt;/p&gt;
&lt;h3&gt;
  
  
  1. Setting up the Google Sheet
&lt;/h3&gt;

&lt;p&gt;Please create a new Google Spreadsheet and populate it with sample data. The image below shows the sample table used in this demonstration.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftiyfvb5nla0co882zo0v.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftiyfvb5nla0co882zo0v.jpg" alt="fig2" width="800" height="481"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For the initial test, share this Google Spreadsheet publicly as "Anyone with the link can view". &lt;a href="https://support.google.com/a/users/answer/13309904?hl=en#sheets_share_link" rel="noopener noreferrer"&gt;Ref&lt;/a&gt; Copy the Spreadsheet ID from the URL bar.&lt;/p&gt;
&lt;h3&gt;
  
  
  2. Client Script for Public URLs
&lt;/h3&gt;

&lt;p&gt;Here, Node.js is used as the client environment. &lt;a href="https://ai.google.dev/gemini-api/docs/file-input-methods#external-urls" rel="noopener noreferrer"&gt;Ref&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Please replace &lt;code&gt;###&lt;/code&gt; with your actual Spreadsheet ID in the &lt;code&gt;spreadsheetId&lt;/code&gt; variable.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;GoogleGenAI&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;createPartFromUri&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@google/genai&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;spreadsheetId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;###&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ai&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;GoogleGenAI&lt;/span&gt;&lt;span class="p"&gt;({});&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Construct the URL to export the Sheet as a PDF&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;uri&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`https://docs.google.com/spreadsheets/d/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;spreadsheetId&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/export?format=pdf`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;ai&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;models&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;generateContent&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;gemini-3-flash-preview&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;contents&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="nf"&gt;createPartFromUri&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;uri&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;application/pdf&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Describe the PDF data.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When this script is executed, the Gemini API directly accesses the PDF export URL of the Spreadsheet. The result confirms that the model can interpret the current state of the Spreadsheet:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;The PDF contains a table for tracking customer feedback, including manual input and AI-generated analysis. The table consists of five columns: **Date**, **Purchase ID**, **Feedback**, **AI sentiment**, and **AI category**.

There are three recorded entries of customer feedback:

1.  **October 12, 2024 (ID: 82736481):** A highly positive review for a custom-made pendant, noted for its beauty and craftsmanship. The AI classifies the sentiment as **Positive** and the category as **Compliment**.
2.  **December 19, 2024 (ID: 77654822):** A request to exchange a pair of pearl earrings for a smaller size. The AI classifies the sentiment as **Neutral** and the category as an **Exchange request**.
3.  **December 24, 2024 (ID: 84435116):** A complaint regarding a silver chain that tarnished quickly, leading to a request for a full refund. The AI classifies the sentiment as **Negative** and the category as a **Return request**.

The bottom of the table includes four placeholder rows with generic labels ("yyyy/mm/dd", "##", "Feedback") for future entries.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. Applying to Other Workspace Services
&lt;/h3&gt;

&lt;p&gt;This approach—using the export URL—is applicable to Google Sheets, Google Docs, and Google Slides. The URL patterns are as follows:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;For Google Sheets:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;uri&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`https://docs.google.com/spreadsheets/d/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;spreadsheetId&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/export?format=pdf`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Or, for a specific sheet (gid)&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;uri&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`https://docs.google.com/spreadsheets/d/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;spreadsheetId&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/export?format=pdf&amp;amp;gid=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;sheetId&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;For Google Docs:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;uri&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`https://docs.google.com/document/d/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;documentId&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/export?format=pdf`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Or, for a specific tab&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;uri&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`https://docs.google.com/document/d/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;documentId&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/export?format=pdf&amp;amp;tab=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;tabId&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;For Google Slides:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;uri&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`https://docs.google.com/presentation/d/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;presentationId&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/export?format=pdf`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  4. Handling Private Content
&lt;/h3&gt;

&lt;p&gt;In a production environment, you likely do not want to make your files public. If you attempt to use the URL of a private file directly, the API will return the following error:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"error"&lt;/span&gt;&lt;span class="p"&gt;:{&lt;/span&gt;&lt;span class="nl"&gt;"code"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;400&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"message"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"Cannot fetch content from the provided URL."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"INVALID_ARGUMENT"&lt;/span&gt;&lt;span class="p"&gt;}}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To resolve this, you can append an OAuth 2.0 access token to the URL query parameters. (The access token from the service account can also be used.) This allows the Gemini API to fetch the content with the necessary permissions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;For Google Sheets:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The access token must have the scope &lt;code&gt;https://www.googleapis.com/auth/spreadsheets&lt;/code&gt; or &lt;code&gt;https://www.googleapis.com/auth/spreadsheets.readonly&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;uri&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`https://docs.google.com/spreadsheets/d/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;spreadsheetId&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/export?format=pdf&amp;amp;access_token=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;accessToken&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;For Google Docs:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The access token must have the scope &lt;code&gt;https://www.googleapis.com/auth/documents&lt;/code&gt; or &lt;code&gt;https://www.googleapis.com/auth/documents.readonly&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;uri&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`https://docs.google.com/document/d/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;documentId&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/export?format=pdf&amp;amp;access_token=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;accessToken&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;For Google Slides:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The access token must have the scope &lt;code&gt;https://www.googleapis.com/auth/presentations&lt;/code&gt; or &lt;code&gt;https://www.googleapis.com/auth/presentations.readonly&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;uri&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`https://docs.google.com/presentation/d/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;presentationId&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/export?format=pdf&amp;amp;access_token=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;accessToken&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Utility Script: Retrieving an Access Token
&lt;/h4&gt;

&lt;p&gt;The following helper script uses the &lt;code&gt;gcloud&lt;/code&gt; CLI to retrieve a valid access token for testing purposes. Ensure &lt;code&gt;gcloud&lt;/code&gt; is installed and authenticated (&lt;code&gt;gcloud auth application-default login&lt;/code&gt;) before running this.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;execSync&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;child_process&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getAccessToken&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// 1. Check if gcloud CLI is installed&lt;/span&gt;
  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;execSync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;gcloud --version&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;stdio&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ignore&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;[Error] Google Cloud SDK (gcloud CLI) not found or failed to run.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Please install it by following the official instructions:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://cloud.google.com/sdk/gcloud&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;// 2. Obtain access token&lt;/span&gt;
  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;execSync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;gcloud auth print-access-token&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;encoding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;utf8&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nf"&gt;trim&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;Error obtaining access token:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Please ensure you are authenticated with gcloud CLI. Run 'gcloud auth application-default login'.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Dynamic Content with Google Apps Script
&lt;/h2&gt;

&lt;p&gt;Google Apps Script (GAS) is a powerful tool for automation and dynamic content generation. By deploying a script as a Web App, we can create a dynamic external URL for the Gemini API.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Setting up the Web App
&lt;/h3&gt;

&lt;p&gt;In this example, we create a simple Web App that returns a specific text string. This simulates a dynamic data source.&lt;/p&gt;

&lt;p&gt;Create a standalone Google Apps Script file in Google Drive and paste the following code:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fufxdpv7h30djgd8azfks.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fufxdpv7h30djgd8azfks.jpg" alt="fig3" width="800" height="333"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;doGet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;The current keyword is 'apple'.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;ContentService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createTextOutput&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, deploy the script as a Web App:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Open the script editor.&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Deploy&lt;/strong&gt; &amp;gt; &lt;strong&gt;New deployment&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Select &lt;strong&gt;Web App&lt;/strong&gt; as the type.&lt;/li&gt;
&lt;li&gt;Set "Execute as" to &lt;strong&gt;Me&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Set "Who has access" to &lt;strong&gt;Anyone&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Deploy&lt;/strong&gt; and copy the Web App URL (&lt;code&gt;https://script.google.com/macros/s/###/exec&lt;/code&gt;).&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Note on Security:&lt;/strong&gt;&lt;br&gt;
If you wish to restrict access (e.g., set "Who has access" to &lt;strong&gt;Only myself&lt;/strong&gt;), you must append the access token to the Web App URL, similar to the private file examples above:&lt;br&gt;
&lt;code&gt;https://script.google.com/macros/s/###/exec?access_token=${accessToken}&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note on Updates:&lt;/strong&gt;&lt;br&gt;
When modifying the GAS code, you must create a new deployment version to reflect the changes in the Web App URL. &lt;a href="https://gist.github.com/tanaikech/ebf92d8f427d02d53989d6c3464a9c43" rel="noopener noreferrer"&gt;Reference&lt;/a&gt;. Additional details on GAS Web Apps can be found &lt;a href="https://github.com/tanaikech/taking-advantage-of-Web-Apps-with-google-apps-script" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;
  
  
  2. Client Script for GAS Web Apps
&lt;/h3&gt;

&lt;p&gt;Set your Web App URL to the &lt;code&gt;uri&lt;/code&gt; variable.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;GoogleGenAI&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;createPartFromUri&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@google/genai&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;uri&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://script.google.com/macros/s/###/exec&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ai&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;GoogleGenAI&lt;/span&gt;&lt;span class="p"&gt;({});&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;ai&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;models&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;generateContent&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;gemini-3-flash-preview&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;contents&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="nf"&gt;createPartFromUri&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;uri&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;text/plain&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;What is the current key word?&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. Testing Dynamic Updates
&lt;/h3&gt;

&lt;p&gt;When the script is run initially, the output is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;The current keyword is **apple**.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To test the dynamic nature of this integration, modify the GAS code:&lt;br&gt;
Change &lt;code&gt;const text = "The current keyword is 'apple'.";&lt;/code&gt; to &lt;code&gt;const text = "The current keyword is 'orange'.";&lt;/code&gt;.&lt;br&gt;
&lt;strong&gt;Deploy a new version&lt;/strong&gt; of the Web App.&lt;/p&gt;

&lt;p&gt;Running the client script again yields:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;The current keyword is **orange**.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This test confirms that the Gemini API always fetches data from the external URL in real-time. This capability is incredibly powerful. It allows Gemini to generate content based on live data feeds, latest news, or real-time calculations performed by Google Apps Script.&lt;/p&gt;

&lt;p&gt;Additionally, it is worth noting that GAS Web Apps typically require clients to follow HTTP redirects. &lt;a href="https://github.com/tanaikech/taking-advantage-of-Web-Apps-with-google-apps-script?tab=readme-ov-file#request-to-web-apps" rel="noopener noreferrer"&gt;Ref&lt;/a&gt;. The success of this test indicates that the Gemini API's external URL fetcher correctly handles HTTP redirects, ensuring seamless integration with services like GAS.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;  The Gemini API now supports external file URLs (up to 100 MB), eliminating the need to upload files beforehand.&lt;/li&gt;
&lt;li&gt;  Google Workspace files (Sheets, Docs, Slides) can be directly ingested by Gemini using their PDF export URLs.&lt;/li&gt;
&lt;li&gt;  Private Workspace files can be accessed securely by appending a valid OAuth 2.0 access token to the URL parameters.&lt;/li&gt;
&lt;li&gt;  Google Apps Script Web Apps allow for the integration of dynamic, real-time data sources into Gemini's generation process.&lt;/li&gt;
&lt;li&gt;  The Gemini API correctly handles HTTP redirects, which is essential for working with Google Apps Script Web Apps and similar services.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>gemini</category>
      <category>googleworkspace</category>
      <category>googleappsscript</category>
    </item>
    <item>
      <title>Building Next-Gen AI Agents for Google Workspace: MCP, A2A, and A2UI with Google Apps Script</title>
      <dc:creator>Tanaike</dc:creator>
      <pubDate>Sat, 10 Jan 2026 02:38:25 +0000</pubDate>
      <link>https://forem.com/gde/building-next-gen-ai-agents-for-google-workspace-mcp-a2a-and-a2ui-with-google-apps-script-299o</link>
      <guid>https://forem.com/gde/building-next-gen-ai-agents-for-google-workspace-mcp-a2a-and-a2ui-with-google-apps-script-299o</guid>
      <description>&lt;h2&gt;
  
  
  Abstract
&lt;/h2&gt;

&lt;p&gt;This article demonstrates how to implement Model Context Protocol (MCP), Agent-to-Agent (A2A), and Agent-to-UI (A2UI) utilizing Google Apps Script (GAS). By leveraging GAS, developers can overcome authentication bottlenecks, enabling seamless integration of AI agents with Google Workspace data for powerful, autonomous workflows and dynamic user interfaces.&lt;/p&gt;

&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;The ecosystem of AI agents is evolving rapidly. To enable these agents to process complex tasks effectively, the Model Context Protocol (MCP) has emerged as a critical standard. MCP allows AI models, such as Large Language Models (LLMs), to connect and interact with external data sources, tools, and software applications in a standardized manner. &lt;a href="https://modelcontextprotocol.io/docs/getting-started/intro" rel="noopener noreferrer"&gt;Ref&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Looking ahead, Agent2Agent (A2A) and Agent-to-User Interface (A2UI) protocols are expected to play pivotal roles. A2A refers to the autonomous communication and interaction between multiple AI agents without direct human intervention. &lt;a href="https://developers.googleblog.com/en/a2a-a-new-era-of-agent-interoperability/" rel="noopener noreferrer"&gt;Ref&lt;/a&gt; Meanwhile, A2UI allows AI agents to construct interactive user interfaces using structured data (such as JSON) rather than static code. This enables an agent to generate a specific UI—like a restaurant booking form—on the fly, which the host application renders using native components. &lt;a href="https://developers.googleblog.com/introducing-a2ui-an-open-project-for-agent-driven-interfaces/" rel="noopener noreferrer"&gt;Ref&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Integrating these technologies with Google Workspace unlocks limitless potential. Recently, a Gemini CLI extension including an MCP server for Workspace management was released. &lt;a href="https://github.com/gemini-cli-extensions/workspace" rel="noopener noreferrer"&gt;Ref&lt;/a&gt; However, existing samples for MCP, A2A, and A2UI often face compatibility issues with Google Workspace, primarily due to complex scope authorization bottlenecks. Connecting from a local environment typically requires handling OAuth 2.0 or service accounts, which can hinder rapid development.&lt;/p&gt;

&lt;p&gt;Google Apps Script (GAS) offers a robust solution to this challenge. As the only low-code platform designed to integrate, automate, and extend Google Workspace, GAS handles scope authorization seamlessly upon execution. &lt;a href="https://workspace.google.com/intl/en/products/apps-script/" rel="noopener noreferrer"&gt;Ref&lt;/a&gt; Consequently, leveraging GAS significantly simplifies the implementation of MCP, A2A, and A2UI. This article introduces practical implementations of these protocols using Google Apps Script.&lt;/p&gt;

&lt;h2&gt;
  
  
  Project Resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;MCP &amp;amp; A2A Repository: &lt;a href="https://github.com/tanaikech/a2a-for-google-apps-script" rel="noopener noreferrer"&gt;https://github.com/tanaikech/a2a-for-google-apps-script&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;A2UI Repository: &lt;a href="https://github.com/tanaikech/A2UI-for-Google-Apps-Script" rel="noopener noreferrer"&gt;https://github.com/tanaikech/A2UI-for-Google-Apps-Script&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Part 1: MCP (Model Context Protocol)
&lt;/h2&gt;

&lt;p&gt;In this section, we will build an MCP server using Google Apps Script and test it using the Gemini CLI.&lt;/p&gt;

&lt;h3&gt;
  
  
  Prerequisites
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Node.js is installed on your local machine.&lt;/li&gt;
&lt;li&gt;A valid Gemini API Key. &lt;a href="https://ai.google.dev/gemini-api/docs/api-key" rel="noopener noreferrer"&gt;Get one here&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;The Gemini CLI is installed. &lt;a href="https://geminicli.com/docs/" rel="noopener noreferrer"&gt;Ref&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  1. Server-Side Setup
&lt;/h3&gt;

&lt;h4&gt;
  
  
  1.1 Copy the Demo Script
&lt;/h4&gt;

&lt;p&gt;You can copy the A2A server template to your Google Drive by running the following GAS code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;myFunction&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fileId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;1mylSOTzCuui95Amk-kRufA9ffMqcnjqk8HSaoatoxaSPwkv-hg1q7uRC&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;file&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;DriveApp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getFileById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fileId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;makeCopy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getName&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run &lt;code&gt;myFunction&lt;/code&gt; to copy the sample A2A server to your root folder.&lt;/p&gt;

&lt;p&gt;Alternatively, you can manually copy the scripts from the &lt;a href="https://github.com/tanaikech/a2a-for-google-apps-script" rel="noopener noreferrer"&gt;GitHub Repository&lt;/a&gt;. This example uses &lt;a href="https://github.com/tanaikech/a2a-for-google-apps-script/blob/master/a2a-server/sample2/a2a-server.js" rel="noopener noreferrer"&gt;a2a-server/sample2/a2a-server.js&lt;/a&gt;, which integrates 160 tools from &lt;a href="https://github.com/tanaikech/ToolsForMCPServer" rel="noopener noreferrer"&gt;ToolsForMCPServer&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;For developers wishing to use custom tools, a separate sample is available at &lt;a href="https://github.com/tanaikech/a2a-for-google-apps-script/blob/master/a2a-server/sample1/a2a-server.js" rel="noopener noreferrer"&gt;a2a-server/sample1/a2a-server.js&lt;/a&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  1.2 Deploy as Web App
&lt;/h4&gt;

&lt;ol&gt;
&lt;li&gt;Open the script editor in your copied project.&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Deploy&lt;/strong&gt; &amp;gt; &lt;strong&gt;New deployment&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Select &lt;strong&gt;Web App&lt;/strong&gt; as the type.&lt;/li&gt;
&lt;li&gt;Set "Execute as" to &lt;strong&gt;Me&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Set "Who has access" to &lt;strong&gt;Anyone&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Deploy&lt;/strong&gt; and copy the Web App URL (&lt;code&gt;https://script.google.com/macros/s/###/exec&lt;/code&gt;).&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;em&gt;Note: Whenever you modify the code, you must deploy a new version to reflect the changes. &lt;a href="https://gist.github.com/tanaikech/ebf92d8f427d02d53989d6c3464a9c43" rel="noopener noreferrer"&gt;Reference&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  1.3 Configure the Script
&lt;/h4&gt;

&lt;p&gt;Locate the configuration object in your GAS project and update the variables:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;object&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;apiKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;{apiKey}&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// Your API key for using Gemini API.&lt;/span&gt;
  &lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;models/gemini-3-flash-preview&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;accessKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;sample&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// Optional: Access key for requesting Web Apps.&lt;/span&gt;
  &lt;span class="na"&gt;webAppsUrl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://script.google.com/macros/s/###/exec&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// Your deployed Web App URL.&lt;/span&gt;
  &lt;span class="c1"&gt;// logSpreadsheetId: "{spreadsheetId}", // Optional: Store logs in Google Spreadsheet.&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Set your Gemini API key in &lt;code&gt;apiKey&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Set your access key (default is &lt;code&gt;sample&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Set your Web App URL in &lt;code&gt;webAppsUrl&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;After updating the script, remember to redeploy the Web App.&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Client-Side Setup
&lt;/h3&gt;

&lt;p&gt;Navigate to the &lt;code&gt;.gemini&lt;/code&gt; directory on your local machine and update &lt;code&gt;settings.json&lt;/code&gt;. Replace &lt;code&gt;{Your value}&lt;/code&gt; with your specific details.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"security"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"auth"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"selectedType"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"{Your value}"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"ui"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"theme"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"{Your value}"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"mcpServers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"gas_web_apps"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"npx"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"args"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"mcp-remote"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"https://script.google.com/macros/s/{Your value}/exec?accessKey=sample"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"timeout"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;300000&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Launch the Gemini CLI and run the command &lt;code&gt;/mcp&lt;/code&gt;. You should see the connected status. You can now use the tools immediately because the authorization process was handled during the GAS Web App deployment.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg3262jrwl4qwdoi311c6.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg3262jrwl4qwdoi311c6.jpg" alt="loaded MCP tools" width="800" height="1953"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Part 2: A2A (Agent-to-Agent)
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Server-Side Setup
&lt;/h3&gt;

&lt;p&gt;If you have successfully completed the MCP test above, your server is ready. The GAS Web App configured in the previous section functions as both an MCP server and an A2A server. No further server-side configuration is required.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Client-Side Setup
&lt;/h3&gt;

&lt;h4&gt;
  
  
  2.1 Clone Repository and Install Dependencies
&lt;/h4&gt;

&lt;p&gt;Execute the following commands in your terminal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/tanaikech/a2a-for-google-apps-script
&lt;span class="nb"&gt;cd &lt;/span&gt;a2a-for-google-apps-script/a2a-client
npm &lt;span class="nb"&gt;install&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The directory contains:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;a2a-client&lt;/code&gt;: Node.js sample client scripts.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;a2a-server&lt;/code&gt;: Google Apps Script Web App server samples.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  2.2 The Proxy Script
&lt;/h4&gt;

&lt;p&gt;The current &lt;code&gt;@a2a-js/sdk&lt;/code&gt; version automatically strips query parameters and redirects to a &lt;code&gt;/.well-known/&lt;/code&gt; path. To resolve this, the provided &lt;a href="https://github.com/tanaikech/a2a-for-google-apps-script/blob/master/a2a-client/clientGas.js" rel="noopener noreferrer"&gt;&lt;code&gt;clientGas.js&lt;/code&gt;&lt;/a&gt; acts as a proxy. It intercepts the fetch API, allowing the SDK to communicate correctly with GAS's dynamic URLs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Authentication Note:&lt;/strong&gt; This script uses the Google Cloud SDK (&lt;code&gt;gcloud CLI&lt;/code&gt;) to retrieve access tokens. Ensure you have authorized the &lt;code&gt;https://www.googleapis.com/auth/drive.readonly&lt;/code&gt; or &lt;code&gt;https://www.googleapis.com/auth/drive&lt;/code&gt; scope. This is required to access the GAS Web App path &lt;code&gt;/.well-known/agent-card.json&lt;/code&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  2.3 Set Environment Variables
&lt;/h4&gt;

&lt;p&gt;Create a &lt;code&gt;.env&lt;/code&gt; file in the &lt;code&gt;a2a-client&lt;/code&gt; directory:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;GEMINI_API_KEY="YOUR_KEY"
GEMINI_MODEL="gemini-3-flash-preview"
A2A_WEB_APPS_URL="https://script.google.com/macros/s/{Your value}/exec?accessKey=sample"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ensure &lt;code&gt;A2A_WEB_APPS_URL&lt;/code&gt; includes your access key as a query parameter. This sample uses a single A2A server. To use multiple servers, define additional variables and register them using &lt;code&gt;new FunctionTool&lt;/code&gt; in the client script.&lt;/p&gt;

&lt;h4&gt;
  
  
  2.4 Testing
&lt;/h4&gt;

&lt;h4&gt;
  
  
  Terminal Test
&lt;/h4&gt;

&lt;p&gt;Run the basic connectivity test:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm run test1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Expected output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Prompt: How much is 10 yen in USD?
Response: 10 yen is approximately **0.0641 USD**...
Prompt: What is the weather forecast for Tokyo?
Response: The weather forecast for Tokyo... is clear sky.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Browser/ADK Test
&lt;/h4&gt;

&lt;p&gt;This test utilizes the Agent Development Kit (ADK) to provide a chat interface:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm run test2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Open &lt;code&gt;http://localhost:8000&lt;/code&gt; in your browser.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example 1: Simple Workflow&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Prompt:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;I want to cook miso soup.
To achieve this goal, create a new Google Spreadsheet,
generate a roadmap for cooking miso soup in the spreadsheet,
and return the Spreadsheet URL.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The browser demonstrates the agent processing the prompt via the A2A server.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fncrbas8octvbfycoz4j9.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fncrbas8octvbfycoz4j9.gif" alt="A2A sample" width="1752" height="1053"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The resulting roadmap in the Spreadsheet:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbvd8hs42yfe5oh4oivm9.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbvd8hs42yfe5oh4oivm9.jpg" alt="A2A sample" width="800" height="248"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example 2: Complex Workflow&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Prompt:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Write a comprehensive article about developing Google Apps Script (GAS) using generative AI.
The article should include an introductory overview, formatted lists for best practices,
and a table comparing different AI-assisted coding techniques.
Once generated, please create a new Google Document, insert the content, convert the Google Document to a PDF file,
and send an email to `tanaike@hotmail.com` including the shareable URL of the PDF file by giving a suitable title and email body.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The browser visualizes the processing steps:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fa7ixu14jmuh8ac58zhmy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fa7ixu14jmuh8ac58zhmy.gif" alt="A2A sample" width="760" height="456"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This task requires coordination between the Docs API, Drive API, and Gmail API. While this might trigger a Tool Selection Issue (TSI) in standard environments, the GAS A2A server handles the deterministic selection seamlessly.&lt;/p&gt;

&lt;p&gt;The generated PDF:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6qgklpvz6062orj10deb.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6qgklpvz6062orj10deb.jpg" alt="A2A sample" width="800" height="3105"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The email received:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Dear Tanaike,

Please find attached the PDF document containing the comprehensive article on "Leveraging Generative AI for Google Apps Script Development". The shareable URL for the PDF is https://drive.google.com/file/d/###/view?usp=sharing.

Best regards,
Your AI Assistant
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Part 3: A2UI (Agent-to-User Interface)
&lt;/h2&gt;

&lt;p&gt;In the &lt;a href="https://github.com/google/A2UI/tree/d7996656ef2bc0cdffad499452fc5b282d878d45/samples/agent/adk/restaurant_finder" rel="noopener noreferrer"&gt;official A2UI sample (Restaurant finder)&lt;/a&gt;, the client communicates via A2A protocols. In this GAS-optimized implementation, we reduce HTTP overhead by allowing the client (HTML) to communicate directly with the AI agent using &lt;code&gt;google.script.run&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Sample Script
&lt;/h3&gt;

&lt;p&gt;Copy the Google Spreadsheet containing the necessary scripts:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://docs.google.com/spreadsheets/d/1csYUJO8LzcEFPkt_ickIkdsGZsvim6lb1OEQZHUkB3c/copy" rel="noopener noreferrer"&gt;https://docs.google.com/spreadsheets/d/1csYUJO8LzcEFPkt_ickIkdsGZsvim6lb1OEQZHUkB3c/copy&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;After copying, open the script editor and set your &lt;code&gt;apiKey&lt;/code&gt; in &lt;code&gt;main.gs&lt;/code&gt;.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Alternatively, access the code via the &lt;a href="https://github.com/tanaikech/A2UI-for-Google-Apps-Script" rel="noopener noreferrer"&gt;GitHub Repository&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Deploy as Web App
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Open the script editor in your project.&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Deploy&lt;/strong&gt; &amp;gt; &lt;strong&gt;New deployment&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Select &lt;strong&gt;Web App&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Set "Execute as" to &lt;strong&gt;Me&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Set "Who has access" to &lt;strong&gt;Only myself&lt;/strong&gt;. (Restrict access to yourself for testing as the owner).&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Deploy&lt;/strong&gt; and copy the Web App URL.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;em&gt;Note: You can use the dev mode URL (&lt;code&gt;.../dev&lt;/code&gt;) for testing. Updates require a new deployment. &lt;a href="https://gist.github.com/tanaikech/ebf92d8f427d02d53989d6c3464a9c43" rel="noopener noreferrer"&gt;Reference&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Testing
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Sample 1: Restaurant Finder
&lt;/h4&gt;

&lt;p&gt;This test reproduces the official "A2UI Restaurant finder" sample.&lt;/p&gt;

&lt;p&gt;Open your Web App URL. You will see the interface:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fw5n09qv5htdsuahl66po.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fw5n09qv5htdsuahl66po.jpg" alt="A2UI sample" width="800" height="830"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Enter: "Find 3 Chinese restaurants in New York" and click &lt;strong&gt;Send&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo6nmc0w6wioe8v757oor.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo6nmc0w6wioe8v757oor.jpg" alt="A2UI sample" width="800" height="830"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Click &lt;strong&gt;Book Now&lt;/strong&gt; for "Han Dynasty". The agent generates a reservation UI.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0a4gfz25sj1we6uczxow.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0a4gfz25sj1we6uczxow.jpg" alt="A2UI sample" width="800" height="830"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Fill in the details and click &lt;strong&gt;Submit Reservation&lt;/strong&gt;. The server processes the request.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fizccfmzr0k2h2j23k0vr.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fizccfmzr0k2h2j23k0vr.jpg" alt="A2UI sample" width="800" height="830"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Using Spreadsheet Data:&lt;/strong&gt;&lt;br&gt;
While the sample uses hard-coded data in &lt;code&gt;executeGetRestaurants&lt;/code&gt;, GAS allows you to fetch this from a Spreadsheet easily. If you have a sheet formatted like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frukywp0wxip74ks7g2hh.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frukywp0wxip74ks7g2hh.jpg" alt="A2UI sample" width="800" height="182"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can replace the data source with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;allRestaurants&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;_&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sheet&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;SpreadsheetApp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getActiveSpreadsheet&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;getSheetByName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Sheet1&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;header&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;values&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;sheet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getDataRange&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;getDisplayValues&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;obj&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;values&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
    &lt;span class="nx"&gt;header&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reduce&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;o&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;h&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;j&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;o&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;h&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;j&lt;/span&gt;&lt;span class="p"&gt;]),&lt;/span&gt; &lt;span class="nx"&gt;o&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;{})&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;})();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Sample 2: Budget Simulator
&lt;/h4&gt;

&lt;p&gt;Copy the Budget Simulator Spreadsheet:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://docs.google.com/spreadsheets/d/1HEfmSD9WMqQfy39aEZEjz7ggFeiZIx0_b2oKkrReEpk/copy" rel="noopener noreferrer"&gt;https://docs.google.com/spreadsheets/d/1HEfmSD9WMqQfy39aEZEjz7ggFeiZIx0_b2oKkrReEpk/copy&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Set your &lt;code&gt;apiKey&lt;/code&gt; in &lt;code&gt;main.gs&lt;/code&gt; after copying.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This sample implements an "Interactive smart household account book and budget simulator". Ensure "Sheet1" contains the initial budget data:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp5y4ujlf4ri55zx91bb2.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp5y4ujlf4ri55zx91bb2.jpg" alt="A2UI sample" width="800" height="280"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Deploy as a Web App and open the URL.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2g01qj96nqiomi3r6hpu.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2g01qj96nqiomi3r6hpu.jpg" alt="A2UI sample" width="800" height="615"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Enter: &lt;code&gt;Check this month's budget&lt;/code&gt;. A2UI displays a pie chart and category list based on the spreadsheet data.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdv2rcnt8bf1cm81w9gl6.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdv2rcnt8bf1cm81w9gl6.jpg" alt="A2UI sample" width="800" height="615"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Enter: "What would happen if I reduced my dining out expenses by 10,000 yen and put the money towards savings?"&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7zp59iz1sovqm0zeex9u.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7zp59iz1sovqm0zeex9u.jpg" alt="A2UI sample" width="800" height="615"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The graph updates to simulate the proposal.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffoycha5ujtmsblmr13y9.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffoycha5ujtmsblmr13y9.jpg" alt="A2UI sample" width="800" height="615"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Click &lt;strong&gt;Update sheet with this budget proposal&lt;/strong&gt;. GAS updates the cells, and a notification confirms the action.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsepnrv3g52iebyx5vvf5.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsepnrv3g52iebyx5vvf5.jpg" alt="A2UI sample" width="800" height="615"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The new data is appended to "Sheet1":&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fczmr0qmgq9z58m1zr59x.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fczmr0qmgq9z58m1zr59x.jpg" alt="A2UI sample" width="800" height="280"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;MCP, A2A, and A2UI represent the future of AI interoperability, enabling agents to connect with tools, collaborate with each other, and generate dynamic user interfaces.&lt;/li&gt;
&lt;li&gt;Google Apps Script provides a unique advantage by resolving complex OAuth 2.0 authorization bottlenecks native to local environments.&lt;/li&gt;
&lt;li&gt;Implementing MCP servers on GAS allows for seamless integration between the Gemini CLI and Google Workspace data.&lt;/li&gt;
&lt;li&gt;A2A on GAS facilitates autonomous multi-agent workflows, capable of executing complex tasks like generating documents and sending emails without human intervention.&lt;/li&gt;
&lt;li&gt;A2UI combined with GAS allows agents to build interactive, data-driven interfaces on the fly, directly connected to live Spreadsheet data.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Appendix
&lt;/h2&gt;

&lt;p&gt;I am exploring taking advantage of Google Apps Script. The summary of this can be seen &lt;a href="https://github.com/tanaikech/taking-advantage-of-google-apps-script" rel="noopener noreferrer"&gt;https://github.com/tanaikech/taking-advantage-of-google-apps-script&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>gemini</category>
      <category>mcp</category>
      <category>a2a</category>
      <category>googleworkspace</category>
    </item>
  </channel>
</rss>
