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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -317,6 +317,18 @@ if (myRepo) {
- `jules.sources()`: Async iterator over all connected sources.
- `jules.sources.get(filter)`: Retrieve a specific source by identifier.

## Documentation

- **[Getting Started](docs/getting-started.md)**: Installation and first steps.
- **[Sessions](docs/sessions.md)**: Managing interactive and automated sessions.
- **[Streaming](docs/streaming.md)**: Real-time activity monitoring.
- **[Artifacts](docs/artifacts.md)**: Working with files, shell output, and media.
- **[Local Query](docs/query.md)**: Querying cached session data.
- **[Sources](docs/sources.md)**: Connecting to GitHub.
- **[Configuration](docs/configuration.md)**: Advanced SDK options.
- **[Error Handling](docs/errors.md)**: Handling SDK errors.
- **[Snapshots](docs/snapshot.md)**: Serializing session state.

## License

Apache-2.0
Expand Down
116 changes: 116 additions & 0 deletions docs/artifacts.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
# Artifacts

Activities often contain "artifacts," which are the tangible outputs of the agent's work. These include code changes, shell command outputs, and images (e.g., screenshots).

## Artifact Types

Any activity can contain one or more artifacts in its `.artifacts` array.

```typescript
for (const artifact of activity.artifacts) {
if (artifact.type === 'changeSet') {
// Handle code changes
} else if (artifact.type === 'bashOutput') {
// Handle shell output
} else if (artifact.type === 'media') {
// Handle images
}
}
```

## ChangeSet Artifacts

Represents a set of code changes (a diff). The SDK provides a `.parsed()` method to make it easier to inspect these changes.

```typescript
if (artifact.type === 'changeSet') {
const parsed = artifact.parsed();

console.log(`Changed ${parsed.summary.totalFiles} files:`);

for (const file of parsed.files) {
console.log(
`${file.changeType}: ${file.path} ` +
`(+${file.additions}/-${file.deletions})`
);
}
}
```

### ParsedFile Structure

```typescript
interface ParsedFile {
path: string; // e.g., "src/index.ts"
changeType: 'created' | 'modified' | 'deleted';
additions: number; // Lines added
deletions: number; // Lines removed
}
```

## Bash Artifacts

Represents the output of a shell command. Use `.toString()` to get a terminal-like representation.

```typescript
if (artifact.type === 'bashOutput') {
console.log(artifact.toString());

// Or access raw properties
console.log('Command:', artifact.command);
console.log('Stdout:', artifact.stdout);
console.log('Stderr:', artifact.stderr);

if (artifact.exitCode !== 0) {
console.error(`Command failed with code ${artifact.exitCode}`);
}
}
```

## Media Artifacts

Represents binary media, such as a screenshot taken by the agent.

### Viewing in Browser

Use `.toUrl()` to generate a Data URI that can be used in an `<img>` tag.

```typescript
if (artifact.type === 'media') {
const url = artifact.toUrl();
// <img src={url} />
}
```

### Saving to Disk (Node.js)

Use `.save()` to write the file to the local filesystem.

```typescript
if (artifact.type === 'media') {
await artifact.save(`./screenshots/${activity.id}.png`);
}
```

## Generated Files

When a session completes successfully, you can access the final state of any modified or created files directly from the `SessionOutcome`. This is often more convenient than parsing individual artifacts.

```typescript
const result = await session.result();
const files = result.generatedFiles();

// 1. Get a specific file
const readme = files.get('README.md');
if (readme) {
console.log(readme.content);
}

// 2. Iterate all generated files
for (const file of files.all()) {
console.log(`File: ${file.path}`);
}

// 3. Filter by change type
const newFiles = files.filter('created');
```
101 changes: 101 additions & 0 deletions docs/configuration.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
# SDK Configuration

The Jules SDK is designed to work out-of-the-box with minimal configuration, but offers granular control for advanced use cases.

## Default Client

The simplest way to use the SDK is via the pre-initialized `jules` instance. This instance automatically reads the `JULES_API_KEY` environment variable.

```typescript
import { jules } from '@google/jules-sdk';

const session = await jules.session({ ... });
```

## Custom Clients

If you need multiple configurations (e.g., connecting to different projects or using different API keys), you can create custom client instances using `jules.with()`.

This method creates a **copy** of the client with the new options merged in. It does not mutate the original instance.

```typescript
const customClient = jules.with({
apiKey: 'another-api-key',
config: {
requestTimeoutMs: 60000
}
});
```

## Configuration Options

### API Key

You can provide the API key explicitly if you are not using environment variables.

```typescript
jules.with({ apiKey: 'YOUR_KEY' });
```

### Polling

The SDK polls the API for updates during streaming and waiting. You can adjust the interval.

```typescript
jules.with({
config: {
// Check for updates every 2 seconds (default is 5000ms)
pollingIntervalMs: 2000
}
});
```

### Timeouts

Set the maximum duration for individual HTTP requests.

```typescript
jules.with({
config: {
// Time out requests after 10 seconds (default is 30000ms)
requestTimeoutMs: 10000
}
});
```

### Rate Limit Retries

The SDK automatically handles `429 Too Many Requests` errors with exponential backoff. You can tune this behavior.

```typescript
jules.with({
config: {
rateLimitRetry: {
// Stop retrying after 5 minutes
maxRetryTimeMs: 300000,
// Start with a 1 second delay
baseDelayMs: 1000,
// Cap the delay at 30 seconds
maxDelayMs: 30000
}
}
});
```

## Interface Reference

```typescript
interface JulesOptions {
apiKey?: string;
baseUrl?: string;
config?: {
pollingIntervalMs?: number;
requestTimeoutMs?: number;
rateLimitRetry?: {
maxRetryTimeMs?: number;
baseDelayMs?: number;
maxDelayMs?: number;
};
};
}
```
78 changes: 78 additions & 0 deletions docs/errors.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
# Error Handling

The Jules SDK uses a typed error hierarchy to make it easy to handle specific failure scenarios programmatically. All errors thrown by the SDK inherit from the base `JulesError` class.

## Error Hierarchy

- `JulesError` (Base class)
- `JulesNetworkError`
- `JulesApiError`
- `JulesAuthenticationError` (401/403)
- `JulesRateLimitError` (429)
- `MissingApiKeyError`
- `SourceNotFoundError`
- `AutomatedSessionFailedError`
- `SyncInProgressError`
- `InvalidStateError`

## Handling Errors

You can catch errors globally or specifically using `instanceof` checks.

```typescript
import {
jules,
JulesError,
JulesAuthenticationError,
AutomatedSessionFailedError
} from '@google/jules-sdk';

try {
await jules.run({ ... });
} catch (error) {
if (error instanceof JulesAuthenticationError) {
console.error('Invalid API Key. Please check your credentials.');
} else if (error instanceof AutomatedSessionFailedError) {
console.error('The session failed to complete the task.');
} else if (error instanceof JulesError) {
// Catch-all for other SDK errors
console.error(`SDK Error: ${error.message}`);
} else {
// Unknown system errors
throw error;
}
}
```

## Common Errors

### `JulesNetworkError`
Thrown when the SDK cannot reach the Jules API (e.g., DNS failure, offline).

### `JulesAuthenticationError`
Thrown when the API key is missing, invalid, or expired.

### `JulesRateLimitError`
Thrown when you have exceeded the API rate limits. The SDK automatically retries for a period of time before throwing this error.

### `SourceNotFoundError`
Thrown when trying to create a session with a GitHub repository that doesn't exist or is not accessible to the installation.

```typescript
try {
await jules.session({
source: { github: 'ghost/does-not-exist', baseBranch: 'main' },
prompt: '...'
});
} catch (e) {
if (e instanceof SourceNotFoundError) {
console.log("Repo not found!");
}
}
```

### `InvalidStateError`
Thrown when attempting an operation that is not valid for the current session state (e.g., calling `approve()` on a session that is not waiting for approval).

### `SyncInProgressError`
Thrown when calling `jules.sync()` while another sync operation is already running. The SDK prevents concurrent syncs to avoid data corruption.
79 changes: 79 additions & 0 deletions docs/getting-started.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
# Getting Started with Jules SDK

The Jules SDK allows you to orchestrate fleet of coding agents in the cloud. This guide will help you get set up and running your first session.

## Installation

Install the package using npm (or your preferred package manager):

```bash
npm i @google/jules-sdk
```

## Configuration

The SDK requires an API key to authenticate with the Jules service. You can obtain one from the [Google Cloud Console](https://console.cloud.google.com).

Set the API key as an environment variable:

```bash
export JULES_API_KEY=<your-api-key>
```

Alternatively, you can pass the key programmatically (not recommended for production code committed to version control):

```typescript
import { jules } from '@google/jules-sdk';

// Using a custom client instance
const client = jules.with({ apiKey: 'your-api-key' });
```

## Your First Session

Here is a minimal example of creating a session that fixes a simple bug in a GitHub repository.

```typescript
import { jules } from '@google/jules-sdk';

async function main() {
// 1. Start a session
console.log('Starting session...');
const session = await jules.session({
prompt: 'Update the README to include a "Contributing" section.',
source: {
github: 'your-username/your-repo',
baseBranch: 'main'
},
autoPr: true // Automatically create a Pull Request when done
});

console.log(`Session created: ${session.id}`);

// 2. Monitor progress
for await (const activity of session.stream()) {
if (activity.type === 'planGenerated') {
console.log('Plan generated with', activity.plan.steps.length, 'steps');
} else if (activity.type === 'progressUpdated') {
console.log('Progress:', activity.title);
} else if (activity.type === 'sessionCompleted') {
console.log('Session completed successfully!');
}
}

// 3. Get the result
const outcome = await session.result();
if (outcome.pullRequest) {
console.log(`Pull Request created: ${outcome.pullRequest.url}`);
}
}

main().catch(console.error);
```

## Next Steps

- **[Sessions](./sessions.md)**: Learn about interactive vs. automated sessions.
- **[Streaming](./streaming.md)**: Observe agent activity in real-time.
- **[Artifacts](./artifacts.md)**: Work with code changes, shell output, and images.
- **[Configuration](./configuration.md)**: Advanced SDK configuration.
Loading
Loading