Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 35 additions & 23 deletions DOCS.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ gemini-api run "Analyze my data" --agent my-data-analyst
| Flag | Short | Type | Default | Description |
|---|---|---|---|---|
| `<prompt>` | | positional | — | Input prompt. |
| `--model` | `-m` | string | `gemini-3-flash-preview` | Model to use |
| `--model` | `-m` | string | `gemini-3.5-flash` | Model to use |
| `--agent` | `-a` | string | — | Agent to use (overrides `--model`) |
| `--input` | `-i` | string[] | — | Multimodal input: `image:path`, `audio:path`, `video:path`, `document:path` |
| `--output` | `-o` | string | — | Save generated media to file |
Expand Down Expand Up @@ -265,7 +265,6 @@ gemini-api files download env_xyz789 --dry-run
my-agent/
├── agent.yaml # Configuration (not inlined)
├── AGENTS.md # System instructions (inlined to /.agents/AGENTS.md)
├── .env # Credentials (inlined to /credentials/.env)
├── skills/ # Custom skills (all files inlined recursively)
└── workspace/ # Files seeded into remote environment (all files inlined recursively)
```
Expand Down Expand Up @@ -293,9 +292,7 @@ environment: remote
# base_environment: env_abc123
```

### `.env`

Credentials file. If it contains non-empty values, it's inlined during `agents test` and `agents create` to `/credentials/.env` in the agent environment. The agent can then `source /credentials/.env` to load the values.

### `AGENTS.md`

Expand All @@ -313,7 +310,7 @@ Files seeded into the remote environment at `/.agents/workspace/`. All files in
| Binary files | Base64-encoded with `"encoding": "base64"` | `.pdf`, `.png`, `.jpg`, `.mp3`, `.wav`, `.zip` |
| Files > 1 MB | Skipped | — |

> **Note:** Only `AGENTS.md`, `.env`, `workspace/`, and `skills/` are inlined from the agent directory. All other root-level files and directories are ignored.
> **Note:** Only `AGENTS.md`, `workspace/`, and `skills/` are inlined from the agent directory. All other root-level files and directories are ignored.

Binary files are automatically detected by extension. The following are treated as binary:
- Images: `.png`, `.jpg`, `.jpeg`, `.webp`, `.gif`, `.bmp`, `.tiff`, `.heic`, `.heif`
Expand All @@ -334,7 +331,7 @@ environment: remote
# base_environment: env_abc123
```

When `environment` is `"remote"`, the API provisions a sandbox with code execution capabilities. Workspace files, skills, and credentials are seeded into it before the agent runs.
When `environment` is `"remote"`, the API provisions a sandbox with code execution capabilities. Workspace files and skills are seeded into it before the agent runs.

You can also specify a structured config object to configure GCS/GitHub `sources`, establish `network` allowlists, and inject secret credentials securely via header `transform` rules:

Expand Down Expand Up @@ -385,29 +382,43 @@ tools:

## Output Modes

### Human (Default)
### Normal (Default)

Streaming text with typed block labels:
Optimized for clean, readable output and valid Markdown parsing. Thoughts are concise, tool calls are consolidated into single lines, and the final response text is printed without leading indentation:

```
[tool] read_file({"path":"/credentials/.env"})
[result] # Configurations for your agent...
[text] I found the credentials file.
[thought]
[tool] write_file(path="hello.py") -> {"success":true}
[code] python3 hello.py -> "Hello, World!"
[text]
I have created a Python script named `hello.py` and successfully executed it.

