modjules 0.1.1 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +10 -6
- package/dist/activities/client.d.ts +44 -1
- package/dist/activities/types.d.ts +7 -0
- package/dist/activity/summary.d.ts +7 -0
- package/dist/api.d.ts +31 -1
- package/dist/artifacts.d.ts +22 -1
- package/dist/browser.d.ts +9 -1
- package/dist/browser.mjs +304 -0
- package/dist/browser.mjs.map +1 -0
- package/dist/caching.d.ts +16 -0
- package/dist/client.d.ts +53 -1
- package/dist/errors.d.ts +7 -0
- package/dist/github/adapter.d.ts +2 -0
- package/dist/github/api.d.ts +6 -0
- package/dist/github/caching.d.ts +2 -0
- package/dist/github/errors.d.ts +17 -0
- package/dist/github/index.d.ts +3 -0
- package/dist/github/pr-client.d.ts +12 -0
- package/dist/github/types.d.ts +72 -0
- package/dist/index.d.ts +24 -2
- package/dist/index.mjs +1438 -0
- package/dist/index.mjs.map +1 -0
- package/dist/memory-DE2Wujcu.js +2573 -0
- package/dist/memory-DE2Wujcu.js.map +1 -0
- package/dist/platform/browser.d.ts +13 -1
- package/dist/platform/node.d.ts +15 -1
- package/dist/platform/types.d.ts +56 -1
- package/dist/platform/web.d.ts +29 -0
- package/dist/query/computed.d.ts +65 -0
- package/dist/query/projection.d.ts +65 -0
- package/dist/query/schema.d.ts +109 -0
- package/dist/query/select.d.ts +6 -0
- package/dist/query/validate.d.ts +59 -0
- package/dist/session.d.ts +30 -6
- package/dist/sessions.d.ts +65 -0
- package/dist/snapshot.d.ts +23 -0
- package/dist/storage/cache-info.d.ts +28 -0
- package/dist/storage/memory.d.ts +15 -2
- package/dist/storage/node-fs.d.ts +19 -3
- package/dist/storage/root.d.ts +2 -0
- package/dist/storage/types.d.ts +63 -1
- package/dist/types.d.ts +422 -11
- package/dist/utils/page-token.d.ts +60 -0
- package/dist/utils.d.ts +1 -1
- package/package.json +14 -44
- package/dist/browser.es.js +0 -158
- package/dist/client-D2GRjxRE.mjs +0 -927
- package/dist/index.es.js +0 -140
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"memory-DE2Wujcu.js","sources":["../src/errors.ts","../src/api.ts","../src/sources.ts","../src/storage/root.ts","../src/artifacts.ts","../src/mappers.ts","../src/streaming.ts","../src/polling.ts","../src/utils/page-token.ts","../src/activities/client.ts","../src/network/adapter.ts","../src/caching.ts","../src/snapshot.ts","../src/session.ts","../src/utils.ts","../src/sessions.ts","../src/storage/cache-info.ts","../src/query/projection.ts","../src/activity/summary.ts","../src/query/computed.ts","../src/query/select.ts","../src/client.ts","../src/storage/memory.ts"],"sourcesContent":["// src/errors.ts\n\n/**\n * Base class for all SDK-specific errors.\n * This allows consumers to catch all Jules SDK errors with a single `catch` block.\n */\nexport class JulesError extends Error {\n /** The original error that caused this error, if any. */\n public readonly cause?: Error;\n\n constructor(message: string, options?: { cause?: Error }) {\n super(message);\n this.name = this.constructor.name;\n this.cause = options?.cause;\n }\n}\n\n/**\n * Thrown for fundamental network issues like fetch failures or timeouts.\n */\nexport class JulesNetworkError extends JulesError {\n public readonly url: string;\n constructor(url: string, options?: { cause?: Error }) {\n super(`Network request to ${url} failed`, options);\n this.url = url;\n }\n}\n\n/**\n * A generic wrapper for non-2xx API responses that don't match other specific errors.\n */\nexport class JulesApiError extends JulesError {\n public readonly url: string;\n public readonly status: number;\n public readonly statusText: string;\n\n constructor(\n url: string,\n status: number,\n statusText: string,\n message?: string, // optional override\n options?: { cause?: Error },\n ) {\n const finalMessage =\n message ?? `[${status} ${statusText}] Request to ${url} failed`;\n super(finalMessage, options);\n this.url = url;\n this.status = status;\n this.statusText = statusText;\n }\n}\n\n/**\n * Thrown for 401 Unauthorized or 403 Forbidden API responses.\n */\nexport class JulesAuthenticationError extends JulesApiError {\n constructor(url: string, status: number, statusText: string) {\n super(\n url,\n status,\n statusText,\n `[${status} ${statusText}] Authentication to ${url} failed. Ensure your API key is correct.`,\n );\n }\n}\n\n/**\n * Thrown for 429 Too Many Requests API responses.\n */\nexport class JulesRateLimitError extends JulesApiError {\n constructor(url: string, status: number, statusText: string) {\n super(\n url,\n status,\n statusText,\n `[${status} ${statusText}] API rate limit exceeded for ${url}.`,\n );\n }\n}\n\n/**\n * Thrown when an API key is required but not provided.\n */\nexport class MissingApiKeyError extends JulesError {\n constructor() {\n super(\n 'Jules API key is missing. Pass it to the constructor or set the JULES_API_KEY environment variable.',\n );\n }\n}\n\n/**\n * Thrown when a requested source cannot be found.\n */\nexport class SourceNotFoundError extends JulesError {\n constructor(sourceIdentifier: string) {\n super(`Could not get source '${sourceIdentifier}'`);\n }\n}\n\n/**\n * Thrown when a jules.run() operation terminates in a FAILED state.\n */\nexport class AutomatedSessionFailedError extends JulesError {\n constructor(reason?: string) {\n let message = 'The Jules automated session terminated with a FAILED state.';\n if (reason) {\n message += ` Reason: ${reason}`;\n }\n super(message);\n }\n}\n\n/**\n * Thrown when attempting to start a sync while another sync is already in progress.\n * This prevents data corruption and thundering herd issues.\n */\nexport class SyncInProgressError extends JulesError {\n constructor() {\n super(\n 'A sync operation is already in progress. Wait for it to complete before starting another.',\n );\n }\n}\n\n/**\n * Thrown when an operation is attempted on a session that is not in a\n * valid state for that operation.\n */\nexport class InvalidStateError extends JulesError {\n constructor(message: string) {\n super(message);\n }\n}\n","// src/api.ts\n\nimport { ProxyConfig } from './types.js';\nimport {\n JulesApiError,\n JulesAuthenticationError,\n JulesNetworkError,\n JulesRateLimitError,\n MissingApiKeyError,\n} from './errors.js';\n\nexport type RateLimitRetryConfig = {\n maxRetryTimeMs: number;\n baseDelayMs: number;\n maxDelayMs: number;\n};\n\nexport type ApiClientOptions = {\n apiKey: string | undefined;\n baseUrl: string;\n requestTimeoutMs: number;\n proxy?: ProxyConfig;\n rateLimitRetry?: Partial<RateLimitRetryConfig>;\n};\n\nexport type HandshakeContext =\n | { intent: 'create'; sessionConfig: any }\n | { intent: 'resume'; sessionId: string };\n\nexport type ApiRequestOptions = {\n method?: 'GET' | 'POST';\n body?: Record<string, unknown>;\n query?: Record<string, any>;\n headers?: Record<string, string>;\n handshake?: HandshakeContext;\n _isRetry?: boolean; // Internal flag to prevent infinite loops\n};\n\n/**\n * A simple internal API client to handle HTTP requests to the Jules API.\n * @internal\n */\nexport class ApiClient {\n private readonly apiKey: string | undefined;\n private readonly baseUrl: string;\n private readonly requestTimeoutMs: number;\n private readonly proxy?: ProxyConfig;\n private readonly rateLimitConfig: RateLimitRetryConfig;\n private capabilityToken: string | null = null;\n // Cache the handshake promise to prevent parallel handshakes (thundering herd)\n private handshakePromise: Promise<string> | null = null;\n\n constructor(options: ApiClientOptions) {\n this.apiKey = options.apiKey;\n this.baseUrl = options.baseUrl;\n this.requestTimeoutMs = options.requestTimeoutMs;\n this.proxy = options.proxy;\n this.rateLimitConfig = {\n maxRetryTimeMs: options.rateLimitRetry?.maxRetryTimeMs ?? 300000, // 5 minutes\n baseDelayMs: options.rateLimitRetry?.baseDelayMs ?? 1000,\n maxDelayMs: options.rateLimitRetry?.maxDelayMs ?? 30000,\n };\n }\n\n async request<T>(\n endpoint: string,\n options: ApiRequestOptions = {},\n ): Promise<T> {\n const {\n method = 'GET',\n body,\n query,\n headers: customHeaders,\n handshake,\n _isRetry,\n } = options;\n const url = this.resolveUrl(endpoint);\n\n if (query) {\n Object.entries(query).forEach(([key, value]) => {\n url.searchParams.append(key, String(value));\n });\n }\n\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n ...customHeaders,\n };\n\n // 1. Inject Credentials\n if (this.apiKey) {\n // Direct Mode\n headers['X-Goog-Api-Key'] = this.apiKey;\n } else if (this.proxy) {\n // Proxy Mode\n const token = await this.ensureToken(handshake);\n headers['Authorization'] = `Bearer ${token}`;\n } else {\n throw new MissingApiKeyError();\n }\n\n // 2. Execute Request\n const response = await this.fetchWithTimeout(url.toString(), {\n method,\n headers,\n body: body ? JSON.stringify(body) : undefined,\n });\n\n // 3. Auto-Retry on 401/403 (Token Expiration)\n if (this.proxy && (response.status === 401 || response.status === 403)) {\n if (_isRetry) {\n throw new JulesAuthenticationError(\n url.toString(),\n response.status,\n 'Authentication failed even after token refresh',\n );\n }\n\n // Force a fresh handshake\n this.capabilityToken = null;\n // Recursive call with retry flag\n return this.request<T>(endpoint, { ...options, _isRetry: true });\n }\n\n if (!response.ok) {\n if (response.status === 429) {\n // Time-based retry: Keep retrying until maxRetryTimeMs is exhausted\n const startTime = (options as any)._rateLimitStartTime || Date.now();\n const elapsed = Date.now() - startTime;\n const retryCount = (options as any)._rateLimitRetryCount || 0;\n\n if (elapsed < this.rateLimitConfig.maxRetryTimeMs) {\n // Exponential backoff capped at maxDelayMs\n const rawDelay =\n this.rateLimitConfig.baseDelayMs * Math.pow(2, retryCount);\n const delay = Math.min(rawDelay, this.rateLimitConfig.maxDelayMs);\n await new Promise((resolve) => setTimeout(resolve, delay));\n return this.request<T>(endpoint, {\n ...options,\n _rateLimitStartTime: startTime,\n _rateLimitRetryCount: retryCount + 1,\n } as any);\n }\n\n throw new JulesRateLimitError(\n url.toString(),\n response.status,\n response.statusText,\n );\n }\n\n switch (response.status) {\n case 401:\n case 403:\n throw new JulesAuthenticationError(\n url.toString(),\n response.status,\n response.statusText,\n );\n default:\n const errorBody = await response\n .text()\n .catch(() => 'Could not read error body');\n const message = `[${\n response.status\n } ${response.statusText}] ${method} ${url.toString()} - ${errorBody}`;\n throw new JulesApiError(\n url.toString(),\n response.status,\n response.statusText,\n message,\n );\n }\n }\n\n const responseText = await response.text();\n if (!responseText) {\n return {} as T;\n }\n\n return JSON.parse(responseText) as T;\n }\n\n /**\n * Ensures we have a valid Capability Token.\n * If not, performs the Handshake.\n */\n private async ensureToken(context?: HandshakeContext): Promise<string> {\n // If we are explicitly asking to create a session, we must ensure the token\n // carries the 'create' intent. Existing cached tokens might be 'resume' tokens\n // or generic ones, which might not be authorized for creation.\n // Therefore, we invalidate the cache for 'create' intent to force a fresh handshake.\n if (context?.intent === 'create') {\n this.capabilityToken = null;\n }\n\n if (this.capabilityToken) return this.capabilityToken;\n if (!this.proxy) throw new Error('Missing Proxy Configuration');\n\n // Deduplicate concurrent handshake requests\n if (!this.handshakePromise) {\n this.handshakePromise = this.performHandshake(context);\n }\n\n try {\n this.capabilityToken = await this.handshakePromise;\n return this.capabilityToken;\n } finally {\n this.handshakePromise = null;\n }\n }\n\n private async performHandshake(context?: HandshakeContext): Promise<string> {\n if (!this.proxy) throw new Error('No proxy config');\n\n // 1. Get Identity Token (e.g. Firebase)\n const authToken = this.proxy.auth ? await this.proxy.auth() : '';\n\n // 2. Construct the Body based on Context\n const body: any = { authToken };\n\n if (context?.intent === 'create') {\n body.intent = 'create';\n body.context = context.sessionConfig; // Pass prompt/source\n } else {\n // Default to resume if unspecified, or explicitly resume\n body.intent = 'resume';\n if (context?.intent === 'resume') {\n body.sessionId = context.sessionId;\n }\n }\n\n // 3. Call Proxy Handshake Endpoint\n const res = await this.fetchWithTimeout(this.proxy.url, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(body),\n });\n\n const data: any = await res.json();\n if (!data.success) throw new Error(data.error || 'Handshake failed');\n return data.token;\n }\n\n private resolveUrl(path: string): URL {\n if (this.proxy) {\n // When using Proxy, the path is appended to the proxy URL\n const url = new URL(this.proxy.url);\n url.searchParams.append('path', `/${path}`);\n return url;\n }\n // Direct Mode\n return new URL(`${this.baseUrl}/${path}`);\n }\n\n private async fetchWithTimeout(url: string, opts: any): Promise<Response> {\n const controller = new AbortController();\n const timeoutId = setTimeout(\n () => controller.abort(),\n this.requestTimeoutMs,\n );\n\n try {\n const response = await fetch(url, {\n ...opts,\n signal: controller.signal,\n });\n return response;\n } catch (error) {\n throw new JulesNetworkError(url, {\n cause: error as Error,\n });\n } finally {\n clearTimeout(timeoutId);\n }\n }\n}\n","// src/sources.ts\nimport { ApiClient } from './api.js';\nimport { JulesApiError } from './errors.js';\nimport { Source, SourceManager, GitHubRepo } from './types.js';\n\n// Internal type representing the raw source from the REST API\ntype RawSource = {\n name: string;\n id: string;\n githubRepo?: GitHubRepo;\n};\n\n// Internal type for the paginated list response\ntype ListSourcesResponse = {\n sources: RawSource[];\n nextPageToken?: string;\n};\n\n/**\n * Maps a raw API source object to the SDK's discriminated union Source type.\n * @internal\n */\nfunction mapRawSourceToSdkSource(rawSource: RawSource): Source {\n if (rawSource.githubRepo) {\n return {\n name: rawSource.name,\n id: rawSource.id,\n type: 'githubRepo',\n githubRepo: rawSource.githubRepo,\n };\n }\n // This is a safeguard; based on current API, we only have githubRepo.\n // If other source types were added, we'd need to handle them here.\n throw new Error(`Unknown source type for source: ${rawSource.name}`);\n}\n\n/**\n * Implements the logic for the SourceManager.\n * @internal\n */\nclass SourceManagerImpl {\n private apiClient: ApiClient;\n\n constructor(apiClient: ApiClient) {\n this.apiClient = apiClient;\n }\n\n /**\n * Lists all connected sources.\n *\n * **Logic:**\n * - Automatically handles API pagination by following `nextPageToken`.\n * - Yields sources one by one as they are retrieved.\n */\n async *list(): AsyncIterable<Source> {\n let pageToken: string | undefined = undefined;\n\n while (true) {\n const params: Record<string, string> = { pageSize: '100' };\n if (pageToken) {\n params.pageToken = pageToken;\n }\n\n const response = await this.apiClient.request<ListSourcesResponse>(\n 'sources',\n { query: params },\n );\n\n if (response && response.sources) {\n for (const rawSource of response.sources) {\n yield mapRawSourceToSdkSource(rawSource);\n }\n }\n\n pageToken = response?.nextPageToken;\n if (!pageToken) {\n break;\n }\n }\n }\n\n /**\n * Retrieves a specific source by its external identifier.\n *\n * **Data Transformation:**\n * - Constructs a resource name (e.g., `sources/github/owner/repo`) from the input filter.\n *\n * @param filter Filter criteria (currently supports GitHub repo name).\n * @returns The matching Source object, or `undefined` if not found (404).\n * @throws {Error} If the filter format is invalid.\n */\n async get(filter: { github: string }): Promise<Source | undefined> {\n const { github } = filter;\n if (!github || !github.includes('/')) {\n throw new Error(\"Invalid GitHub filter. Expected format: 'owner/repo'.\");\n }\n\n const resourceName = `sources/github/${github}`;\n\n try {\n const rawSource = await this.apiClient.request<RawSource>(resourceName);\n if (!rawSource) {\n return undefined;\n }\n return mapRawSourceToSdkSource(rawSource);\n } catch (error) {\n if (error instanceof JulesApiError && error.status === 404) {\n return undefined; // Gracefully return undefined for 404s\n }\n throw error; // Re-throw other errors\n }\n }\n}\n\n/**\n * Creates a SourceManager instance.\n * The SourceManager is a callable object (an async iterator) with a `get` method attached.\n * @internal\n */\nexport function createSourceManager(apiClient: ApiClient): SourceManager {\n const manager = new SourceManagerImpl(apiClient);\n\n const callable = manager.list.bind(manager);\n\n // Attach the 'get' method to the callable function object\n const sourceManager = callable as SourceManager;\n sourceManager.get = manager.get.bind(manager);\n\n return sourceManager;\n}\n","// src/storage/root.ts\nimport { homedir } from 'node:os';\nimport { accessSync, constants, existsSync } from 'node:fs';\nimport * as path from 'node:path';\n\nexport function isWritable(dir: string): boolean {\n try {\n accessSync(dir, constants.W_OK);\n return true;\n } catch {\n return false;\n }\n}\n\nexport function getRootDir(): string {\n // 1. Explicit environment variable (highest priority)\n const julesHome = process.env.JULES_HOME;\n if (julesHome && isWritable(julesHome)) {\n return julesHome;\n }\n\n // 2. Project-first: If package.json exists in cwd, use project-local cache\n const cwd = process.cwd();\n const isInProject = existsSync(path.join(cwd, 'package.json'));\n if (isInProject && cwd !== '/' && isWritable(cwd)) {\n return cwd;\n }\n\n // 3. HOME environment variable\n const home = process.env.HOME;\n if (home && home !== '/' && isWritable(home)) {\n return home;\n }\n\n // 4. os.homedir() (may use /etc/passwd on Unix)\n const osHome = homedir();\n if (osHome && osHome !== '/' && isWritable(osHome)) {\n return osHome;\n }\n\n // 5. Temporary directory as last resort\n const tmpDir = process.env.TMPDIR || process.env.TMP || '/tmp';\n return tmpDir;\n}\n","import type {\n RestMediaArtifact,\n RestBashOutputArtifact,\n GitPatch,\n ParsedChangeSet,\n ParsedFile,\n} from './types.js';\nimport { Platform } from './platform/types.js';\n\n/**\n * Parses a unified diff string and extracts file information.\n * @internal\n */\nfunction parseUnidiff(patch: string): ParsedFile[] {\n const files: ParsedFile[] = [];\n // Split by diff headers (diff --git a/... b/...)\n const diffSections = patch.split(/^diff --git /m).filter(Boolean);\n\n for (const section of diffSections) {\n const lines = section.split('\\n');\n\n // Extract file path from the +++ line (destination file)\n // Format: +++ b/path/to/file or +++ /dev/null\n let path = '';\n let fromPath = '';\n let toPath = '';\n\n for (const line of lines) {\n if (line.startsWith('--- ')) {\n // --- a/path or --- /dev/null\n fromPath = line\n .slice(4)\n .replace(/^a\\//, '')\n .replace(/^\\/dev\\/null$/, '');\n } else if (line.startsWith('+++ ')) {\n // +++ b/path or +++ /dev/null\n toPath = line\n .slice(4)\n .replace(/^b\\//, '')\n .replace(/^\\/dev\\/null$/, '');\n }\n }\n\n // Determine change type and path\n let changeType: 'created' | 'modified' | 'deleted';\n if (fromPath === '' || lines.some((l) => l.startsWith('--- /dev/null'))) {\n changeType = 'created';\n path = toPath;\n } else if (\n toPath === '' ||\n lines.some((l) => l.startsWith('+++ /dev/null'))\n ) {\n changeType = 'deleted';\n path = fromPath;\n } else {\n changeType = 'modified';\n path = toPath;\n }\n\n // Skip if we couldn't determine a path\n if (!path) continue;\n\n // Count additions and deletions (lines starting with + or - in hunks)\n let additions = 0;\n let deletions = 0;\n let inHunk = false;\n\n for (const line of lines) {\n if (line.startsWith('@@')) {\n inHunk = true;\n continue;\n }\n if (inHunk) {\n if (line.startsWith('+') && !line.startsWith('+++')) {\n additions++;\n } else if (line.startsWith('-') && !line.startsWith('---')) {\n deletions++;\n }\n }\n }\n\n files.push({ path, changeType, additions, deletions });\n }\n\n return files;\n}\n\n/**\n * Represents a media artifact (e.g. image) produced by an activity.\n * Provides helper methods for saving and viewing the media.\n */\nexport class MediaArtifact {\n public readonly type = 'media';\n public readonly data: string;\n public readonly format: string;\n private platform: Platform;\n private activityId?: string;\n\n constructor(\n artifact: RestMediaArtifact['media'],\n platform: Platform,\n activityId?: string,\n ) {\n this.data = artifact.data;\n this.format = artifact.format;\n this.platform = platform;\n this.activityId = activityId;\n }\n\n /**\n * Saves the media artifact to a file.\n *\n * **Side Effects:**\n * - Node.js: Writes the file to disk (overwrites if exists).\n * - Browser: Saves the file to the 'artifacts' object store in IndexedDB.\n *\n * @param filepath The path where the file should be saved.\n */\n async save(filepath: string): Promise<void> {\n await this.platform.saveFile(\n filepath,\n this.data,\n 'base64',\n this.activityId,\n );\n }\n\n /**\n * Converts the media artifact to a data URL.\n * Useful for displaying images in a browser.\n *\n * **Data Transformation:**\n * - Prefixes the base64 data with `data:<mimeType>;base64,`.\n *\n * @returns A valid Data URI string.\n */\n toUrl(): string {\n return this.platform.createDataUrl(this.data, this.format);\n }\n}\n\n/**\n * Represents the output of a bash command executed by the agent.\n */\nexport class BashArtifact {\n public readonly type = 'bashOutput';\n public readonly command: string;\n public readonly stdout: string;\n public readonly stderr: string;\n public readonly exitCode: number | null;\n\n constructor(artifact: RestBashOutputArtifact['bashOutput']) {\n this.command = artifact.command;\n this.stdout = artifact.stdout;\n this.stderr = artifact.stderr;\n this.exitCode = artifact.exitCode;\n }\n\n /**\n * Formats the bash output as a string, mimicking a terminal session.\n *\n * **Data Transformation:**\n * - Combines `stdout` and `stderr`.\n * - Formats the command with a `$` prompt.\n * - Appends the exit code.\n */\n toString(): string {\n const output = [this.stdout, this.stderr].filter(Boolean).join('');\n const commandLine = `$ ${this.command}`;\n const outputLine = output ? `${output}\\n` : '';\n const exitLine = `[exit code: ${this.exitCode ?? 'N/A'}]`;\n return `${commandLine}\\n${outputLine}${exitLine}`;\n }\n}\n\n/**\n * Represents a set of code changes (unified diff) produced by an activity.\n * Provides a helper method to parse the diff into structured data.\n */\nexport class ChangeSetArtifact {\n public readonly type = 'changeSet' as const;\n public readonly source: string;\n public readonly gitPatch: GitPatch;\n\n constructor(source: string, gitPatch: GitPatch) {\n this.source = source;\n this.gitPatch = gitPatch;\n }\n\n /**\n * Parses the unified diff and returns structured file change information.\n *\n * **Data Transformation:**\n * - Extracts file paths from diff headers.\n * - Determines change type (created/modified/deleted) from /dev/null markers.\n * - Counts additions (+) and deletions (-) in hunks.\n *\n * @returns Parsed diff with file paths, change types, and line counts.\n */\n parsed(): ParsedChangeSet {\n const files = parseUnidiff(this.gitPatch.unidiffPatch);\n\n const summary = {\n totalFiles: files.length,\n created: files.filter((f) => f.changeType === 'created').length,\n modified: files.filter((f) => f.changeType === 'modified').length,\n deleted: files.filter((f) => f.changeType === 'deleted').length,\n };\n\n return { files, summary };\n }\n}\n","// src/mappers.ts\nimport { MediaArtifact, BashArtifact, ChangeSetArtifact } from './artifacts.js';\nimport { AutomatedSessionFailedError } from './errors.js';\nimport {\n Activity,\n Artifact,\n Outcome,\n PullRequest,\n RestArtifact,\n SessionResource,\n} from './types.js';\n\n/**\n * Maps a raw REST API Artifact resource to the SDK's `Artifact` type.\n * This now instantiates rich classes for certain artifact types.\n *\n * @param restArtifact The raw artifact object from the REST API.\n * @returns A structured `Artifact` object for the SDK.\n * @internal\n */\nexport function mapRestArtifactToSdkArtifact(\n restArtifact: RestArtifact,\n platform: any,\n activityId?: string,\n): Artifact {\n if ('changeSet' in restArtifact) {\n return new ChangeSetArtifact(\n restArtifact.changeSet.source,\n restArtifact.changeSet.gitPatch,\n );\n }\n if ('media' in restArtifact) {\n return new MediaArtifact(restArtifact.media, platform, activityId);\n }\n if ('bashOutput' in restArtifact) {\n return new BashArtifact(restArtifact.bashOutput);\n }\n // This provides a fallback, though the API should always provide a known type.\n throw new Error(`Unknown artifact type: ${JSON.stringify(restArtifact)}`);\n}\n\n/**\n * Maps a raw REST API Activity resource to the SDK's discriminated union `Activity` type.\n *\n * **Data Transformation:**\n * - Flattens nested union fields (e.g., `agentMessaged`) into a top-level `type` property.\n * - Extracts the short ID from the full resource `name`.\n * - Recursively maps all artifacts within the activity.\n *\n * @param restActivity The raw activity object from the REST API.\n * @param platform The platform adapter (needed for artifact mapping).\n * @returns A structured `Activity` object for the SDK.\n * @throws {Error} If the activity type is unknown.\n * @internal\n */\nexport function mapRestActivityToSdkActivity(\n restActivity: any,\n platform: any,\n): Activity {\n const {\n name,\n createTime,\n originator,\n artifacts: rawArtifacts,\n } = restActivity;\n\n const activityId = name.split('/').pop();\n\n // First, map the artifacts since they are common to all activities.\n const artifacts: Artifact[] = (rawArtifacts || []).map((artifact: any) =>\n mapRestArtifactToSdkArtifact(artifact, platform, activityId),\n );\n\n const baseActivity = {\n name,\n id: activityId,\n createTime,\n originator: originator || 'system',\n artifacts,\n };\n\n if (restActivity.agentMessaged) {\n return {\n ...baseActivity,\n type: 'agentMessaged',\n message: restActivity.agentMessaged.agentMessage,\n };\n }\n if (restActivity.userMessaged) {\n return {\n ...baseActivity,\n type: 'userMessaged',\n message: restActivity.userMessaged.userMessage,\n };\n }\n if (restActivity.planGenerated) {\n return {\n ...baseActivity,\n type: 'planGenerated',\n plan: restActivity.planGenerated.plan,\n };\n }\n if (restActivity.planApproved) {\n return {\n ...baseActivity,\n type: 'planApproved',\n planId: restActivity.planApproved.planId,\n };\n }\n if (restActivity.progressUpdated) {\n return {\n ...baseActivity,\n type: 'progressUpdated',\n title: restActivity.progressUpdated.title,\n description: restActivity.progressUpdated.description,\n };\n }\n if (restActivity.sessionCompleted) {\n return {\n ...baseActivity,\n type: 'sessionCompleted',\n };\n }\n if (restActivity.sessionFailed) {\n return {\n ...baseActivity,\n type: 'sessionFailed',\n reason: restActivity.sessionFailed.reason,\n };\n }\n\n // Fallback for unknown activity types.\n throw new Error('Unknown activity type');\n}\n\n/**\n * Maps the final state of a SessionResource to a user-facing Outcome object.\n * This includes extracting the primary pull request and handling the failed state.\n *\n * @param session The final SessionResource from the API.\n * @returns The corresponding Outcome object.\n * @throws {AutomatedSessionFailedError} If the session state is 'failed'.\n */\nexport function mapSessionResourceToOutcome(session: SessionResource): Outcome {\n if (session.state === 'failed') {\n // TODO: The reason is not available on the session resource directly.\n // This will be improved when the API provides a failure reason.\n throw new AutomatedSessionFailedError(`Session ${session.id} failed.`);\n }\n\n // Find the pull request output, if it exists.\n const prOutput = session.outputs.find((o) => 'pullRequest' in o);\n const pullRequest = prOutput\n ? (prOutput as { pullRequest: PullRequest }).pullRequest\n : undefined;\n\n return {\n sessionId: session.id,\n title: session.title,\n state: 'completed', // We only call this mapper on a completed session.\n pullRequest,\n outputs: session.outputs,\n };\n}\n","// src/streaming.ts\nimport { ApiClient } from './api.js';\nimport { JulesApiError } from './errors.js';\nimport { mapRestActivityToSdkActivity } from './mappers.js';\nimport { Activity, Origin, SessionResource } from './types.js';\n\n// A helper function for delaying execution.\nconst sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));\n\n// Define the raw REST API response type for listing activities.\ntype ListActivitiesResponse = {\n activities: any[]; // Using any for now, will be mapped.\n nextPageToken?: string;\n};\n\n/**\n * Options for controlling the activity stream.\n * @internal\n */\nexport type StreamActivitiesOptions = {\n /**\n * Filters to exclude certain activities.\n */\n exclude?: {\n originator: Origin;\n };\n};\n\n/**\n * An async generator that implements a hybrid pagination/polling strategy\n * to stream activities for a given session.\n *\n * @param sessionId The ID of the session to stream activities for.\n * @param apiClient The API client to use for requests.\n * @param pollingInterval The time in milliseconds to wait before polling for new activities.\n * @param platform The platform adapter.\n * @param options Streaming options, including filters.\n * @internal\n */\nimport { Platform } from './platform/types.js';\n\nexport async function* streamActivities(\n sessionId: string,\n apiClient: ApiClient,\n pollingInterval: number,\n platform: Platform,\n options: StreamActivitiesOptions = {},\n): AsyncGenerator<Activity> {\n let pageToken: string | undefined = undefined;\n let isFirstCall = true;\n const yieldedActivityNames = new Set<string>();\n\n while (true) {\n let response: ListActivitiesResponse;\n try {\n response = await apiClient.request<ListActivitiesResponse>(\n `sessions/${sessionId}/activities`,\n {\n query: {\n pageSize: '50', // A reasonable page size\n ...(pageToken ? { pageToken } : {}),\n },\n },\n );\n } catch (error) {\n if (\n isFirstCall &&\n error instanceof JulesApiError &&\n error.status === 404\n ) {\n let lastError: JulesApiError = error;\n let successfulResponse: ListActivitiesResponse | undefined;\n let delay = 1000; // Start with a 1-second delay\n\n for (let i = 0; i < 5; i++) {\n await sleep(delay);\n delay *= 2; // Double the delay for the next attempt\n try {\n successfulResponse =\n await apiClient.request<ListActivitiesResponse>(\n `sessions/${sessionId}/activities`,\n {\n query: {\n pageSize: '50',\n ...(pageToken ? { pageToken } : {}),\n },\n },\n );\n break; // On success, exit the retry loop.\n } catch (retryError) {\n if (\n retryError instanceof JulesApiError &&\n retryError.status === 404\n ) {\n lastError = retryError;\n } else {\n throw retryError; // Re-throw non-404 errors immediately.\n }\n }\n }\n\n if (successfulResponse) {\n response = successfulResponse;\n } else {\n throw lastError; // If all retries fail, throw the last 404 error.\n }\n } else {\n throw error; // Re-throw non-retryable errors.\n }\n }\n\n isFirstCall = false; // Mark the first call as done.\n\n const activities = response.activities || [];\n\n for (const rawActivity of activities) {\n // Duplication check\n if (yieldedActivityNames.has(rawActivity.name)) {\n continue;\n }\n\n const activity = mapRestActivityToSdkActivity(rawActivity, platform);\n\n if (\n options.exclude?.originator &&\n activity.originator === options.exclude.originator\n ) {\n continue;\n }\n\n yieldedActivityNames.add(rawActivity.name);\n yield activity;\n }\n\n if (response.nextPageToken) {\n pageToken = response.nextPageToken;\n // Immediately fetch the next page without waiting.\n continue;\n } else {\n // We've reached the end of the current stream, so wait before polling again.\n pageToken = undefined; // Reset for the next poll\n await sleep(pollingInterval);\n }\n }\n}\n","// src/polling.ts\nimport { ApiClient } from './api.js';\nimport { SessionResource } from './types.js';\n\n// A helper function for delaying execution.\nconst sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));\n\n/**\n * A generalized utility for polling the session resource until a specific\n * condition is met.\n *\n * @param sessionId The ID of the session to poll.\n * @param apiClient The API client for making requests.\n * @param predicateFn A function that returns `true` if polling should stop.\n * @param pollingInterval The interval in milliseconds between poll attempts.\n * @returns The session resource that satisfied the predicate.\n * @internal\n */\nexport async function pollSession(\n sessionId: string,\n apiClient: ApiClient,\n predicateFn: (session: SessionResource) => boolean,\n pollingInterval: number,\n): Promise<SessionResource> {\n while (true) {\n const session = await apiClient.request<SessionResource>(\n `sessions/${sessionId}`,\n );\n\n if (predicateFn(session)) {\n return session;\n }\n\n await sleep(pollingInterval);\n }\n}\n\n/**\n * Polls the `GET /sessions/{id}` endpoint until the session reaches a terminal state.\n *\n * @param sessionId The ID of the session to poll.\n * @param apiClient The API client for making requests.\n * @param pollingInterval The interval in milliseconds between poll attempts.\n * @returns The final SessionResource.\n * @internal\n */\nexport async function pollUntilCompletion(\n sessionId: string,\n apiClient: ApiClient,\n pollingInterval: number,\n): Promise<SessionResource> {\n return pollSession(\n sessionId,\n apiClient,\n (session) => session.state === 'completed' || session.state === 'failed',\n pollingInterval,\n );\n}\n","/**\n * Utilities for constructing and parsing Jules API pageTokens.\n *\n * The Jules API uses nanosecond timestamps as pageTokens for activity pagination.\n * This allows us to construct tokens from cached activity createTimes,\n * enabling efficient incremental syncing without re-downloading existing activities.\n *\n * @module\n */\n\n/**\n * Converts an activity's createTime to a Jules API pageToken.\n *\n * The pageToken is the createTime represented as nanoseconds since Unix epoch.\n * By default, adds 1 nanosecond to make it exclusive (returns activities AFTER this time).\n *\n * @param createTime - RFC 3339 timestamp (e.g., \"2024-01-05T10:05:00.999999Z\")\n * @param exclusive - If true (default), adds 1ns to exclude the activity at this exact time\n * @returns The pageToken string (nanosecond timestamp)\n *\n * @example\n * ```typescript\n * const token = createTimeToPageToken(\"2024-01-05T10:05:00.999999Z\");\n * // Returns \"1704448500999999001\" (exclusive - activities after this time)\n *\n * const inclusiveToken = createTimeToPageToken(\"2024-01-05T10:05:00.999999Z\", false);\n * // Returns \"1704448500999999000\" (inclusive - activities at or after this time)\n * ```\n */\nexport function createTimeToPageToken(\n createTime: string,\n exclusive = true,\n): string {\n const date = new Date(createTime);\n const epochMs = date.getTime();\n\n // Extract sub-millisecond precision from the timestamp\n // Format: \"2024-01-05T10:05:00.999999Z\" → extract \"999999\"\n const match = createTime.match(/\\.(\\d+)Z$/);\n const fractional = match ? match[1].padEnd(9, '0').slice(0, 9) : '000000000';\n\n // Convert to nanoseconds:\n // - epochMs * 1,000,000 gives nanoseconds from milliseconds\n // - Add the sub-millisecond portion (microseconds → nanoseconds)\n const epochNs =\n BigInt(epochMs) * 1000000n + BigInt(fractional.slice(3, 9)) * 1000n;\n\n // Add 1 nanosecond for exclusive queries (get activities AFTER this time)\n const token = exclusive ? epochNs + 1n : epochNs;\n\n return token.toString();\n}\n\n/**\n * Converts a Jules API pageToken back to a Date.\n * Useful for debugging and logging.\n *\n * @param token - The pageToken string (nanosecond timestamp)\n * @returns The corresponding Date object\n *\n * @example\n * ```typescript\n * const date = pageTokenToDate(\"1704448500999999000\");\n * // Returns Date for 2024-01-05T10:05:00.999Z\n * ```\n */\nexport function pageTokenToDate(token: string): Date {\n const tokenNs = BigInt(token);\n const tokenMs = Number(tokenNs / 1000000n);\n return new Date(tokenMs);\n}\n\n/**\n * Checks if a session's activities are \"frozen\" (no new activities possible).\n * A session is considered frozen if its last activity is older than the threshold.\n *\n * @param lastActivityCreateTime - The createTime of the most recent activity\n * @param thresholdDays - Number of days after which a session is frozen (default: 30)\n * @returns true if the session is frozen and no API call is needed\n *\n * @example\n * ```typescript\n * const isFrozen = isSessionFrozen(\"2024-01-05T10:05:00Z\");\n * if (isFrozen) {\n * // Skip API call - no new activities will ever appear\n * }\n * ```\n */\nexport function isSessionFrozen(\n lastActivityCreateTime: string,\n thresholdDays = 30,\n): boolean {\n const lastActivity = new Date(lastActivityCreateTime);\n const now = new Date();\n const ageMs = now.getTime() - lastActivity.getTime();\n const ageDays = ageMs / (1000 * 60 * 60 * 24);\n return ageDays > thresholdDays;\n}\n","import {\n MediaArtifact,\n BashArtifact,\n ChangeSetArtifact,\n} from '../artifacts.js';\nimport { Activity, Artifact } from '../types.js';\nimport { ActivityStorage } from '../storage/types.js';\nimport { ActivityClient, ListOptions, SelectOptions } from './types.js';\nimport { createTimeToPageToken, isSessionFrozen } from '../utils/page-token.js';\n\n/**\n * Interface for the network layer used by the activity client.\n * Abstracts away the details of polling and fetching from the API.\n * @internal\n */\nexport interface NetworkClient {\n rawStream(): AsyncIterable<Activity>;\n listActivities(\n options?: ListOptions,\n ): Promise<{ activities: Activity[]; nextPageToken?: string }>;\n fetchActivity(activityId: string): Promise<Activity>;\n}\n\n/**\n * The default implementation of the ActivityClient.\n * Implements a \"local-first\" architecture where activities are fetched from\n * the network, cached locally, and then served from the cache.\n */\nexport class DefaultActivityClient implements ActivityClient {\n constructor(\n private storage: ActivityStorage,\n private network: NetworkClient,\n ) {}\n\n /**\n * Re-hydrates plain artifact objects from storage into rich class instances.\n * JSON serialization loses class information (methods), so we need to restore it.\n *\n * **Behavior:**\n * - Iterates through artifacts in an activity.\n * - If an artifact is a plain object (not a class instance), it's re-instantiated.\n * - Handles backward compatibility: if an artifact is already a class instance, it's skipped.\n *\n * @param activity The activity from storage, potentially with plain artifacts.\n * @returns The same activity with its artifacts guaranteed to be class instances.\n */\n private _hydrateActivityArtifacts(activity: Activity): Activity {\n if (!activity.artifacts || activity.artifacts.length === 0) {\n return activity;\n }\n\n const hydratedArtifacts = activity.artifacts.map((artifact) => {\n // If it's already a class instance, we're done.\n if (artifact instanceof MediaArtifact) return artifact;\n if (artifact instanceof BashArtifact) return artifact;\n if (artifact instanceof ChangeSetArtifact) return artifact;\n\n // It's a plain object from JSON.parse(), so we need to re-hydrate it.\n // We check for the 'type' property to know which class to use.\n switch (artifact.type) {\n case 'changeSet':\n // The raw cached format has artifact.changeSet.gitPatch structure.\n // We need to handle this legacy format gracefully.\n const rawChangeSet = (artifact as any).changeSet || artifact;\n return new ChangeSetArtifact(\n rawChangeSet.source,\n rawChangeSet.gitPatch,\n );\n case 'bashOutput':\n // The raw cached format has artifact.bashOutput...\n const rawBashOutput = (artifact as any).bashOutput || artifact;\n return new BashArtifact(rawBashOutput);\n case 'media':\n // MediaArtifact requires the platform object for some methods.\n // However, for local cache re-hydration, we don't have access to it here.\n // For now, we accept this limitation as the primary bug is with ChangeSetArtifact.\n // A future refactor could pass the platform object down.\n const rawMedia = (artifact as any).media || artifact;\n return new MediaArtifact(rawMedia, {} as any, activity.id);\n default:\n // If we don't recognize the type, return it as-is.\n return artifact as Artifact;\n }\n });\n\n return {\n ...activity,\n artifacts: hydratedArtifacts,\n };\n }\n\n /**\n * Returns an async iterable of all activities.\n *\n * **Behavior:**\n * - Always syncs new activities from the network first (via hydrate).\n * - Then yields all activities from local storage.\n *\n * This ensures callers always get the complete, up-to-date history\n * rather than potentially stale cached data.\n */\n async *history(): AsyncIterable<Activity> {\n // Always sync new activities from network before yielding.\n // This fixes the stale cache bug where history() would return\n // outdated data if the cache was populated earlier.\n await this.hydrate();\n\n // Now yield all activities from storage (including newly synced ones)\n for await (const activity of this.storage.scan()) {\n yield this._hydrateActivityArtifacts(activity);\n }\n }\n\n /**\n * Fetches all activities from the network and caches them.\n * Used to populate an empty cache.\n * @internal\n */\n private async *fetchAndCacheAll(): AsyncIterable<Activity> {\n let pageToken: string | undefined;\n\n do {\n const response = await this.network.listActivities({ pageToken });\n\n for (const activity of response.activities) {\n await this.storage.append(activity);\n yield activity;\n }\n\n pageToken = response.nextPageToken;\n } while (pageToken);\n }\n\n /**\n * Syncs new activities from the network to local cache.\n *\n * **Optimization Strategy:**\n * Activities are immutable - once downloaded, they never change.\n * We use the Jules API's pageToken (nanosecond timestamp) to fetch\n * only activities newer than our latest cached one.\n *\n * **Behavior:**\n * - Empty cache: Fetches all activities (no pageToken)\n * - Has cached activities: Constructs pageToken from latest createTime,\n * fetches only newer activities\n * - Frozen session (> 30 days): Skips API call entirely\n *\n * @returns The number of new activities synced.\n */\n async hydrate(): Promise<number> {\n await this.storage.init();\n\n // 1. Check for cached activities and establish high-water mark\n const latest = await this.storage.latest();\n\n // 2. Frozen session optimization: If the last activity is older than\n // 30 days, the session is frozen and no new activities will appear.\n if (latest?.createTime && isSessionFrozen(latest.createTime)) {\n return 0; // No API call needed\n }\n\n // 3. Construct pageToken from latest cached activity's createTime.\n // This tells the API to return only activities AFTER this timestamp.\n // If no cached activities, pageToken is undefined (fetch from beginning).\n const pageToken = latest?.createTime\n ? createTimeToPageToken(latest.createTime, true) // exclusive: activities AFTER this time\n : undefined;\n\n let count = 0;\n let nextPageToken: string | undefined = pageToken;\n\n do {\n const response = await this.network.listActivities({\n pageToken: nextPageToken,\n });\n\n for (const activity of response.activities) {\n // With pageToken, the API should only return newer activities.\n // But we still check for duplicates at the boundary to be safe\n // (handles edge case of multiple activities with identical timestamps).\n if (latest?.createTime) {\n const actTime = new Date(activity.createTime).getTime();\n const latestTime = new Date(latest.createTime).getTime();\n\n if (actTime === latestTime) {\n const existing = await this.storage.get(activity.id);\n if (existing) {\n continue;\n }\n }\n }\n\n // It's new - append to storage\n await this.storage.append(activity);\n count++;\n }\n\n nextPageToken = response.nextPageToken;\n } while (nextPageToken);\n\n return count;\n }\n\n /**\n * Returns an async iterable of new activities from the network.\n * This method polls the network and updates the local storage.\n *\n * **Side Effects:**\n * - Polls the network continuously.\n * - Appends new activities to local storage (write-through caching).\n *\n * **Logic:**\n * - Reads the latest activity from storage to determine the \"high-water mark\".\n * - Ignores incoming activities older than or equal to the high-water mark.\n */\n async *updates(): AsyncIterable<Activity> {\n await this.storage.init();\n\n // 1. Establish High-Water Mark\n // We only want events strictly NEWER than the last one we successfully stored.\n const latest = await this.storage.latest();\n // We use createTime as the primary cursor because it's standard and comparable.\n // Fallback to epoch 0 if storage is empty.\n let highWaterMark = latest?.createTime\n ? new Date(latest.createTime).getTime()\n : 0;\n // We also track the specific ID of the latest to handle events with identical timestamps.\n let lastSeenId = latest?.id;\n\n // 2. Start crude polling from the raw network source\n for await (const activity of this.network.rawStream()) {\n const actTime = new Date(activity.createTime).getTime();\n\n // 3. Deduplication Filter\n // If this activity is older than our high-water mark, skip it.\n if (actTime < highWaterMark) {\n continue;\n }\n\n // If it has the exact same time, we need to check IDs to avoid double-processing\n // the exact same event we used as our mark.\n if (actTime === highWaterMark && activity.id === lastSeenId) {\n continue;\n }\n\n // 4. It's new! Persist it FIRST for crash consistency.\n await this.storage.append(activity);\n\n // 5. Update our in-memory watermarks\n highWaterMark = actTime;\n lastSeenId = activity.id;\n\n // 6. Yield to the application\n yield activity;\n }\n }\n\n /**\n * Returns a combined stream of history and updates.\n * This is the primary method for consuming the activity stream.\n *\n * **Behavior:**\n * 1. Yields all historical activities from local storage (offline capable).\n * 2. Switches to `updates()` to yield new activities from the network (real-time).\n */\n async *stream(): AsyncIterable<Activity> {\n // The Hybrid is just a composition of the two modalities.\n // 1. Yield everything we already know safely from disk.\n yield* this.history();\n\n // 2. Switch to watching for new things.\n // Because updates() re-initializes its highWaterMark when called,\n // it will correctly pick up exactly where history() ended.\n yield* this.updates();\n }\n\n /**\n * Queries local storage for activities matching the given options.\n */\n async select(options: SelectOptions = {}): Promise<Activity[]> {\n await this.storage.init();\n const results: Activity[] = [];\n\n // State machine flags for cursor handling\n let started = !options.after; // If no 'after', start immediately\n let count = 0;\n\n for await (const act of this.storage.scan()) {\n // 1. Handle 'after' cursor (exclusive)\n if (!started) {\n if (act.id === options.after) {\n started = true;\n }\n continue;\n }\n\n // 2. Handle 'before' cursor (exclusive)\n if (options.before && act.id === options.before) {\n break;\n }\n\n // 3. Apply filters\n if (options.type && act.type !== options.type) {\n continue;\n }\n\n // 4. Collect result\n results.push(this._hydrateActivityArtifacts(act));\n count++;\n\n // 5. Check limits\n if (options.limit && count >= options.limit) {\n break;\n }\n }\n\n return results;\n }\n\n /**\n * Lists activities from the network directly.\n * @param options Pagination options.\n */\n async list(\n options?: ListOptions,\n ): Promise<{ activities: Activity[]; nextPageToken?: string }> {\n return this.network.listActivities(options);\n }\n\n /**\n * Gets a single activity by ID.\n * Implements a \"read-through\" caching strategy.\n *\n * **Logic:**\n * 1. Checks local storage. If found, returns it immediately (fast).\n * 2. If missing, fetches from the network.\n * 3. Persists the fetched activity to storage (future reads will hit cache).\n * 4. Returns the activity.\n *\n * **Side Effects:**\n * - May perform a network request.\n * - May write to local storage.\n */\n async get(activityId: string): Promise<Activity> {\n await this.storage.init();\n\n // 1. Try cache first (Aggressive Caching)\n const cached = await this.storage.get(activityId);\n if (cached) {\n return this._hydrateActivityArtifacts(cached);\n }\n\n // 2. Network fallback (Read-Through)\n const fresh = await this.network.fetchActivity(activityId);\n\n // 3. Persist for next time before returning\n // We await this to guarantee consistency.\n await this.storage.append(fresh);\n\n // No need to hydrate 'fresh' as it comes directly from the network mapper\n // which already creates class instances.\n return fresh;\n }\n}\n","import { ApiClient } from '../api.js';\nimport { NetworkClient } from '../activities/client.js';\nimport { Activity } from '../types.js';\nimport { ListOptions } from '../activities/types.js';\nimport { mapRestActivityToSdkActivity } from '../mappers.js';\n\nimport { Platform } from '../platform/types.js';\n\n/**\n * Concrete implementation of NetworkClient that communicates with the Jules API.\n * Handles fetching activities and streaming them via polling.\n */\nexport class NetworkAdapter implements NetworkClient {\n constructor(\n private apiClient: ApiClient,\n private sessionId: string,\n private pollingIntervalMs: number = 5000,\n private platform: Platform,\n ) {}\n\n /**\n * Fetches a single activity from the API.\n */\n async fetchActivity(activityId: string): Promise<Activity> {\n const restActivity = await this.apiClient.request<any>(\n `sessions/${this.sessionId}/activities/${activityId}`,\n );\n return mapRestActivityToSdkActivity(restActivity, this.platform);\n }\n\n /**\n * Lists activities from the API with pagination.\n */\n async listActivities(\n options?: ListOptions,\n ): Promise<{ activities: Activity[]; nextPageToken?: string }> {\n const params: Record<string, string> = {};\n if (options?.pageSize) {\n params.pageSize = options.pageSize.toString();\n }\n if (options?.pageToken) {\n params.pageToken = options.pageToken;\n }\n\n const response = await this.apiClient.request<{\n activities?: any[];\n nextPageToken?: string;\n }>(`sessions/${this.sessionId}/activities`, { query: params });\n\n return {\n activities: (response.activities || []).map((activity) =>\n mapRestActivityToSdkActivity(activity, this.platform),\n ),\n nextPageToken: response.nextPageToken,\n };\n }\n\n /**\n * Polls the API for new activities and yields them.\n * This stream never ends unless the process is terminated.\n */\n async *rawStream(): AsyncIterable<Activity> {\n while (true) {\n let pageToken: string | undefined = undefined;\n\n do {\n const response = await this.listActivities({ pageToken });\n\n for (const activity of response.activities) {\n yield activity;\n }\n\n pageToken = response.nextPageToken;\n } while (pageToken);\n\n await this.platform.sleep(this.pollingIntervalMs);\n }\n }\n}\n","import { SessionResource } from './types.js';\nimport { CachedSession } from './storage/types.js';\n\nconst ONE_MONTH_MS = 30 * 24 * 60 * 60 * 1000;\nconst ONE_DAY_MS = 24 * 60 * 60 * 1000;\n\nexport type CacheTier = 'hot' | 'warm' | 'frozen';\n\n/**\n * Determines the cache tier for a session based on its state and age.\n *\n * Strategy:\n * - **Frozen (Tier 3):** > 30 days old. Immutable.\n * - **Warm (Tier 2):** Terminal state + Verified < 24h ago. High read performance.\n * - **Hot (Tier 1):** Active or Stale. Requires network sync.\n */\nexport function determineCacheTier(\n cached: CachedSession,\n now: number = Date.now(),\n): CacheTier {\n const createdAt = new Date(cached.resource.createTime).getTime();\n const age = now - createdAt;\n const isTerminal = ['failed', 'completed'].includes(cached.resource.state);\n\n // TIER 3: FROZEN (Older than 1 month)\n if (age > ONE_MONTH_MS) {\n return 'frozen';\n }\n\n // TIER 2: WARM (Terminal state + synced recently)\n const timeSinceSync = now - cached._lastSyncedAt;\n if (isTerminal && timeSinceSync < ONE_DAY_MS) {\n return 'warm';\n }\n\n // TIER 1: HOT\n return 'hot';\n}\n\n/**\n * Helper to check if a cached session is valid to return immediately.\n * Returns true if the session is Frozen or Warm.\n */\nexport function isCacheValid(\n cached: CachedSession | undefined,\n now: number = Date.now(),\n): cached is CachedSession {\n if (!cached) return false;\n const tier = determineCacheTier(cached, now);\n return tier === 'frozen' || tier === 'warm';\n}\n","// src/snapshot.ts\nimport {\n Activity,\n PullRequest,\n SessionInsights,\n SessionResource,\n SessionSnapshot,\n SessionState,\n SerializedSnapshot,\n TimelineEntry,\n ActivityPlanGenerated,\n ActivitySessionFailed,\n ActivityUserMessaged,\n ActivityAgentMessaged,\n ActivityProgressUpdated,\n} from './types.js';\n\nexport class SessionSnapshotImpl implements SessionSnapshot {\n readonly id: string;\n readonly state: SessionState;\n readonly url: string;\n readonly createdAt: Date;\n readonly updatedAt: Date;\n readonly durationMs: number;\n readonly prompt: string;\n readonly title: string;\n readonly pr?: PullRequest;\n readonly activities: readonly Activity[];\n readonly activityCounts: Readonly<Record<string, number>>;\n readonly timeline: readonly TimelineEntry[];\n readonly insights: SessionInsights;\n\n constructor(session: SessionResource, activities: Activity[]) {\n this.id = session.id;\n this.state = session.state;\n this.url = session.url;\n this.createdAt = new Date(session.createTime);\n this.updatedAt = new Date(session.updateTime);\n this.durationMs = this.updatedAt.getTime() - this.createdAt.getTime();\n this.prompt = session.prompt;\n this.title = session.title;\n this.pr = session.outputs.find(\n (o) => o.type === 'pullRequest',\n )?.pullRequest;\n this.activities = Object.freeze(activities);\n\n // Compute derived views\n this.activityCounts = this.computeActivityCounts();\n this.timeline = this.computeTimeline();\n this.insights = this.computeInsights();\n\n // Make the instance immutable\n Object.freeze(this);\n }\n\n private computeActivityCounts(): Readonly<Record<string, number>> {\n const counts: Record<string, number> = {};\n for (const activity of this.activities) {\n counts[activity.type] = (counts[activity.type] || 0) + 1;\n }\n return counts;\n }\n\n private computeTimeline(): readonly TimelineEntry[] {\n return this.activities.map((activity) => ({\n time: activity.createTime,\n type: activity.type,\n summary: this.generateSummary(activity),\n }));\n }\n\n private generateSummary(activity: Activity): string {\n switch (activity.type) {\n case 'planGenerated':\n return `Plan with ${(activity as ActivityPlanGenerated).plan.steps.length} steps`;\n case 'planApproved':\n return 'Plan approved';\n case 'sessionCompleted':\n return 'Session completed';\n case 'sessionFailed':\n return `Failed: ${(activity as ActivitySessionFailed).reason}`;\n case 'userMessaged': {\n const msg = (activity as ActivityUserMessaged).message;\n return `User: ${msg.substring(0, 100)}${msg.length > 100 ? '...' : ''}`;\n }\n case 'agentMessaged': {\n const msg = (activity as ActivityAgentMessaged).message;\n return `Agent: ${msg.substring(0, 100)}${msg.length > 100 ? '...' : ''}`;\n }\n case 'progressUpdated': {\n const progress = activity as ActivityProgressUpdated;\n return progress.title || progress.description || 'Progress update';\n }\n default:\n // This case should be unreachable if all activity types are handled.\n // Casting to Activity bypasses the 'never' type inference.\n return (activity as Activity).type;\n }\n }\n\n private computeInsights(): SessionInsights {\n const failedCommands = this.activities.filter((activity) =>\n activity.artifacts.some((artifact) => {\n if (artifact.type === 'bashOutput') {\n return artifact.exitCode !== 0;\n }\n return false;\n }),\n );\n\n return {\n completionAttempts: this.activityCounts['sessionCompleted'] || 0,\n planRegenerations: this.activityCounts['planGenerated'] || 0,\n userInterventions: this.activityCounts['userMessaged'] || 0,\n failedCommands,\n };\n }\n\n toJSON(): SerializedSnapshot {\n return {\n id: this.id,\n state: this.state,\n url: this.url,\n createdAt: this.createdAt.toISOString(),\n updatedAt: this.updatedAt.toISOString(),\n durationMs: this.durationMs,\n prompt: this.prompt,\n title: this.title,\n activities: this.activities as Activity[],\n activityCounts: this.activityCounts,\n timeline: this.timeline as TimelineEntry[],\n insights: {\n completionAttempts: this.insights.completionAttempts,\n planRegenerations: this.insights.planRegenerations,\n userInterventions: this.insights.userInterventions,\n failedCommandCount: this.insights.failedCommands.length,\n },\n pr: this.pr,\n };\n }\n\n toMarkdown(): string {\n const lines: string[] = [];\n\n // Header\n lines.push(`# Session: ${this.title}`);\n lines.push(`**Status**: \\`${this.state}\\` | **ID**: \\`${this.id}\\``);\n lines.push('');\n\n // Stats & PR\n lines.push('## Overview');\n lines.push(`- **Duration**: ${Math.round(this.durationMs / 1000)}s`);\n lines.push(`- **Total Activities**: ${this.activities.length}`);\n if (this.pr) {\n lines.push(`- **Pull Request**: [${this.pr.title}](${this.pr.url})`);\n }\n lines.push('');\n\n // Insights\n lines.push('## Insights');\n lines.push(\n `- **Completion Attempts**: ${this.insights.completionAttempts}`,\n );\n lines.push(`- **Plan Regenerations**: ${this.insights.planRegenerations}`);\n lines.push(`- **User Interventions**: ${this.insights.userInterventions}`);\n lines.push(`- **Failed Commands**: ${this.insights.failedCommands.length}`);\n lines.push('');\n\n // Timeline\n lines.push('## Timeline');\n if (this.timeline.length === 0) {\n lines.push('_No activities recorded._');\n } else {\n for (const entry of this.timeline) {\n lines.push(`- **[${entry.type}]** ${entry.summary} _(${entry.time})_`);\n }\n }\n lines.push('');\n\n // Activity Counts\n if (Object.keys(this.activityCounts).length > 0) {\n lines.push('## Activity Counts');\n lines.push('```');\n for (const [type, count] of Object.entries(this.activityCounts)) {\n lines.push(`${type.padEnd(20)}: ${count}`);\n }\n lines.push('```');\n }\n\n return lines.join('\\n');\n }\n}\n","// src/session.ts\nimport { DefaultActivityClient } from './activities/client.js';\nimport { ActivityClient, SelectOptions } from './activities/types.js';\nimport { ApiClient, ApiRequestOptions } from './api.js';\nimport { InternalConfig } from './client.js';\nimport { InvalidStateError, JulesError } from './errors.js';\nimport { mapSessionResourceToOutcome } from './mappers.js';\nimport { NetworkAdapter } from './network/adapter.js';\nimport { pollSession, pollUntilCompletion } from './polling.js';\nimport { ActivityStorage, SessionStorage } from './storage/types.js';\nimport { StreamActivitiesOptions } from './streaming.js';\nimport {\n Activity,\n ActivityAgentMessaged,\n Outcome,\n SessionClient,\n SessionResource,\n SessionState,\n} from './types.js';\nimport { isCacheValid } from './caching.js';\nimport { SessionSnapshotImpl } from './snapshot.js';\nimport { SessionSnapshot } from './types.js';\n\n/**\n * Helper function to collect all items from an async iterable into an array.\n * @param iterable The async iterable to collect.\n * @returns A promise that resolves to an array of items.\n */\nasync function collectAsync<T>(iterable: AsyncIterable<T>): Promise<T[]> {\n const items: T[] = [];\n for await (const item of iterable) {\n items.push(item);\n }\n return items;\n}\n\n/**\n * Implementation of the SessionClient interface.\n * Manages an interactive session with the Jules agent.\n */\nexport class SessionClientImpl implements SessionClient {\n readonly id: string;\n private apiClient: ApiClient;\n private config: InternalConfig;\n private sessionStorage: SessionStorage; // Added property\n private _activities: ActivityClient;\n\n /**\n * Creates a new instance of SessionClientImpl.\n *\n * @param sessionId The ID of the session.\n * @param apiClient The API client to use for network requests.\n * @param config The configuration options.\n * @param activityStorage The storage engine for activities.\n * @param sessionStorage The storage engine for sessions.\n * @param platform The platform adapter.\n */\n constructor(\n sessionId: string,\n apiClient: ApiClient,\n config: InternalConfig,\n activityStorage: ActivityStorage,\n sessionStorage: SessionStorage, // Injected dependency\n platform: any,\n ) {\n this.id = sessionId.replace(/^sessions\\//, '');\n this.apiClient = apiClient;\n this.config = config;\n this.sessionStorage = sessionStorage;\n\n // --- WIRING THE NEW ENGINE ---\n const network = new NetworkAdapter(\n this.apiClient,\n this.id,\n this.config.pollingIntervalMs,\n platform,\n );\n\n this._activities = new DefaultActivityClient(activityStorage, network);\n }\n\n // Private helper wrapper to enforce resume context\n private async request<T>(path: string, options: ApiRequestOptions = {}) {\n return this.apiClient.request<T>(path, {\n ...options,\n // Always attach 'resume' context for this session instance\n handshake: { intent: 'resume', sessionId: this.id },\n });\n }\n\n /**\n * COLD STREAM: Yields all known past activities from local storage.\n * If local cache is empty, fetches from network first.\n */\n history(): AsyncIterable<Activity> {\n return this._activities.history();\n }\n\n /**\n * Forces a full sync of activities from the network to local cache.\n * @returns The number of new activities synced.\n */\n hydrate(): Promise<number> {\n return this._activities.hydrate();\n }\n\n /**\n * HOT STREAM: Yields ONLY future activities as they arrive from the network.\n */\n updates(): AsyncIterable<Activity> {\n return this._activities.updates();\n }\n\n /**\n * LOCAL QUERY: Performs rich filtering against local storage only.\n *\n * @deprecated Use `session.activities.select()` instead.\n */\n select(options?: SelectOptions): Promise<Activity[]> {\n return this._activities.select(options);\n }\n\n /**\n * Scoped access to activity-specific operations.\n */\n public get activities() {\n return this._activities;\n }\n\n /**\n * Provides a real-time stream of activities for the session.\n *\n * @param options Options to control the stream.\n */\n async *stream(\n options: StreamActivitiesOptions = {},\n ): AsyncIterable<Activity> {\n // Proxy to the new engine, preserving legacy filtering options.\n // The base .stream() does not yet support filtering, so we do it here.\n for await (const activity of this._activities.stream()) {\n if (\n options.exclude?.originator &&\n activity.originator === options.exclude.originator\n ) {\n continue;\n }\n yield activity;\n }\n }\n\n /**\n * Approves the currently pending plan.\n * Only valid if the session state is `awaitingPlanApproval`.\n *\n * **Side Effects:**\n * - Sends a POST request to `sessions/{id}:approvePlan`.\n * - Transitions the session state from `awaitingPlanApproval` to `inProgress` (eventually).\n *\n * @throws {InvalidStateError} If the session is not in the `awaitingPlanApproval` state.\n *\n * @example\n * await session.waitFor('awaitingPlanApproval');\n * await session.approve();\n */\n async approve(): Promise<void> {\n const currentState = (await this.info()).state;\n if (currentState !== 'awaitingPlanApproval') {\n throw new InvalidStateError(\n `Cannot approve plan because the session is not awaiting approval. Current state: ${currentState}`,\n );\n }\n await this.request(`sessions/${this.id}:approvePlan`, {\n method: 'POST',\n body: {},\n });\n }\n\n /**\n * Sends a message (prompt) to the agent in the context of the current session.\n * This is a fire-and-forget operation. To see the response, use `stream()` or `ask()`.\n *\n * **Side Effects:**\n * - Sends a POST request to `sessions/{id}:sendMessage`.\n * - Appends a new `userMessaged` activity to the session history.\n *\n * @param prompt The message to send.\n *\n * @example\n * await session.send(\"Please clarify step 2.\");\n */\n async send(prompt: string): Promise<void> {\n await this.request(`sessions/${this.id}:sendMessage`, {\n method: 'POST',\n body: { prompt },\n });\n }\n\n /**\n * Sends a message to the agent and waits specifically for the agent's immediate reply.\n * This provides a convenient request/response flow for conversational interactions.\n *\n * **Behavior:**\n * - Sends the prompt using `send()`.\n * - Subscribes to the activity stream.\n * - Resolves with the first `agentMessaged` activity that appears *after* the prompt was sent.\n *\n * @param prompt The message to send.\n * @returns The agent's reply activity.\n * @throws {JulesError} If the session terminates before the agent replies.\n *\n * @example\n * const reply = await session.ask(\"What is the status?\");\n * console.log(reply.message);\n */\n async ask(prompt: string): Promise<ActivityAgentMessaged> {\n const startTime = new Date();\n await this.send(prompt);\n\n // Don't return our own message.\n for await (const activity of this.stream({\n exclude: { originator: 'user' },\n })) {\n const activityTime = new Date(activity.createTime).getTime();\n const askTime = startTime.getTime();\n\n if (activityTime <= askTime) {\n continue;\n }\n\n if (activity.type === 'agentMessaged') {\n return activity;\n }\n }\n\n throw new JulesError('Session ended before the agent replied.');\n }\n\n /**\n * Waits for the session to reach a terminal state and returns the result.\n *\n * **Behavior:**\n * - Polls the session API until state is 'completed' or 'failed'.\n * - Maps the final session resource to a friendly `Outcome` object.\n *\n * @returns The final outcome of the session.\n * @throws {AutomatedSessionFailedError} If the session ends in a 'failed' state.\n */\n async result(): Promise<Outcome> {\n const finalSession = await pollUntilCompletion(\n this.id,\n this.apiClient,\n this.config.pollingIntervalMs,\n );\n // Write-Through: Persist final state\n await this.sessionStorage.upsert(finalSession);\n return mapSessionResourceToOutcome(finalSession);\n }\n\n /**\n * Pauses execution and waits until the session reaches a specific state.\n * Also returns if the session reaches a terminal state ('completed' or 'failed')\n * to prevent infinite waiting.\n *\n * **Behavior:**\n * - Polls the session API at the configured interval.\n * - Resolves immediately if the session is already in the target state (or terminal).\n *\n * @param targetState The target state to wait for.\n *\n * @example\n * await session.waitFor('awaitingPlanApproval');\n */\n async waitFor(targetState: SessionState): Promise<void> {\n await pollSession(\n this.id,\n this.apiClient,\n (session) => {\n return (\n session.state === targetState ||\n session.state === 'completed' ||\n session.state === 'failed'\n );\n },\n this.config.pollingIntervalMs,\n );\n }\n\n /**\n * Retrieves the latest state of the underlying session resource.\n * Implements \"Iceberg\" Read-Through caching.\n */\n async info(): Promise<SessionResource> {\n const cached = await this.sessionStorage.get(this.id);\n\n if (isCacheValid(cached)) {\n return cached.resource;\n }\n\n // TIER 1: HOT (Network Fetch)\n try {\n const fresh = await this.request<SessionResource>(`sessions/${this.id}`);\n await this.sessionStorage.upsert(fresh);\n return fresh;\n } catch (e: any) {\n if (e.status === 404 && cached) {\n await this.sessionStorage.delete(this.id);\n }\n throw e;\n }\n }\n\n /**\n * Creates a point-in-time snapshot of the session.\n * This is a network operation that fetches the latest session info and all activities.\n *\n * @returns A `SessionSnapshot` instance.\n */\n async snapshot(): Promise<SessionSnapshot> {\n const [info, activities] = await Promise.all([\n this.info(),\n collectAsync(this.history()),\n ]);\n return new SessionSnapshotImpl(info, activities);\n }\n}\n","/**\n * The internal engine for jules.all()\n *\n * @param items - Data to process\n * @param mapper - Async function (item) => result\n * @param options - Configuration options\n */\nexport async function pMap<T, R>(\n items: T[],\n mapper: (item: T, index: number) => Promise<R>,\n options: {\n concurrency?: number;\n stopOnError?: boolean;\n delayMs?: number;\n } = {},\n): Promise<R[]> {\n const concurrency = options.concurrency ?? 3;\n const stopOnError = options.stopOnError ?? true;\n const delayMs = options.delayMs ?? 0;\n\n const results = new Array<R>(items.length);\n const errors = new Array<Error | unknown>();\n let nextIndex = 0;\n\n const workers = new Array(concurrency).fill(0).map(async () => {\n while (true) {\n const index = nextIndex++;\n if (index >= items.length) {\n break;\n }\n const item = items[index];\n\n if (delayMs > 0) {\n await new Promise((resolve) => setTimeout(resolve, delayMs));\n }\n try {\n results[index] = await mapper(item, index);\n } catch (err) {\n if (stopOnError) {\n throw err;\n }\n errors.push(err);\n }\n }\n });\n\n await Promise.all(workers);\n\n if (!stopOnError && errors.length > 0) {\n throw new AggregateError(\n errors,\n 'Multiple errors occurred during jules.all()',\n );\n }\n\n return results;\n}\n","import { ApiClient } from './api.js';\nimport { SessionResource } from './types.js';\nimport { SessionStorage } from './storage/types.js';\n\nexport type ListSessionsOptions = {\n pageSize?: number;\n pageToken?: string;\n /**\n * Hard limit on the number of items to yield when iterating.\n * Useful if you want \"The last 50\" without manual counting.\n */\n limit?: number;\n /**\n * Whether to persist fetched sessions to local storage.\n * Defaults to `true` (Write-Through Caching).\n * Set to `false` to disable side effects.\n */\n persist?: boolean;\n};\n\nexport type ListSessionsResponse = {\n sessions: SessionResource[];\n nextPageToken?: string;\n};\n\n/**\n * The SessionCursor handles the complexity of pagination state.\n * It is \"Thenable\" (acts like a Promise) and \"AsyncIterable\".\n *\n * This allows two usage patterns:\n * 1. `await jules.sessions()` - Get the first page (Promise behavior).\n * 2. `for await (const session of jules.sessions())` - Stream all sessions (AsyncIterable behavior).\n *\n * **Design Notes:**\n * - **Pagination:** Handles `nextPageToken` automatically during iteration. For manual control,\n * access the `nextPageToken` property on the promised response.\n * - **Limiting:** The `limit` option hard-stops the iteration after N items, preventing over-fetching.\n * - **Write-Through Caching:** Fetched sessions are automatically persisted to local storage\n * using `storage.upsertMany()`. This ensures the local graph is populated during listing.\n * - **Platform:** Fully platform-agnostic (Node.js/Browser/GAS) via the injected `ApiClient`.\n */\nexport class SessionCursor\n implements PromiseLike<ListSessionsResponse>, AsyncIterable<SessionResource>\n{\n constructor(\n private apiClient: ApiClient,\n private storage: SessionStorage,\n private options: ListSessionsOptions = {},\n ) {}\n\n /**\n * DX Feature: Promise Compatibility.\n * Allows `const page = await jules.sessions()` to just get the first page.\n * This is great for UIs that render a list and a \"Load More\" button.\n */\n then<TResult1 = ListSessionsResponse, TResult2 = never>(\n onfulfilled?:\n | ((value: ListSessionsResponse) => TResult1 | PromiseLike<TResult1>)\n | null,\n onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | null,\n ): PromiseLike<TResult1 | TResult2> {\n // When used as a promise, we default to the pageToken from options\n return this.fetchPage(this.options.pageToken).then(onfulfilled, onrejected);\n }\n\n /**\n * DX Feature: Async Iterator.\n * Allows `for await (const s of jules.sessions())` to stream ALL items.\n * Automatically handles page tokens and fetching behind the scenes.\n */\n async *[Symbol.asyncIterator](): AsyncIterator<SessionResource> {\n let currentToken = this.options.pageToken;\n let itemCount = 0;\n const limit = this.options.limit ?? Infinity;\n\n do {\n // Check limit before fetching a whole new page\n if (itemCount >= limit) break;\n\n const response = await this.fetchPage(currentToken);\n\n // If no sessions returned, break\n if (!response.sessions || response.sessions.length === 0) {\n break;\n }\n\n for (const session of response.sessions) {\n if (itemCount >= limit) break;\n yield session;\n itemCount++;\n }\n\n currentToken = response.nextPageToken;\n } while (currentToken);\n }\n\n /**\n * Helper to fetch all pages into a single array.\n * WARNING: Use with caution on large datasets.\n */\n async all(): Promise<SessionResource[]> {\n const results: SessionResource[] = [];\n for await (const session of this) {\n results.push(session);\n }\n return results;\n }\n\n /**\n * Internal fetcher that maps the options to the REST parameters.\n */\n private async fetchPage(pageToken?: string): Promise<ListSessionsResponse> {\n const params: Record<string, string> = {};\n if (this.options.pageSize)\n params.pageSize = this.options.pageSize.toString();\n if (pageToken) params.pageToken = pageToken;\n\n // Use the existing ApiClient from your SDK\n const response = await this.apiClient.request<{\n sessions?: SessionResource[];\n nextPageToken?: string;\n }>('sessions', { query: params });\n\n const sessions = response.sessions || [];\n\n // Write-Through Cache: Persist fetched sessions immediately\n // Default to true if undefined\n if (sessions.length > 0 && this.options.persist !== false) {\n // We await to ensure data integrity\n await this.storage.upsertMany(sessions);\n }\n\n return {\n sessions,\n nextPageToken: response.nextPageToken,\n };\n }\n}\n","import * as fs from 'fs/promises';\nimport * as path from 'path';\nimport { Activity } from '../types.js';\nimport { getRootDir } from './root.js';\nimport { GlobalCacheMetadata, SessionMetadata } from './types.js';\n\n/**\n * Represents the cache information for a single session.\n */\nexport type SessionCacheInfo = {\n sessionId: string;\n activityCount: number;\n lastSyncedAt: Date;\n};\n\n/**\n * Represents global cache information.\n */\nexport type GlobalCacheInfo = {\n lastSyncedAt: Date;\n sessionCount: number;\n};\n\nconst GLOBAL_METADATA_FILE = 'global-metadata.json';\n\n/**\n * Retrieves cache information for a specific session.\n *\n * @param sessionId - The ID of the session.\n * @returns A promise that resolves with the session's cache information, or null if not found.\n */\nexport async function getSessionCacheInfo(\n sessionId: string,\n rootDirOverride?: string,\n): Promise<SessionCacheInfo | null> {\n const rootDir = rootDirOverride ?? getRootDir();\n const sessionDir = path.join(rootDir, '.jules/cache', sessionId);\n const sessionPath = path.join(sessionDir, 'session.json');\n const metadataPath = path.join(sessionDir, 'metadata.json');\n\n try {\n const sessionContent = await fs.readFile(sessionPath, 'utf8');\n const sessionData = JSON.parse(sessionContent);\n const lastSyncedAt = new Date(sessionData._lastSyncedAt);\n\n let activityCount = 0;\n try {\n const metadataContent = await fs.readFile(metadataPath, 'utf8');\n const metadata: SessionMetadata = JSON.parse(metadataContent);\n activityCount = metadata.activityCount;\n } catch (e: any) {\n if (e.code !== 'ENOENT') {\n console.warn(`Could not read metadata for session ${sessionId}:`, e);\n }\n // If metadata.json doesn't exist, activityCount remains 0.\n }\n\n return {\n sessionId,\n activityCount,\n lastSyncedAt,\n };\n } catch (e: any) {\n if (e.code === 'ENOENT') {\n return null; // Session not found\n }\n throw e;\n }\n}\n\n/**\n * The legacy implementation of getCacheInfo that scans all session directories.\n * Used as a fallback during migration.\n */\nasync function getCacheInfoLegacy(\n rootDirOverride?: string,\n): Promise<GlobalCacheInfo> {\n const rootDir = rootDirOverride ?? getRootDir();\n const cacheDir = path.join(rootDir, '.jules/cache');\n let lastSyncedAt = new Date(0);\n let sessionCount = 0;\n\n try {\n const sessionDirs = await fs.readdir(cacheDir, { withFileTypes: true });\n const sessionIds = sessionDirs\n .filter((dirent) => dirent.isDirectory())\n .map((dirent) => dirent.name);\n\n sessionCount = sessionIds.length;\n\n const syncTimes = await Promise.all(\n sessionIds.map(async (sessionId) => {\n const sessionPath = path.join(cacheDir, sessionId, 'session.json');\n try {\n const sessionContent = await fs.readFile(sessionPath, 'utf8');\n const sessionData = JSON.parse(sessionContent);\n return sessionData._lastSyncedAt as number;\n } catch {\n return 0;\n }\n }),\n );\n\n const mostRecentSync = Math.max(...syncTimes);\n if (mostRecentSync > 0) {\n lastSyncedAt = new Date(mostRecentSync);\n }\n } catch (e: any) {\n if (e.code !== 'ENOENT') {\n throw e;\n }\n // If the cache directory doesn't exist, return default values.\n }\n\n return {\n lastSyncedAt,\n sessionCount,\n };\n}\n\nexport async function updateGlobalCacheMetadata(\n rootDirOverride?: string,\n): Promise<void> {\n const rootDir = rootDirOverride ?? getRootDir();\n const cacheDir = path.join(rootDir, '.jules/cache');\n const metadataPath = path.join(cacheDir, GLOBAL_METADATA_FILE);\n\n // Read current or create new\n let metadata: GlobalCacheMetadata = { lastSyncedAt: 0, sessionCount: 0 };\n try {\n const content = await fs.readFile(metadataPath, 'utf8');\n metadata = JSON.parse(content);\n } catch {}\n\n // Update timestamp\n metadata.lastSyncedAt = Date.now();\n\n // Count sessions (only during update, not read)\n try {\n const entries = await fs.readdir(cacheDir, { withFileTypes: true });\n metadata.sessionCount = entries.filter((e) => e.isDirectory()).length;\n } catch {\n // If the directory doesn't exist, the count is 0.\n metadata.sessionCount = 0;\n }\n\n await fs.mkdir(cacheDir, { recursive: true });\n await fs.writeFile(metadataPath, JSON.stringify(metadata), 'utf8');\n}\n\n// Update getCacheInfo to read from global metadata first (O(1))\nexport async function getCacheInfo(\n rootDirOverride?: string,\n): Promise<GlobalCacheInfo> {\n const rootDir = rootDirOverride ?? getRootDir();\n const metadataPath = path.join(rootDir, '.jules/cache', GLOBAL_METADATA_FILE);\n\n try {\n const content = await fs.readFile(metadataPath, 'utf8');\n const metadata: GlobalCacheMetadata = JSON.parse(content);\n return {\n lastSyncedAt: new Date(metadata.lastSyncedAt),\n sessionCount: metadata.sessionCount,\n };\n } catch {\n // Fallback to scan (migration path)\n return getCacheInfoLegacy(rootDirOverride);\n }\n}\n\nexport async function getSessionCount(\n rootDirOverride?: string,\n): Promise<number> {\n const info = await getCacheInfo(rootDirOverride);\n return info.sessionCount;\n}\n\nexport async function getActivityCount(\n sessionId: string,\n rootDirOverride?: string,\n): Promise<number> {\n const info = await getSessionCacheInfo(sessionId, rootDirOverride);\n return info?.activityCount ?? 0;\n}\n\nexport async function getLatestActivities(\n sessionId: string,\n n: number,\n rootDirOverride?: string,\n): Promise<Activity[]> {\n const rootDir = rootDirOverride ?? getRootDir();\n const activitiesPath = path.join(\n rootDir,\n '.jules/cache',\n sessionId,\n 'activities.jsonl',\n );\n\n const CHUNK_SIZE = 1024 * 8; // 8KB\n let fileHandle;\n try {\n fileHandle = await fs.open(activitiesPath, 'r');\n const stats = await fileHandle.stat();\n let position = stats.size;\n let buffer = Buffer.alloc(CHUNK_SIZE);\n let tail = '';\n const lines: string[] = [];\n\n while (position > 0 && lines.length < n) {\n const readSize = Math.min(position, CHUNK_SIZE);\n position -= readSize;\n\n await fileHandle.read(buffer, 0, readSize, position);\n const chunk = buffer.toString('utf8', 0, readSize);\n const combined = chunk + tail;\n const chunkLines = combined.split('\\n');\n tail = chunkLines.shift() || '';\n\n for (let i = chunkLines.length - 1; i >= 0; i--) {\n if (chunkLines[i] && lines.length < n) {\n lines.unshift(chunkLines[i]);\n }\n }\n }\n\n if (lines.length < n && tail) {\n lines.unshift(tail);\n }\n\n const finalLines = lines.slice(-n).reverse();\n return finalLines.map((line) => JSON.parse(line));\n } catch (e: any) {\n if (e.code === 'ENOENT') return [];\n throw e;\n } finally {\n await fileHandle?.close();\n }\n}\n","/**\n * Projection Engine for Jules Query Language\n *\n * Supports:\n * - Dot notation field paths: \"plan.steps.title\"\n * - Wildcard inclusion: \"*\"\n * - Exclusion prefix: \"-artifacts.data\"\n * - Implicit array traversal: \"artifacts.type\" works on arrays\n */\n\n/**\n * Parsed select expression\n */\nexport interface SelectExpression {\n path: string[];\n exclude: boolean;\n wildcard: boolean;\n}\n\n/**\n * Parse a select expression string into structured form\n *\n * Examples:\n * - \"id\" → { path: [\"id\"], exclude: false, wildcard: false }\n * - \"plan.steps.title\" → { path: [\"plan\", \"steps\", \"title\"], exclude: false, wildcard: false }\n * - \"-artifacts.data\" → { path: [\"artifacts\", \"data\"], exclude: true, wildcard: false }\n * - \"*\" → { path: [], exclude: false, wildcard: true }\n */\nexport function parseSelectExpression(expr: string): SelectExpression {\n if (expr === '*') {\n return { path: [], exclude: false, wildcard: true };\n }\n\n const exclude = expr.startsWith('-');\n const pathStr = exclude ? expr.slice(1) : expr;\n\n // Remove optional array markers like \"[]\" - they're implicit\n const cleanPath = pathStr.replace(/\\[\\]/g, '');\n const path = cleanPath.split('.').filter((p) => p.length > 0);\n\n return { path, exclude, wildcard: false };\n}\n\n/**\n * Get a value at a nested path, handling arrays transparently\n *\n * For paths that traverse arrays, returns an array of values from each element.\n *\n * Examples:\n * - getPath({a: {b: 1}}, [\"a\", \"b\"]) → 1\n * - getPath({items: [{x: 1}, {x: 2}]}, [\"items\", \"x\"]) → [1, 2]\n */\nexport function getPath(obj: unknown, path: string[]): unknown {\n if (path.length === 0) return obj;\n if (obj === null || obj === undefined) return undefined;\n\n const [head, ...tail] = path;\n\n if (Array.isArray(obj)) {\n // Map over array elements and collect values\n const results = obj\n .map((item) => getPath(item, path))\n .filter((v) => v !== undefined);\n return results.length > 0 ? results : undefined;\n }\n\n if (typeof obj === 'object') {\n const value = (obj as Record<string, unknown>)[head];\n return getPath(value, tail);\n }\n\n return undefined;\n}\n\n/**\n * Set a value at a nested path, creating intermediate objects as needed\n *\n * For array paths, preserves array structure.\n */\nexport function setPath(\n obj: Record<string, unknown>,\n path: string[],\n value: unknown,\n): void {\n if (path.length === 0 || value === undefined) return;\n\n const [head, ...tail] = path;\n\n if (tail.length === 0) {\n obj[head] = value;\n return;\n }\n\n if (!(head in obj)) {\n obj[head] = {};\n }\n\n const next = obj[head];\n if (typeof next === 'object' && next !== null && !Array.isArray(next)) {\n setPath(next as Record<string, unknown>, tail, value);\n }\n}\n\n/**\n * Delete a value at a nested path\n *\n * For paths ending in array elements, removes the field from each element.\n */\nexport function deletePath(obj: unknown, path: string[]): void {\n if (path.length === 0 || obj === null || obj === undefined) return;\n\n if (Array.isArray(obj)) {\n obj.forEach((item) => deletePath(item, path));\n return;\n }\n\n if (typeof obj !== 'object') return;\n\n const record = obj as Record<string, unknown>;\n const [head, ...tail] = path;\n\n if (tail.length === 0) {\n delete record[head];\n return;\n }\n\n if (head in record) {\n deletePath(record[head], tail);\n }\n}\n\n/**\n * Deep clone an object\n */\nexport function deepClone<T>(obj: T): T {\n if (obj === null || typeof obj !== 'object') return obj;\n if (Array.isArray(obj)) return obj.map((item) => deepClone(item)) as T;\n\n const cloned: Record<string, unknown> = {};\n for (const key of Object.keys(obj)) {\n cloned[key] = deepClone((obj as Record<string, unknown>)[key]);\n }\n return cloned as T;\n}\n\n/**\n * Project an array value based on sub-paths\n *\n * Given an array and paths that start with common prefix,\n * project each element to include only the specified sub-fields.\n */\nfunction projectArray(\n arr: unknown[],\n subPaths: string[][],\n excludePaths: string[][],\n): unknown[] {\n return arr.map((item) => {\n if (item === null || typeof item !== 'object') return item;\n\n const projected: Record<string, unknown> = {};\n\n for (const subPath of subPaths) {\n const value = getPath(item, subPath);\n if (value !== undefined) {\n if (subPath.length === 0) {\n // Include all fields from this item\n Object.assign(projected, deepClone(item));\n } else {\n setPath(projected, subPath, deepClone(value));\n }\n }\n }\n\n // Apply exclusions\n for (const excludePath of excludePaths) {\n deletePath(projected, excludePath);\n }\n\n return projected;\n });\n}\n\n/**\n * Project a document according to select expressions\n *\n * @param doc The source document\n * @param selects Array of select expression strings\n * @returns Projected document with only selected fields\n */\nexport function projectDocument(\n doc: Record<string, unknown>,\n selects: string[],\n): Record<string, unknown> {\n if (!selects || selects.length === 0) {\n // No selection = return as-is (default projection handled elsewhere)\n return doc;\n }\n\n const parsed = selects.map(parseSelectExpression);\n const hasWildcard = parsed.some((p) => p.wildcard && !p.exclude);\n const inclusions = parsed.filter((p) => !p.exclude && !p.wildcard);\n const exclusions = parsed.filter((p) => p.exclude);\n\n let result: Record<string, unknown>;\n\n if (hasWildcard) {\n // Start with full clone, then apply exclusions\n result = deepClone(doc);\n } else {\n // Start with empty, add inclusions\n result = {};\n\n // Group inclusions by top-level field for efficient array handling\n const byTopLevel = new Map<string, string[][]>();\n\n for (const incl of inclusions) {\n if (incl.path.length === 0) continue;\n const top = incl.path[0];\n if (!byTopLevel.has(top)) {\n byTopLevel.set(top, []);\n }\n byTopLevel.get(top)!.push(incl.path.slice(1));\n }\n\n for (const [topField, subPaths] of byTopLevel) {\n const value = doc[topField];\n if (value === undefined) continue;\n\n if (Array.isArray(value)) {\n // Handle array projection\n const exclusionSubPaths = exclusions\n .filter((e) => e.path[0] === topField)\n .map((e) => e.path.slice(1));\n\n if (subPaths.some((p) => p.length === 0)) {\n // Include full array (possibly with exclusions)\n result[topField] = projectArray(value, [[]], exclusionSubPaths);\n } else {\n // Project specific sub-fields from array elements\n result[topField] = projectArray(value, subPaths, exclusionSubPaths);\n }\n } else if (typeof value === 'object' && value !== null) {\n // Handle nested object\n if (subPaths.some((p) => p.length === 0)) {\n // Include full object\n result[topField] = deepClone(value);\n } else {\n // Recursively project nested fields\n const nestedSelects = subPaths.map((p) => p.join('.'));\n result[topField] = projectDocument(\n value as Record<string, unknown>,\n nestedSelects,\n );\n }\n } else {\n // Primitive value\n result[topField] = value;\n }\n }\n }\n\n // Apply exclusions\n for (const excl of exclusions) {\n deletePath(result, excl.path);\n }\n\n return result;\n}\n\n/**\n * Check if a path is a prefix of another path\n */\nexport function isPathPrefix(prefix: string[], path: string[]): boolean {\n if (prefix.length > path.length) return false;\n return prefix.every((p, i) => p === path[i]);\n}\n","import type {\n Activity,\n ActivitySummary,\n ActivityAgentMessaged,\n ActivityPlanGenerated,\n ActivityProgressUpdated,\n ActivityUserMessaged,\n ActivitySessionFailed,\n} from '../types.js';\n\nconst MAX_SUMMARY_LENGTH = 200;\n\n/**\n * Creates a concise summary for an activity.\n * @param activity The activity to summarize.\n * @returns A summary of the activity.\n */\nexport function toSummary(activity: Activity): ActivitySummary {\n const { id, type, createTime } = activity;\n let summary: string = type; // Default fallback is the type name\n\n switch (activity.type) {\n case 'agentMessaged':\n case 'userMessaged': {\n const message = (activity as ActivityAgentMessaged | ActivityUserMessaged)\n .message;\n if (!message || message.length === 0) {\n summary = type; // Fallback to type name for empty messages\n } else if (message.length > MAX_SUMMARY_LENGTH) {\n summary = message.substring(0, MAX_SUMMARY_LENGTH) + '...';\n } else {\n summary = message;\n }\n break;\n }\n case 'progressUpdated': {\n const progress = activity as ActivityProgressUpdated;\n // Handle missing title/description gracefully\n if (progress.title && progress.description) {\n summary = `${progress.title}: ${progress.description}`;\n } else if (progress.title) {\n summary = progress.title;\n } else if (progress.description) {\n summary = progress.description;\n }\n // else: fallback to type name (already set)\n break;\n }\n case 'planGenerated': {\n const plan = activity as ActivityPlanGenerated;\n const stepCount = plan.plan?.steps?.length ?? 0;\n summary = `Plan generated with ${stepCount} steps`;\n break;\n }\n case 'planApproved':\n summary = 'Plan approved';\n break;\n case 'sessionCompleted':\n summary = 'Session completed';\n break;\n case 'sessionFailed': {\n const failed = activity as ActivitySessionFailed;\n summary = failed.reason\n ? `Session failed: ${failed.reason}`\n : 'Session failed';\n break;\n }\n }\n\n return { id, type, createTime, summary };\n}\n","/**\n * Computed Fields for Jules Query Language\n *\n * Computed fields are derived at query time, not stored.\n * They can be selected but not filtered.\n */\n\nimport { Activity } from '../types.js';\nimport { toSummary } from '../activity/summary.js';\n\n/**\n * List of computed field names for activities\n */\nexport const ACTIVITY_COMPUTED_FIELDS = ['artifactCount', 'summary'] as const;\n\n/**\n * List of computed field names for sessions\n */\nexport const SESSION_COMPUTED_FIELDS = ['durationMs'] as const;\n\n/**\n * Check if a field name is a computed field for activities\n */\nexport function isActivityComputedField(field: string): boolean {\n return ACTIVITY_COMPUTED_FIELDS.includes(\n field as (typeof ACTIVITY_COMPUTED_FIELDS)[number],\n );\n}\n\n/**\n * Check if a field name is a computed field for sessions\n */\nexport function isSessionComputedField(field: string): boolean {\n return SESSION_COMPUTED_FIELDS.includes(\n field as (typeof SESSION_COMPUTED_FIELDS)[number],\n );\n}\n\n/**\n * Compute the artifactCount for an activity\n */\nexport function computeArtifactCount(activity: Activity): number {\n return activity.artifacts?.length ?? 0;\n}\n\n/**\n * Compute the summary for an activity\n * Delegates to existing toSummary implementation\n */\nexport function computeSummary(activity: Activity): string {\n return toSummary(activity).summary;\n}\n\n/**\n * Compute the duration in milliseconds for a session\n */\nexport function computeDurationMs(session: {\n createTime?: string;\n updateTime?: string;\n}): number {\n if (!session.createTime || !session.updateTime) return 0;\n\n const created = new Date(session.createTime).getTime();\n const updated = new Date(session.updateTime).getTime();\n\n if (isNaN(created) || isNaN(updated)) return 0;\n\n return Math.max(0, updated - created);\n}\n\n/**\n * Inject computed fields into an activity based on selected fields\n *\n * @param activity The activity to augment\n * @param selectFields The fields being selected (or undefined for default)\n * @returns Activity with computed fields added\n */\nexport function injectActivityComputedFields(\n activity: Activity,\n selectFields?: string[],\n): Activity & { artifactCount?: number; summary?: string } {\n const result = { ...activity } as Activity & {\n artifactCount?: number;\n summary?: string;\n };\n\n // Determine which computed fields to include\n const includeAll =\n !selectFields || selectFields.length === 0 || selectFields.includes('*');\n\n const needsArtifactCount =\n includeAll || selectFields?.includes('artifactCount');\n const needsSummary = includeAll || selectFields?.includes('summary');\n\n if (needsArtifactCount) {\n result.artifactCount = computeArtifactCount(activity);\n }\n\n if (needsSummary) {\n result.summary = computeSummary(activity);\n }\n\n return result;\n}\n\n/**\n * Inject computed fields into a session based on selected fields\n *\n * @param session The session to augment\n * @param selectFields The fields being selected (or undefined for default)\n * @returns Session with computed fields added\n */\nexport function injectSessionComputedFields<\n T extends { createTime?: string; updateTime?: string },\n>(session: T, selectFields?: string[]): T & { durationMs?: number } {\n const result = { ...session } as T & { durationMs?: number };\n\n const includeAll =\n !selectFields || selectFields.length === 0 || selectFields.includes('*');\n\n const needsDurationMs = includeAll || selectFields?.includes('durationMs');\n\n if (needsDurationMs) {\n result.durationMs = computeDurationMs(session);\n }\n\n return result;\n}\n\n/**\n * Default projection fields for activities (includes computed fields)\n */\nexport const DEFAULT_ACTIVITY_PROJECTION = [\n 'id',\n 'type',\n 'createTime',\n 'originator',\n 'artifactCount',\n 'summary',\n];\n\n/**\n * Default projection fields for sessions\n */\nexport const DEFAULT_SESSION_PROJECTION = [\n 'id',\n 'state',\n 'title',\n 'createTime',\n];\n","import {\n JulesClient,\n JulesQuery,\n JulesDomain,\n QueryResult,\n FilterOp,\n WhereClause,\n SelectOptions,\n Activity,\n} from '../types.js';\nimport { pMap } from '../utils.js';\nimport { projectDocument, getPath } from './projection.js';\nimport {\n injectActivityComputedFields,\n injectSessionComputedFields,\n DEFAULT_ACTIVITY_PROJECTION,\n DEFAULT_SESSION_PROJECTION,\n} from './computed.js';\n\n/**\n * Matches a value against a FilterOp.\n */\nfunction match<V>(actual: V, filter?: FilterOp<V>): boolean {\n if (filter === undefined) return true;\n if (typeof filter !== 'object' || filter === null || Array.isArray(filter)) {\n return actual === filter;\n }\n\n const op = filter as {\n eq?: V;\n neq?: V;\n contains?: string;\n gt?: V;\n lt?: V;\n gte?: V;\n lte?: V;\n in?: V[];\n exists?: boolean;\n };\n\n // Handle exists operator\n if (op.exists !== undefined) {\n const valueExists = actual !== undefined && actual !== null;\n return op.exists ? valueExists : !valueExists;\n }\n\n if (op.eq !== undefined && actual !== op.eq) return false;\n if (op.neq !== undefined && actual === op.neq) return false;\n if (\n op.contains !== undefined &&\n typeof actual === 'string' &&\n !actual.toLowerCase().includes(op.contains.toLowerCase())\n )\n return false;\n if (op.gt !== undefined && op.gt !== null && actual <= op.gt) return false;\n if (op.gte !== undefined && op.gte !== null && actual < op.gte) return false;\n if (op.lt !== undefined && op.lt !== null && actual >= op.lt) return false;\n if (op.lte !== undefined && op.lte !== null && actual > op.lte) return false;\n if (op.in !== undefined && !op.in.includes(actual)) return false;\n\n return true;\n}\n\n/**\n * Check if a where key uses dot notation (nested path)\n */\nfunction isDotPath(key: string): boolean {\n return key.includes('.');\n}\n\n/**\n * Match a document against a filter using dot notation paths\n * Uses existential quantification for array paths\n */\nfunction matchPath(\n doc: unknown,\n path: string,\n filter: FilterOp<unknown>,\n): boolean {\n const pathParts = path.split('.');\n const value = getPath(doc, pathParts);\n\n // For arrays, use existential matching (ANY element matches)\n if (Array.isArray(value)) {\n return value.some((v) => match(v, filter));\n }\n\n return match(value, filter);\n}\n\n/**\n * Match a document against a full where clause with dot notation support\n */\nfunction matchWhere(\n doc: unknown,\n where?: Record<string, FilterOp<unknown>>,\n): boolean {\n if (!where) return true;\n\n for (const [key, filter] of Object.entries(where)) {\n if (isDotPath(key)) {\n // Use path-based matching\n if (!matchPath(doc, key, filter)) return false;\n } else {\n // Use direct field matching\n const value = (doc as Record<string, unknown>)[key];\n if (!match(value, filter)) return false;\n }\n }\n\n return true;\n}\n\n/**\n * Helper to convert WhereClause<'activities'> to SelectOptions.\n * Note: ActivityClient.select currently takes a simpler SelectOptions object.\n * We'll map what we can.\n */\nfunction toActivitySelectOptions(\n where?: WhereClause<'activities'>,\n): SelectOptions {\n if (!where) return {};\n const options: SelectOptions = {};\n\n // Simple mapping for 'type' if it's an equality check\n if (where.type) {\n if (typeof where.type === 'string') {\n options.type = where.type;\n } else if (\n typeof where.type === 'object' &&\n 'eq' in where.type &&\n where.type.eq\n ) {\n options.type = where.type.eq;\n }\n }\n\n return options;\n}\n\n/**\n * Apply projection to a document, handling computed fields\n */\nfunction applyProjection(\n doc: unknown,\n select: string[] | undefined,\n domain: 'sessions' | 'activities',\n): Record<string, unknown> {\n const docRecord = doc as Record<string, unknown>;\n\n // Inject computed fields first\n const withComputed =\n domain === 'activities'\n ? injectActivityComputedFields(doc as Activity, select)\n : injectSessionComputedFields(docRecord, select);\n\n // If no select specified, use default projection\n if (!select) {\n const defaults =\n domain === 'activities'\n ? DEFAULT_ACTIVITY_PROJECTION\n : DEFAULT_SESSION_PROJECTION;\n return projectDocument(withComputed as Record<string, unknown>, defaults);\n }\n\n // If empty array or contains only '*', return all with computed\n if (select.length === 0) {\n return withComputed as Record<string, unknown>;\n }\n\n // Apply projection engine\n return projectDocument(withComputed as Record<string, unknown>, select);\n}\n\n/**\n * Standalone query engine function.\n * Handles planning, index scanning, and hydration.\n */\nexport async function select<T extends JulesDomain>(\n client: JulesClient,\n query: JulesQuery<T>,\n): Promise<QueryResult<T>[]> {\n const storage = client.storage;\n const results: Record<string, unknown>[] = [];\n const limit = query.limit ?? Infinity;\n\n if (query.from === 'sessions') {\n const where = query.where as WhereClause<'sessions'> | undefined;\n\n // PASS 1: Index Scan (Metadata Only)\n for await (const entry of storage.scanIndex()) {\n if (results.length >= limit) break;\n\n // Filter by ID\n if (where?.id && !match(entry.id, where.id)) continue;\n // Filter by State\n if (where?.state && !match(entry.state, where.state)) continue;\n // Filter by Title (Fuzzy Search or specific title)\n if (where?.title && !match(entry.title, where.title)) continue;\n // Global Search\n if (\n where?.search &&\n !entry.title.toLowerCase().includes(where.search.toLowerCase())\n )\n continue;\n\n // PASS 2: Hydration (Heavy Data)\n const cached = await storage.get(entry.id);\n if (!cached) continue;\n\n // Check dot-notation filters against full document\n const whereRecord = where as\n | Record<string, FilterOp<unknown>>\n | undefined;\n const dotFilters = whereRecord\n ? Object.entries(whereRecord).filter(([k]) => isDotPath(k))\n : [];\n\n if (dotFilters.length > 0) {\n const dotWhere = Object.fromEntries(dotFilters);\n if (!matchWhere(cached.resource, dotWhere)) continue;\n }\n\n const item = applyProjection(\n cached.resource,\n query.select as string[] | undefined,\n 'sessions',\n );\n\n // Preserve sorting metadata from original document\n const resourceRecord = cached.resource as unknown as Record<\n string,\n unknown\n >;\n item._sortKey = {\n createTime: resourceRecord.createTime,\n id: resourceRecord.id,\n };\n\n results.push(item);\n }\n\n // PASS 3: Virtual Join (Include Activities)\n if (query.include && 'activities' in query.include) {\n const actConfig = query.include.activities;\n let mappedOptions: SelectOptions & { limit?: number } = {};\n if (typeof actConfig === 'object') {\n mappedOptions = {\n ...toActivitySelectOptions(actConfig.where),\n limit: actConfig.limit,\n };\n }\n\n await pMap(\n results,\n async (session) => {\n const sessionClient = await client.session(session.id as string);\n const localActivities = await sessionClient.activities.select({});\n const activities: Record<string, unknown>[] = [];\n for (const act of localActivities) {\n if (\n mappedOptions.limit &&\n activities.length >= mappedOptions.limit\n ) {\n break;\n }\n if (mappedOptions.type && act.type !== mappedOptions.type) {\n continue;\n }\n activities.push(act as unknown as Record<string, unknown>);\n }\n session.activities = activities;\n },\n { concurrency: 5 },\n );\n }\n } else if (query.from === 'activities') {\n const where = query.where as Record<string, FilterOp<unknown>> | undefined;\n\n // Optimization: Target specific session if ID is provided\n let targetSessionIds: string[] = [];\n\n if (where?.sessionId) {\n if (typeof where.sessionId === 'string') {\n targetSessionIds = [where.sessionId];\n } else if (\n typeof where.sessionId === 'object' &&\n 'eq' in where.sessionId &&\n where.sessionId.eq\n ) {\n targetSessionIds = [where.sessionId.eq as string];\n }\n }\n\n // Use a session cache to avoid N+1 fetches for session info\n const sessionCache = new Map<string, Record<string, unknown>>();\n\n // Generator for session IDs to scan\n const sessionScanner = async function* () {\n if (targetSessionIds.length > 0) {\n for (const id of targetSessionIds) {\n yield { id };\n }\n } else {\n yield* storage.scanIndex();\n }\n };\n\n // PASS 1: Scatter-Gather (Cross-session activity search)\n for await (const sessionEntry of sessionScanner()) {\n const sessionClient = await client.session(sessionEntry.id);\n const localActivities = await sessionClient.activities.select({});\n\n for (const act of localActivities) {\n // Apply standard filters\n if (where?.id && !match(act.id, where.id)) continue;\n if (where?.type && !match(act.type, where.type)) continue;\n\n // Apply dot-notation filters with existential matching\n // Exclude sessionId from activity-level matching since it's handled by session routing\n const activityWhere = where\n ? Object.fromEntries(\n Object.entries(where).filter(([k]) => k !== 'sessionId'),\n )\n : undefined;\n if (!matchWhere(act, activityWhere)) continue;\n\n const item = applyProjection(\n act,\n query.select as string[] | undefined,\n 'activities',\n );\n\n // Preserve sorting metadata from original document\n const actRecord = act as unknown as Record<string, unknown>;\n item._sortKey = {\n createTime: actRecord.createTime,\n id: actRecord.id,\n };\n\n // PASS 2: Reverse Join (Include Session Metadata)\n if (query.include && 'session' in query.include) {\n const sessConfig = query.include.session;\n const sessSelect =\n typeof sessConfig === 'object' ? sessConfig.select : undefined;\n\n let sessionInfo = sessionCache.get(sessionEntry.id);\n if (!sessionInfo) {\n const info = await sessionClient.info();\n sessionInfo = info as unknown as Record<string, unknown>;\n sessionCache.set(sessionEntry.id, sessionInfo);\n }\n\n item.session = applyProjection(\n sessionInfo,\n sessSelect as string[] | undefined,\n 'sessions',\n );\n }\n\n results.push(item);\n }\n }\n }\n\n // Sorting - use _sortKey if available, fallback to document fields\n const order = query.order ?? 'desc';\n results.sort((a, b) => {\n const sortKeyA = a._sortKey as\n | { createTime: string; id: string }\n | undefined;\n const sortKeyB = b._sortKey as\n | { createTime: string; id: string }\n | undefined;\n const timeA = new Date(\n (sortKeyA?.createTime ?? a.createTime) as string,\n ).getTime();\n const timeB = new Date(\n (sortKeyB?.createTime ?? b.createTime) as string,\n ).getTime();\n const idA = (sortKeyA?.id ?? a.id) as string;\n const idB = (sortKeyB?.id ?? b.id) as string;\n if (timeA !== timeB) {\n return order === 'desc' ? timeB - timeA : timeA - timeB;\n }\n if (order === 'desc') {\n return idB.localeCompare(idA);\n }\n return idA.localeCompare(idB);\n });\n\n let finalResults = results;\n\n // Handle cursor pagination (before removing _sortKey so we can use the id)\n const cursorId = query.startAfter ?? query.startAt;\n if (cursorId) {\n const cursorIndex = finalResults.findIndex((item) => {\n const sortKey = item._sortKey as { id: string } | undefined;\n const itemId = sortKey?.id ?? item.id;\n return itemId === cursorId;\n });\n if (cursorIndex === -1) {\n return [];\n }\n const sliceIndex = query.startAfter ? cursorIndex + 1 : cursorIndex;\n finalResults = finalResults.slice(sliceIndex);\n }\n\n // Remove _sortKey from results\n for (const result of finalResults) {\n delete result._sortKey;\n }\n\n return finalResults.slice(0, limit) as unknown as QueryResult<T>[];\n}\n","// src/client.ts\nimport { ApiClient } from './api.js';\nimport { createSourceManager } from './sources.js';\nimport { join } from 'node:path';\nimport { getRootDir } from './storage/root.js';\nimport {\n JulesClient,\n JulesOptions,\n SessionConfig,\n SourceManager,\n AutomatedSession,\n SessionClient,\n Outcome,\n SessionResource,\n StorageFactory,\n} from './types.js';\nimport { SourceNotFoundError, SyncInProgressError } from './errors.js';\nimport { streamActivities } from './streaming.js';\nimport { pollUntilCompletion } from './polling.js';\nimport { mapSessionResourceToOutcome } from './mappers.js';\nimport { SessionClientImpl } from './session.js';\nimport { pMap } from './utils.js';\nimport { SessionCursor, ListSessionsOptions } from './sessions.js';\nimport { Platform } from './platform/types.js';\nimport { SessionStorage } from './storage/types.js';\nimport { isCacheValid } from './caching.js';\nimport { updateGlobalCacheMetadata } from './storage/cache-info.js';\nimport { select as modularSelect } from './query/select.js';\nimport {\n JulesQuery,\n JulesDomain,\n QueryResult,\n SyncCheckpoint,\n SyncOptions,\n SyncStats,\n} from './types.js';\n\n/**\n * The fully resolved internal configuration for the SDK.\n * @internal\n */\nexport type InternalConfig = {\n pollingIntervalMs: number;\n requestTimeoutMs: number;\n};\n\n/**\n * Implementation of the main JulesClient interface.\n * This class acts as the central hub for creating and managing sessions,\n * as well as accessing other resources like sources.\n */\nexport class JulesClientImpl implements JulesClient {\n /**\n * Manages source connections (e.g., GitHub repositories).\n */\n public sources: SourceManager;\n // Expose storage for modular functions (Phase 3 requirement)\n public readonly storage: SessionStorage;\n\n private apiClient: ApiClient;\n private config: InternalConfig;\n private options: JulesOptions;\n private storageFactory: StorageFactory;\n private platform: Platform;\n\n /**\n * Lock to prevent concurrent sync operations.\n * Using a simple boolean for in-process locking.\n */\n private syncInProgress: boolean = false;\n\n /**\n * Creates a new instance of the JulesClient.\n *\n * @param options Configuration options for the client.\n * @param defaultStorageFactory Factory for creating storage instances.\n * @param defaultPlatform Platform-specific implementation.\n */\n constructor(\n options: JulesOptions = {},\n defaultStorageFactory: StorageFactory,\n defaultPlatform: Platform,\n ) {\n this.options = options;\n this.storageFactory = options.storageFactory ?? defaultStorageFactory;\n this.platform = options.platform ?? defaultPlatform;\n\n // Phase 1 / Phase 2 Integration: Initialize Session Storage\n // NOTE: This assumes StorageFactory was updated to { activity: ..., session: ... } in Phase 1\n this.storage = this.storageFactory.session();\n\n // 1. Resolve Proxy Configuration\n const envProxyUrl = this.getEnv('JULES_PROXY');\n const envSecret = this.getEnv('JULES_SECRET');\n\n // Priority: Options > Env > Default (Node Only)\n if (!options.proxy && envProxyUrl) {\n options.proxy = {\n url: envProxyUrl,\n auth: envSecret ? () => envSecret : undefined,\n };\n }\n\n const apiKey =\n options.apiKey_TEST_ONLY_DO_NOT_USE_IN_PRODUCTION ??\n options.apiKey ??\n this.platform.getEnv('JULES_API_KEY');\n const baseUrl = options.baseUrl ?? 'https://jules.googleapis.com/v1alpha';\n\n // Apply defaults to the user-provided config\n this.config = {\n pollingIntervalMs: options.config?.pollingIntervalMs ?? 5000,\n requestTimeoutMs: options.config?.requestTimeoutMs ?? 30000,\n };\n\n this.apiClient = new ApiClient({\n apiKey,\n baseUrl,\n requestTimeoutMs: this.config.requestTimeoutMs,\n proxy: options.proxy,\n rateLimitRetry: options.config?.rateLimitRetry,\n });\n this.sources = createSourceManager(this.apiClient);\n }\n\n /**\n * Fluent API for rich local querying across sessions and activities.\n * This method uses the modular query engine internally.\n */\n async select<T extends JulesDomain>(\n query: JulesQuery<T>,\n ): Promise<QueryResult<T>[]> {\n // Pass 'this' to the modular function.\n // Bundlers can still tree-shake if this method is never called.\n return modularSelect(this, query);\n }\n\n /**\n * Synchronizes local state with the server.\n * Logic:\n * 1. Find High-Water Mark (newest local record).\n * 2. Stream latest sessions from API.\n * 3. Terminate stream early if 'incremental' and High-Water Mark is hit.\n * 4. Throttled hydration of activities if depth is 'activities'.\n */\n async sync(options: SyncOptions = {}): Promise<SyncStats> {\n // Acquire lock\n if (this.syncInProgress) {\n throw new SyncInProgressError();\n }\n\n this.syncInProgress = true;\n\n try {\n const startTime = Date.now();\n const {\n sessionId,\n limit = 100,\n depth = 'metadata',\n incremental = true,\n concurrency = 3,\n onProgress,\n checkpoint: useCheckpoint = false,\n signal,\n } = options;\n\n let wasAborted = false; // Track if we aborted\n const candidates: SessionResource[] = [];\n let activitiesIngested = 0;\n let sessionsIngestedThisRun = 0;\n\n // CTRL-07: Targeted Sync Logic\n if (sessionId) {\n // For a targeted sync, we always fetch from the network, bypassing normal cache checks.\n const session = await this.apiClient.request<SessionResource>(\n `sessions/${sessionId}`,\n );\n // We still upsert to update the cache with the fresh data.\n await this.storage.upsert(session);\n candidates.push(session);\n sessionsIngestedThisRun = 1; // We synced one session.\n } else {\n // CTRL-08: Full Sync Logic (existing behavior)\n let resumeFromId: string | null = null;\n let startingCount = 0;\n\n if (useCheckpoint) {\n const ckpt = await this.loadCheckpoint();\n if (ckpt) {\n resumeFromId = ckpt.lastProcessedSessionId;\n startingCount = ckpt.sessionsProcessed;\n }\n }\n\n let skipUntilPast = !!resumeFromId;\n const highWaterMark = incremental\n ? await this._getHighWaterMark()\n : null;\n\n const cursor = this.sessions({\n pageSize: Math.min(limit, 100),\n persist: false,\n });\n\n onProgress?.({ phase: 'fetching_list', current: 0 });\n\n for await (const session of cursor) {\n // Check for abort BEFORE processing each session\n if (signal?.aborted) {\n wasAborted = true;\n break;\n }\n\n if (skipUntilPast) {\n if (session.id === resumeFromId) {\n skipUntilPast = false;\n continue;\n }\n continue;\n }\n\n if (highWaterMark && new Date(session.createTime) <= highWaterMark) {\n // We've reached sessions we already have cached.\n // For activities depth: include this session for hydration (to get new activities)\n // but stop iterating after - we don't need to process older sessions.\n if (depth === 'activities') {\n await this.storage.upsert(session);\n candidates.push(session);\n // Don't increment sessionsIngested - this is an existing session\n }\n // For both depths, stop iterating - the hydrate() method uses pageToken\n // to efficiently fetch only NEW activities for cached sessions.\n break;\n }\n\n await this.storage.upsert(session);\n candidates.push(session);\n sessionsIngestedThisRun++;\n\n if (useCheckpoint) {\n await this.saveCheckpoint({\n lastProcessedSessionId: session.id,\n sessionsProcessed: startingCount + sessionsIngestedThisRun,\n startedAt: new Date(startTime).toISOString(),\n });\n }\n\n onProgress?.({\n phase: 'fetching_list',\n current: sessionsIngestedThisRun,\n lastIngestedId: session.id,\n });\n\n if (candidates.length >= limit) break;\n }\n }\n\n // 3. Deep Ingestion (Activity Hydration)\n // Uses pMap for backpressure to prevent quota saturation.\n // The hydrate() method is optimized to:\n // - Skip frozen sessions (> 30 days old) entirely\n // - Use pageToken to fetch only new activities\n // - Handle duplicate detection at timestamp boundaries\n if (depth === 'activities' && candidates.length > 0 && !wasAborted) {\n let hydratedCount = 0;\n onProgress?.({\n phase: 'hydrating_records',\n current: 0,\n total: candidates.length,\n });\n\n await pMap(\n candidates,\n async (session) => {\n if (signal?.aborted) return; // Skip if aborted\n\n const sessionClient = this.session(session.id);\n // hydrate() handles all the optimization:\n // - Frozen sessions (> 30 days) return 0 immediately\n // - Uses pageToken for incremental sync\n const count = await sessionClient.activities.hydrate();\n activitiesIngested += count;\n\n hydratedCount++;\n onProgress?.({\n phase: 'hydrating_records',\n current: hydratedCount,\n total: candidates.length,\n lastIngestedId: session.id,\n activityCount: count,\n });\n },\n { concurrency },\n );\n }\n\n // Clear checkpoint on successful completion (only if not aborted and not targeted)\n if (useCheckpoint && !wasAborted && !sessionId) {\n await this.clearCheckpoint();\n }\n\n const stats = {\n sessionsIngested: sessionsIngestedThisRun,\n activitiesIngested,\n isComplete: !wasAborted, // false if aborted\n durationMs: Date.now() - startTime,\n };\n\n await updateGlobalCacheMetadata();\n\n return stats;\n } finally {\n // ALWAYS release lock, even on error\n this.syncInProgress = false;\n }\n }\n\n private getCheckpointPath(): string {\n // Assumes storage has a way to get the cache directory\n // Or use getRootDir() from index.ts\n return join(getRootDir(), '.jules', 'cache', 'sync-checkpoint.json');\n }\n\n private async loadCheckpoint(): Promise<SyncCheckpoint | null> {\n if (!this.platform.readFile) return null;\n try {\n const path = this.getCheckpointPath();\n const data = await this.platform.readFile(path);\n return JSON.parse(data) as SyncCheckpoint;\n } catch {\n return null; // No checkpoint or invalid\n }\n }\n\n private async saveCheckpoint(checkpoint: SyncCheckpoint): Promise<void> {\n if (!this.platform.writeFile) return;\n const path = this.getCheckpointPath();\n await this.platform.writeFile(path, JSON.stringify(checkpoint, null, 2));\n }\n\n private async clearCheckpoint(): Promise<void> {\n if (!this.platform.deleteFile) return;\n try {\n const path = this.getCheckpointPath();\n await this.platform.deleteFile(path);\n } catch {\n // Ignore if doesn't exist\n }\n }\n\n private async _getHighWaterMark(): Promise<Date | null> {\n let newest: Date | null = null;\n // scanIndex is the high-speed index scanner implemented in Phase 1\n for await (const entry of this.storage.scanIndex()) {\n const date = new Date(entry.createTime);\n if (!newest || date > newest) newest = date;\n }\n return newest;\n }\n\n /**\n * Helper to resolve environment variables with support for frontend prefixes.\n */\n private getEnv(key: string): string | undefined {\n return (\n this.platform.getEnv(`NEXT_PUBLIC_${key}`) ||\n this.platform.getEnv(`REACT_APP_${key}`) ||\n this.platform.getEnv(`VITE_${key}`) ||\n this.platform.getEnv(key)\n );\n }\n\n /**\n * Creates a new Jules client instance with updated configuration.\n * This is an immutable operation; the original client instance remains unchanged.\n *\n * @param options The new configuration options to merge with the existing ones.\n * @returns A new JulesClient instance with the updated configuration.\n */\n with(options: JulesOptions): JulesClient {\n return new JulesClientImpl(\n {\n ...this.options,\n ...options,\n config: {\n ...this.options.config,\n ...options.config,\n },\n },\n this.storageFactory,\n this.platform,\n );\n }\n\n /**\n * Connects to the Jules service with the provided configuration.\n * Acts as a factory method for creating a new client instance.\n *\n * @param options Configuration options for the client.\n * @returns A new JulesClient instance.\n */\n connect(options: JulesOptions): JulesClient {\n return new JulesClientImpl(\n {\n ...this.options,\n ...options,\n },\n this.storageFactory,\n this.platform,\n );\n }\n\n /**\n * Retrieves a session resource using the \"Iceberg\" caching strategy.\n * * - **Tier 3 (Frozen):** > 30 days old. Returns from cache immediately.\n * - **Tier 2 (Warm):** Terminal state + Verified < 24h ago. Returns from cache.\n * - **Tier 1 (Hot):** Active or Stale. Fetches from network, updates cache, returns.\n */\n async getSessionResource(id: string): Promise<SessionResource> {\n const cached = await this.storage.get(id);\n\n if (isCacheValid(cached)) {\n return cached.resource;\n }\n\n // TIER 1 & Fallback: Network Request\n try {\n const fresh = await this.apiClient.request<SessionResource>(\n `sessions/${id}`,\n );\n\n await this.storage.upsert(fresh);\n\n return fresh;\n } catch (e: any) {\n // Handle 404: If it was in cache but 404s on network, it was deleted remotely.\n if (e.status === 404 && cached) {\n await this.storage.delete(id);\n }\n throw e;\n }\n }\n\n /**\n * Lists sessions with a fluent, pagination-friendly API.\n * @param options Configuration for pagination (pageSize, limit, pageToken)\n * @returns A SessionCursor that can be awaited (first page) or iterated (all pages).\n */\n sessions(options?: ListSessionsOptions): SessionCursor {\n // Inject storage into the cursor for Write-Through behavior\n return new SessionCursor(this.apiClient, this.storage, options);\n }\n\n async all<T>(\n items: T[],\n mapper: (item: T) => SessionConfig | Promise<SessionConfig>,\n options?: {\n concurrency?: number;\n stopOnError?: boolean;\n delayMs?: number;\n },\n ): Promise<AutomatedSession[]> {\n return pMap(\n items,\n async (item) => {\n const config = await mapper(item);\n return this.run(config);\n },\n options,\n );\n }\n\n private async _prepareSessionCreation(\n config: SessionConfig,\n ): Promise<object> {\n const source = await this.sources.get({ github: config.source.github });\n if (!source) {\n throw new SourceNotFoundError(config.source.github);\n }\n\n return {\n prompt: config.prompt,\n title: config.title,\n sourceContext: {\n source: source.name,\n githubRepoContext: {\n startingBranch: config.source.branch,\n },\n },\n };\n }\n\n /**\n * Executes a task in automated mode.\n * This is a high-level abstraction for \"fire-and-forget\" tasks.\n *\n * **Side Effects:**\n * - Creates a new session on the Jules API (`POST /sessions`).\n * - Initiates background polling for activity updates.\n * - May create a Pull Request if `autoPr` is true (default).\n *\n * **Data Transformation:**\n * - Resolves the `github` source identifier (e.g., `owner/repo`) to a full resource name.\n * - Defaults `requirePlanApproval` to `false` for automated runs.\n *\n * @param config The configuration for the run.\n * @returns A `AutomatedSession` object, which is an enhanced Promise that resolves to the final outcome.\n * @throws {SourceNotFoundError} If the specified GitHub repository cannot be found or accessed.\n * @throws {JulesApiError} If the session creation fails (e.g., 401 Unauthorized).\n *\n * @example\n * const run = await jules.run({\n * prompt: \"Fix the login bug\",\n * source: { github: \"my-org/repo\", branch: \"main\" }\n * });\n * const outcome = await run.result();\n */\n async run(config: SessionConfig): Promise<AutomatedSession> {\n const body = await this._prepareSessionCreation(config);\n const sessionResource = await this.apiClient.request<SessionResource>(\n 'sessions',\n {\n method: 'POST',\n body: {\n ...body,\n automationMode:\n config.autoPr === false\n ? 'AUTOMATION_MODE_UNSPECIFIED'\n : 'AUTO_CREATE_PR',\n requirePlanApproval: config.requireApproval ?? false,\n },\n // ✅ PASS CONTEXT: I want to CREATE\n handshake: {\n intent: 'create',\n sessionConfig: { prompt: config.prompt, source: config.source },\n },\n },\n );\n\n // Cache the new session immediately\n await this.storage.upsert(sessionResource);\n\n const sessionId = sessionResource.id;\n\n return {\n id: sessionId,\n stream: async function* (this: JulesClientImpl) {\n yield* streamActivities(\n sessionId,\n this.apiClient,\n this.config.pollingIntervalMs,\n this.platform,\n );\n }.bind(this),\n result: async () => {\n const finalSession = await pollUntilCompletion(\n sessionId,\n this.apiClient,\n this.config.pollingIntervalMs,\n );\n // Cache the final state\n await this.storage.upsert(finalSession);\n return mapSessionResourceToOutcome(finalSession);\n },\n };\n }\n\n /**\n * Creates a new interactive session for workflows requiring human oversight.\n *\n * **Side Effects:**\n * - Creates a new session on the Jules API (`POST /sessions`).\n * - Initializes local storage for the session.\n *\n * **Data Transformation:**\n * - Defaults `requirePlanApproval` to `true` for interactive sessions.\n *\n * @param config The configuration for the session.\n * @returns A Promise resolving to the interactive `SessionClient`.\n * @throws {SourceNotFoundError} If the source cannot be found.\n *\n * @example\n * const session = await jules.session({\n * prompt: \"Let's explore the codebase\",\n * source: { github: \"owner/repo\", branch: \"main\" }\n * });\n */\n session(config: SessionConfig): Promise<SessionClient>;\n /**\n * Rehydrates an existing session from its ID, allowing you to resume interaction.\n * This is useful for stateless environments (like serverless functions) where you need to\n * reconnect to a long-running session.\n *\n * **Side Effects:**\n * - Initializes local storage for the existing session ID.\n * - Does NOT make a network request immediately (lazy initialization).\n *\n * @param sessionId The ID of the existing session.\n * @returns The interactive `SessionClient`.\n *\n * @example\n * const session = jules.session(\"12345\");\n * const info = await session.info(); // Now makes a request\n */\n session(sessionId: string): SessionClient;\n session(\n configOrId: SessionConfig | string,\n ): Promise<SessionClient> | SessionClient {\n if (typeof configOrId === 'string') {\n const storage = this.storageFactory.activity(configOrId);\n return new SessionClientImpl(\n configOrId,\n this.apiClient,\n this.config,\n storage,\n this.storage,\n this.platform,\n );\n }\n\n const config = configOrId;\n const sessionPromise = (async () => {\n const body = await this._prepareSessionCreation(config);\n const session = await this.apiClient.request<SessionResource>(\n 'sessions',\n {\n method: 'POST',\n body: {\n ...body,\n automationMode: 'AUTOMATION_MODE_UNSPECIFIED',\n requirePlanApproval: config.requireApproval ?? true,\n },\n // ✅ PASS CONTEXT\n handshake: {\n intent: 'create',\n sessionConfig: { prompt: config.prompt, source: config.source },\n },\n },\n );\n\n // Cache created session\n await this.storage.upsert(session);\n\n const activityStorage = this.storageFactory.activity(session.id);\n return new SessionClientImpl(\n session.name,\n this.apiClient,\n this.config,\n activityStorage,\n this.storage,\n this.platform,\n );\n })();\n return sessionPromise;\n }\n}\n","import { Activity, SessionResource } from '../types.js';\nimport {\n ActivityStorage,\n SessionStorage,\n CachedSession,\n SessionIndexEntry,\n} from './types.js';\n\n/**\n * In-memory implementation of ActivityStorage.\n * Useful for testing or environments where persistence is not required.\n */\nexport class MemoryStorage implements ActivityStorage {\n private activities: Activity[] = [];\n\n /**\n * Initializes the storage. No-op for memory storage.\n */\n async init(): Promise<void> {\n // No-op for memory\n }\n\n /**\n * Closes the storage and clears memory.\n */\n async close(): Promise<void> {\n this.activities = []; // Clear memory on close\n }\n\n /**\n * Appends an activity to the in-memory list.\n *\n * **Guarantee:**\n * - Idempotent: If an activity with the same ID exists, it updates it in place.\n * - Append-only: New activities are always added to the end.\n *\n * **Side Effects:**\n * - Modifies the internal `activities` array.\n */\n async append(activity: Activity): Promise<void> {\n // Upsert logic to maintain idempotency contract\n const index = this.activities.findIndex((a) => a.id === activity.id);\n if (index >= 0) {\n // Maintain original position\n this.activities[index] = activity;\n } else {\n this.activities.push(activity);\n }\n }\n\n /**\n * Retrieves an activity by ID.\n */\n async get(activityId: string): Promise<Activity | undefined> {\n return this.activities.find((a) => a.id === activityId);\n }\n\n /**\n * Retrieves the latest activity.\n */\n async latest(): Promise<Activity | undefined> {\n if (this.activities.length === 0) return undefined;\n return this.activities[this.activities.length - 1];\n }\n\n /**\n * Yields all activities in chronological order.\n */\n async *scan(): AsyncIterable<Activity> {\n for (const activity of this.activities) {\n yield activity;\n }\n }\n}\n\n/**\n * In-memory implementation of SessionStorage.\n */\nexport class MemorySessionStorage implements SessionStorage {\n private sessions: Map<string, CachedSession> = new Map();\n private index: SessionIndexEntry[] = [];\n\n async init(): Promise<void> {\n // No-op\n }\n\n async upsert(session: SessionResource): Promise<void> {\n this.sessions.set(session.id, {\n resource: session,\n _lastSyncedAt: Date.now(),\n });\n\n // Append to index (mimicking file behavior)\n this.index.push({\n id: session.id,\n title: session.title,\n state: session.state,\n createTime: session.createTime,\n source: session.sourceContext?.source || 'unknown',\n _updatedAt: Date.now(),\n });\n }\n\n async upsertMany(sessions: SessionResource[]): Promise<void> {\n for (const s of sessions) {\n await this.upsert(s);\n }\n }\n\n async get(sessionId: string): Promise<CachedSession | undefined> {\n return this.sessions.get(sessionId);\n }\n\n async delete(sessionId: string): Promise<void> {\n this.sessions.delete(sessionId);\n // Index entries remain (append-only simulation), or we could filter them out.\n // Spec says \"We do NOT rewrite the index here for performance\", so we leave them.\n }\n\n async *scanIndex(): AsyncIterable<SessionIndexEntry> {\n for (const entry of this.index) {\n yield entry;\n }\n }\n}\n"],"names":["path","sleep","match","select","modularSelect"],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Platform } from './types.js';
|
|
1
|
+
import { Platform, PlatformResponse } from './types.js';
|
|
2
2
|
/**
|
|
3
3
|
* Browser implementation of the Platform interface.
|
|
4
4
|
* Uses IndexedDB for file storage.
|
|
@@ -22,4 +22,16 @@ export declare class BrowserPlatform implements Platform {
|
|
|
22
22
|
sleep(ms: number): Promise<void>;
|
|
23
23
|
createDataUrl(data: string, mimeType: string): string;
|
|
24
24
|
private base64ToBlob;
|
|
25
|
+
fetch(input: string, init?: any): Promise<PlatformResponse>;
|
|
26
|
+
crypto: {
|
|
27
|
+
randomUUID: () => `${string}-${string}-${string}-${string}-${string}`;
|
|
28
|
+
sign(text: string, secret: string): Promise<string>;
|
|
29
|
+
verify(text: string, signature: string, secret: string): Promise<boolean>;
|
|
30
|
+
arrayBufferToBase64Url(buffer: ArrayBuffer): string;
|
|
31
|
+
};
|
|
32
|
+
encoding: {
|
|
33
|
+
base64Encode: (text: string) => string;
|
|
34
|
+
base64Decode: (text: string) => string;
|
|
35
|
+
};
|
|
36
|
+
getEnv(key: string): string | undefined;
|
|
25
37
|
}
|
package/dist/platform/node.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Platform } from './types.js';
|
|
1
|
+
import { Platform, PlatformResponse } from './types.js';
|
|
2
2
|
/**
|
|
3
3
|
* Node.js implementation of the Platform interface.
|
|
4
4
|
*/
|
|
@@ -13,4 +13,18 @@ export declare class NodePlatform implements Platform {
|
|
|
13
13
|
saveFile(filepath: string, data: string, encoding: 'base64', activityId?: string): Promise<void>;
|
|
14
14
|
sleep(ms: number): Promise<void>;
|
|
15
15
|
createDataUrl(data: string, mimeType: string): string;
|
|
16
|
+
fetch(input: string, init?: any): Promise<PlatformResponse>;
|
|
17
|
+
crypto: {
|
|
18
|
+
randomUUID: () => `${string}-${string}-${string}-${string}-${string}`;
|
|
19
|
+
sign(text: string, secret: string): Promise<string>;
|
|
20
|
+
verify(text: string, signature: string, secret: string): Promise<boolean>;
|
|
21
|
+
};
|
|
22
|
+
encoding: {
|
|
23
|
+
base64Encode: (text: string) => string;
|
|
24
|
+
base64Decode: (text: string) => string;
|
|
25
|
+
};
|
|
26
|
+
getEnv(key: string): string | undefined;
|
|
27
|
+
readFile(path: string): Promise<string>;
|
|
28
|
+
writeFile(path: string, content: string): Promise<void>;
|
|
29
|
+
deleteFile(path: string): Promise<void>;
|
|
16
30
|
}
|
package/dist/platform/types.d.ts
CHANGED
|
@@ -1,6 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A unified response interface that works across Node, Browser, and GAS.
|
|
3
|
+
*/
|
|
4
|
+
export interface PlatformResponse {
|
|
5
|
+
ok: boolean;
|
|
6
|
+
status: number;
|
|
7
|
+
json<T = any>(): Promise<T>;
|
|
8
|
+
text(): Promise<string>;
|
|
9
|
+
}
|
|
1
10
|
/**
|
|
2
11
|
* Abstract interface for platform-specific functionality.
|
|
3
|
-
* Allows the SDK to run in both Node.js and
|
|
12
|
+
* Allows the SDK to run in both Node.js, browser, and Google Apps Script environments.
|
|
4
13
|
*/
|
|
5
14
|
export interface Platform {
|
|
6
15
|
/**
|
|
@@ -15,4 +24,50 @@ export interface Platform {
|
|
|
15
24
|
* Creates a data URL for the given data.
|
|
16
25
|
*/
|
|
17
26
|
createDataUrl(data: string, mimeType: string): string;
|
|
27
|
+
/**
|
|
28
|
+
* Unified network fetch.
|
|
29
|
+
*/
|
|
30
|
+
fetch(input: string, init?: any): Promise<PlatformResponse>;
|
|
31
|
+
/**
|
|
32
|
+
* Unified crypto operations.
|
|
33
|
+
*/
|
|
34
|
+
crypto: {
|
|
35
|
+
/**
|
|
36
|
+
* Generates a standard UUID v4.
|
|
37
|
+
*/
|
|
38
|
+
randomUUID(): string;
|
|
39
|
+
/**
|
|
40
|
+
* Signs a string using HMAC-SHA256 and returns a Base64Url encoded string.
|
|
41
|
+
* Used for minting Capability Tokens.
|
|
42
|
+
*/
|
|
43
|
+
sign(text: string, secret: string): Promise<string>;
|
|
44
|
+
/**
|
|
45
|
+
* Verifies a signature.
|
|
46
|
+
*/
|
|
47
|
+
verify(text: string, signature: string, secret: string): Promise<boolean>;
|
|
48
|
+
};
|
|
49
|
+
/**
|
|
50
|
+
* Unified encoding/decoding operations.
|
|
51
|
+
*/
|
|
52
|
+
encoding: {
|
|
53
|
+
/**
|
|
54
|
+
* Encodes a string to Base64URL format.
|
|
55
|
+
* (URL-safe: '-' instead of '+', '_' instead of '/', no padding)
|
|
56
|
+
*/
|
|
57
|
+
base64Encode(text: string): string;
|
|
58
|
+
/**
|
|
59
|
+
* Decodes a Base64URL encoded string.
|
|
60
|
+
*/
|
|
61
|
+
base64Decode(text: string): string;
|
|
62
|
+
};
|
|
63
|
+
/**
|
|
64
|
+
* Retrieves an environment variable or configuration value.
|
|
65
|
+
*
|
|
66
|
+
* @param key The name of the environment variable (e.g., "JULES_API_KEY").
|
|
67
|
+
* @returns The value of the environment variable, or `undefined` if not set.
|
|
68
|
+
*/
|
|
69
|
+
getEnv(key: string): string | undefined;
|
|
70
|
+
readFile?(path: string): Promise<string>;
|
|
71
|
+
writeFile?(path: string, content: string): Promise<void>;
|
|
72
|
+
deleteFile?(path: string): Promise<void>;
|
|
18
73
|
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { Platform, PlatformResponse } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Web Platform implementation using standard Web APIs.
|
|
4
|
+
* Works on Edge runtimes, Deno, Cloudflare Workers, and Node.js 18+.
|
|
5
|
+
*
|
|
6
|
+
* Note: This is a minimal implementation focused on server-side gateway usage.
|
|
7
|
+
* It does not support file storage operations (use NodePlatform for that).
|
|
8
|
+
*/
|
|
9
|
+
export declare class WebPlatform implements Platform {
|
|
10
|
+
/**
|
|
11
|
+
* File saving is not supported in the Web Platform.
|
|
12
|
+
* Use NodePlatform or BrowserPlatform for file operations.
|
|
13
|
+
*/
|
|
14
|
+
saveFile(filepath: string, data: string, encoding: 'base64', activityId?: string): Promise<void>;
|
|
15
|
+
sleep(ms: number): Promise<void>;
|
|
16
|
+
createDataUrl(data: string, mimeType: string): string;
|
|
17
|
+
fetch(input: string, init?: any): Promise<PlatformResponse>;
|
|
18
|
+
crypto: {
|
|
19
|
+
randomUUID: () => string;
|
|
20
|
+
sign(text: string, secret: string): Promise<string>;
|
|
21
|
+
verify(text: string, signature: string, secret: string): Promise<boolean>;
|
|
22
|
+
arrayBufferToBase64Url(buffer: ArrayBuffer): string;
|
|
23
|
+
};
|
|
24
|
+
encoding: {
|
|
25
|
+
base64Encode: (text: string) => string;
|
|
26
|
+
base64Decode: (text: string) => string;
|
|
27
|
+
};
|
|
28
|
+
getEnv(key: string): string | undefined;
|
|
29
|
+
}
|