✓ completed
interaction_id: v1_ChdZTlR3YVlfSE9zdjJ4TjhQbjVHQjhRdxIXWU5Ud2FZX0hPc3YyeE44UG41R0I4UXc
environment_id: 7a5c3831-cc50-48cf-9351-100aa7d1c3a8
latency: 15.8s
Here is the content of `hello.py`:
```python
print("Hello, World!")
```
```

### Verbose (`--verbose` / `-v`)

Optimized for automated parsing by agents. Steps are output as completed single-line JSON objects, followed by the final `{interaction}` metadata as a JSON line:

```json
{"index":0,"type":"thought","status":"completed","thought":{"signature":"EvQBCvEBAQw5..."}}
{"index":1,"type":"function_call","status":"completed","function_call":{"name":"write_file","arguments":{"path":"hello.py","content":"print(\"Hello, World!\")"}}}
{"index":2,"type":"function_result","status":"completed","function_result":{"name":"write_file","result":{"success":true}}}
{"interaction":{"id":"v1_ChdIcjRp...","status":"completed","usage":{"total_tokens":9131,"total_input_tokens":8970,"total_output_tokens":161,"total_cached_tokens":0},"object":"interaction"}}
```

### JSON (`--json`)

Raw SSE events as JSONL (one event per line):
Raw streamed SSE events as JSONL (one raw event per line):

```jsonl
{"event_type":"interaction.start","interaction":{...}}
{"event_type":"content.delta","index":0,"delta":{"type":"text","text":"Hello"}}
{"event_type":"interaction.complete","interaction":{...}}
{"event_type":"interaction.created","interaction":{...}}
{"index":0,"step":{"type":"thought"},"event_type":"step.start"}
{"index":0,"delta":{"signature":"EvQBC...","type":"thought_signature"},"event_type":"step.delta"}
{"event_type":"interaction.completed","interaction":{...}}
```

### Dry Run (`--dry-run`)
Expand Down Expand Up @@ -541,13 +552,14 @@ gemini-api agents create --dry-run

| Model | Description |
|---|---|
| `gemini-3-flash-preview` | Frontier + search (default) |
| `gemini-3.5-flash` | Frontier + search (default) |
| `gemini-3.1-pro-preview` | SOTA reasoning + multimodal |
| `gemini-3.1-pro-preview` | SOTA reasoning |
| `gemini-3-flash-preview` | Gemini 3 Flash Preview |
| `gemini-3.1-flash-lite` | Gemini 3.1 Flash-Lite |
| `gemini-2.5-flash` | Hybrid reasoning, 1M context |
| `gemini-2.5-pro` | SOTA coding + reasoning |
| `gemini-3-pro-image-preview` | Image generation |
| `gemini-3.1-flash-image-preview` | Flash image generation |
| `gemini-3-pro-image` | Nano Banana Pro (image generation) |
| `gemini-3.1-flash-image` | Nano Banana 2 (image generation) |
| `gemini-2.5-flash-image` | Native image generation |
| `gemini-3.1-flash-tts-preview` | Text-to-speech |
| `gemini-2.5-flash-preview-tts` | TTS |
Expand Down
4 changes: 2 additions & 2 deletions skills/gemini-api-cli/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,8 @@ Outputs raw SSE events as JSONL (one event per line), useful for machine parsing
gemini-api run "Hello" --json
```

### Verbose (`--verbose`)
Enables detailed request and response logging for debugging.
### Verbose (`--verbose` / `-v`)
Outputs complete JSON step objects line-by-line as they finish. Recommended for programmatic parsing by calling agents.
```bash
gemini-api run "Hello" --verbose
```
3 changes: 1 addition & 2 deletions skills/gemini-api-cli/references/agents.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,11 @@ This creates a directory with the default structure.
my-agent/
├── agent.yaml # Configuration (ID, tools, etc.) — not inlined
├── AGENTS.md # System instructions (inlined to /.agents/AGENTS.md)
├── .env # Credentials (inlined to /credentials/.env)
├── skills/ # Custom skills (all files inlined recursively)
└── workspace/ # Files seeded into remote environment (all files inlined recursively)
```

> **Note:** Only `AGENTS.md`, `.env`, `workspace/`, and `skills/` are inlined from the agent directory. All other root-level files and directories (e.g., `README.md`, `package.json`) are ignored during `agents test` and `agents create`.
> **Note:** Only `AGENTS.md`, `workspace/`, and `skills/` are inlined from the agent directory. All other root-level files and directories (e.g., `README.md`, `package.json`) are ignored during `agents test` and `agents create`.

### Adding Skills

Expand Down
7 changes: 4 additions & 3 deletions skills/gemini-api-cli/references/genmedia.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,13 @@ This reference covers generating and editing media using the Gemini API CLI.
To generate an image, use a model that supports image generation and specify an output file with `--output` or `-o`.

```bash
gemini-api run "A cat in space, oil painting" --model gemini-3.1-flash-image-preview --output cat.png
gemini-api run "A cat in space, oil painting" --model gemini-3.1-flash-image --output cat.png
```

Supported image models include:
- `gemini-3-pro-image-preview`
- `gemini-3.1-flash-image-preview`
- `gemini-3-pro-image` (Nano Banana Pro)
- `gemini-3.1-flash-image` (Nano Banana 2)
- `gemini-2.5-flash-image`

## Image Editing

Expand Down
7 changes: 3 additions & 4 deletions skills/gemini-api-cli/references/model_calls.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ This reference covers standard interactions with Gemini models using the `gemini

## Basic Prompting

Run a simple prompt against the default model (`gemini-3-flash-preview`):
Run a simple prompt against the default model (`gemini-3.5-flash`):

```bash
gemini-api run "What is the capital of France?"
Expand All @@ -19,10 +19,9 @@ gemini-api run "Explain quantum computing" --model gemini-3.1-pro-preview
```

Common models:
- `gemini-3-flash-preview` (default)
- `gemini-3.5-flash` (default)
- `gemini-3.1-pro-preview`
- `gemini-2.5-flash`
- `gemini-2.5-pro`
- `gemini-3.1-flash-lite`

## Multi-Turn Conversations

Expand Down
10 changes: 7 additions & 3 deletions src/commands/agents/create.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
import { defineCommand } from "citty";
import { apiRequest, resolveContext, type Source, normalizeSources, validateSources } from "../../lib/api";
import {
apiRequest,
normalizeSources,
resolveContext,
type Source,
validateSources,
} from "../../lib/api";
import { loadAgent } from "../../lib/config";
import { CLIError, ConfigError } from "../../lib/errors";
import { collectInlineFiles } from "../../lib/files";
Expand Down Expand Up @@ -96,8 +102,6 @@ Examples:
(body.base_environment as any).network = envObj.network;
}
}


}

const url = "/agents";
Expand Down
11 changes: 5 additions & 6 deletions src/commands/agents/test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,10 @@ import {
apiStreamRequest,
buildInteractionRequest,
isAgentName,
normalizeSources,
type RunOptions,
resolveContext,
type Source,
normalizeSources,
validateSources,
} from "../../lib/api";
import { loadAgent } from "../../lib/config";
Expand Down Expand Up @@ -88,9 +88,7 @@ Examples:
// system_instruction from agent.yaml is sent in the request body.
// AGENTS.md is NOT loaded here — it is uploaded as an inline file and
// merged with the system instruction on the server side.
let systemInstruction = config.instructions;


const systemInstruction = config.instructions;

// Build environment config
let environment: any;
Expand Down Expand Up @@ -166,7 +164,8 @@ Examples:
onComplete: () => {},
});
} else {
const renderer = new HumanStreamRenderer();
const verbose = args.verbose as boolean;
const renderer = new HumanStreamRenderer(process.stdout, verbose);

await processStream(response, {
onEvent: (event, block) => {
Expand All @@ -176,7 +175,7 @@ Examples:
onComplete: (result) => {
renderer.finish();
const latencySeconds = (performance.now() - startTime) / 1000;
printCompletionSummary(result, latencySeconds);
printCompletionSummary(result, latencySeconds, verbose);
if (!args.json) {
logRequest(result.interactionId, body);
logResponse(result.interactionId, result);
Expand Down
4 changes: 2 additions & 2 deletions src/commands/files/download.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
import * as fs from "node:fs";
import * as path from "node:path";
import { defineCommand } from "citty";
import { resolveContext } from "../../lib/api";
import { fetchWithTimeout, resolveContext } from "../../lib/api";
import { CLIError } from "../../lib/errors";
import { printCurl, printError } from "../../lib/output";
import { globalFlags } from "../../lib/shared-args";
Expand Down Expand Up @@ -65,7 +65,7 @@ Examples:
};

console.log(`Downloading snapshot for environment ${envId}...`);
const response = await fetch(fullUrl, { headers });
const response = await fetchWithTimeout(fullUrl, { headers });

if (!response.ok) {
throw new CLIError(`Failed to download snapshot: ${response.statusText}`);
Expand Down
7 changes: 4 additions & 3 deletions src/commands/run.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ Examples:
type: "string",
alias: "m",
description: "Model to use",
default: "gemini-3-flash-preview",
default: "gemini-3.5-flash",
},
agent: {
type: "string",
Expand Down Expand Up @@ -350,7 +350,8 @@ Examples:
onComplete: () => {},
});
} else {
const renderer = new HumanStreamRenderer();
const verbose = args.verbose as boolean;
const renderer = new HumanStreamRenderer(process.stdout, verbose);

await processStream(response, {
onEvent: (event, block) => {
Expand All @@ -361,7 +362,7 @@ Examples:
renderer.finish();
saveMediaOutputs(result.outputs, result.interactionId, args.output as string | undefined);
const latencySeconds = (performance.now() - startTime) / 1000;
printCompletionSummary(result, latencySeconds);
printCompletionSummary(result, latencySeconds, verbose);
if (!args.json) {
logRequest(result.interactionId, body);
logResponse(result.interactionId, result);
Expand Down
34 changes: 28 additions & 6 deletions src/lib/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,26 @@ import { CLIError } from "./errors";

export const DEFAULT_BASE_URL = "https://generativelanguage.googleapis.com/v1beta";

export async function fetchWithTimeout(
url: string,
init?: RequestInit,
timeoutMs = 3000000,
): Promise<Response> {
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
try {
const response = await fetch(url, {
...init,
signal: controller.signal,
});
clearTimeout(timeoutId);
return response;
} catch (error) {
clearTimeout(timeoutId);
throw error;
}
}

export interface CLIContext {
apiKey: string;
baseUrl: string;
Expand Down Expand Up @@ -63,7 +83,7 @@ export async function apiRequest<T>(
headers["Api-Revision"] = "2026-05-20";
}

const response = await fetch(url, {
const response = await fetchWithTimeout(url, {
method,
headers,
body: body ? JSON.stringify(body) : undefined,
Expand All @@ -89,7 +109,7 @@ export async function apiRequest<T>(

if (response.status === 400) {
throw new CLIError(
`${errorMsg}\n\n Try:\n gemini-api run "Hello" --model gemini-3-flash-preview`,
`${errorMsg}\n\n Try:\n gemini-api run "Hello" --model gemini-3.5-flash`,
);
}

Expand All @@ -113,7 +133,7 @@ export async function apiGetRequest<T>(
"x-goog-api-key": ctx.apiKey,
};

const response = await fetch(url, { method: "GET", headers });
const response = await fetchWithTimeout(url, { method: "GET", headers });

if (!response.ok) {
let errorMsg = `API error (${response.status})`;
Expand Down Expand Up @@ -145,7 +165,7 @@ export async function apiGetStreamRequest(
"x-goog-api-key": ctx.apiKey,
};

const response = await fetch(url, { method: "GET", headers });
const response = await fetchWithTimeout(url, { method: "GET", headers });

if (!response.ok) {
let errorMsg = `API error (${response.status})`;
Expand Down Expand Up @@ -179,7 +199,7 @@ export async function apiStreamRequest(
headers["Api-Revision"] = "2026-05-20";
}

const response = await fetch(url, {
const response = await fetchWithTimeout(url, {
method: "POST",
headers,
body: JSON.stringify(body),
Expand Down Expand Up @@ -329,7 +349,9 @@ export function parseSourceFlag(value: string): Source {
return { type: "gcs", source: rest.substring(0, idx), target: rest.substring(idx + 1) };
}

throw new CLIError(`Unknown source type in '${value}'\n\n Available: inline, github, repository, gcs`);
throw new CLIError(
`Unknown source type in '${value}'\n\n Available: inline, github, repository, gcs`,
);
}

export interface RunOptions {
Expand Down
4 changes: 0 additions & 4 deletions src/lib/files.ts
Original file line number Diff line number Diff line change
Expand Up @@ -240,8 +240,6 @@ export async function collectInlineFiles(
const rel = relative(dir, fullPath);
if (rel === "agent.yaml") continue;



// If we are in the root directory, only allow AGENTS.md
if (currentDir === dir) {
if (rel !== "AGENTS.md") {
Expand Down Expand Up @@ -277,5 +275,3 @@ export async function collectInlineFiles(
await walk(dir);
return files;
}


Loading
Loading