jules

package
v1.53.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 9, 2026 License: AGPL-3.0 Imports: 15 Imported by: 0

Documentation

Overview

Package jules is a self-contained gateway over the Jules alpha API (https://jules.googleapis.com/v1alpha). It imports nothing from session/ or server/ (enforced by TestJulesPackage_should_NotImportSessionOrServer_When_DepsListed in client_test.go) so wire-format churn from an alpha API stays confined to this one directory.

Index

Constants

This section is empty.

Variables

View Source
var (
	// ErrJulesNotConfigured indicates the Jules API key is missing or
	// invalid (401/403) — the feature should be treated as off, not
	// retried on every poll tick.
	ErrJulesNotConfigured = errors.New("jules: not configured (invalid or missing API key)")

	// ErrJulesRateLimited indicates the Jules API returned 429. The
	// client's rateLimiter (rate_limit.go) tracks how long to back off.
	ErrJulesRateLimited = errors.New("jules: rate limited")

	// ErrJulesSessionNotFound indicates GetSession's target session no
	// longer exists (404) — the poller should end the session rather than
	// retry forever.
	ErrJulesSessionNotFound = errors.New("jules: session not found")

	// ErrJulesSourceNotRegistered indicates the source registry
	// (Story 1.3.1) found no entry for a requested repo — the repo has not
	// been connected through the Jules GitHub App at jules.google.com.
	ErrJulesSourceNotRegistered = errors.New("jules: source not registered")

	// ErrJulesTransient indicates a 5xx response — safe to retry later.
	ErrJulesTransient = errors.New("jules: transient server error")

	// ErrJulesKeychainPaused indicates KeyringTokenSource's circuit breaker
	// (jules/keychain.go) is open after a hung OS keychain read timed out,
	// and this call was served the paused result immediately rather than
	// blocking on another keyring probe. It wraps ErrJulesNotConfigured so
	// all existing "feature off" handling (dispatch guard, poller skip)
	// treats it identically without a new branch.
	ErrJulesKeychainPaused = fmt.Errorf("jules: keychain paused after timeout: %w", ErrJulesNotConfigured)
)

Functions

This section is empty.

Types

type Client

type Client struct {
	// contains filtered or unexported fields
}

Client is a typed gateway over the three Jules API endpoints the MVP needs: ListSources, CreateSession, GetSession. It builds requests, sets authentication, classifies non-2xx responses into sentinel errors (errors.go), and decodes JSON — so call sites never build URLs or parse JSON themselves.

func NewClient

func NewClient(tokens JulesTokenSource, opts ...Option) *Client

NewClient builds a Client. tokens resolves the API key on every request; the default HTTP client has a 30s timeout, mirroring github/http_client.go's ghHTTPClient.

func (*Client) CreateSession

func (c *Client) CreateSession(ctx context.Context, in CreateSessionRequest) (*JulesSession, error)

CreateSession starts a Jules session against source's starting branch with prompt, fire-and-forget (requirePlanApproval=false, automationMode=AUTO_CREATE_PR — no caller knob for MVP).

func (*Client) GetSession

func (c *Client) GetSession(ctx context.Context, name JulesSessionName) (*JulesSession, error)

GetSession fetches the current state of a session by resource name, e.g. "sessions/abc". A 404 surfaces as ErrJulesSessionNotFound so the poller can end a vanished session rather than retry forever.

func (*Client) IsLimited

func (c *Client) IsLimited() bool

IsLimited reports whether the client is currently rate limited from a prior 429 response.

func (*Client) ListSources

func (c *Client) ListSources(ctx context.Context) ([]JulesSource, error)

ListSources lists every GitHub source registered against the caller's Jules account, following nextPageToken until it is empty (capped at maxListSourcesPages to bound a misbehaving server).

func (*Client) RetryAfter

func (c *Client) RetryAfter() time.Duration

RetryAfter returns how long the client will remain rate limited. Zero when not limited.

type CreateSessionRequest

type CreateSessionRequest struct {
	Prompt         string
	Source         JulesSourceName
	StartingBranch GitHubBranchRef
}

CreateSessionRequest is the input to Client.CreateSession. The MVP is fire-and-forget (requirements.md): RequirePlanApproval and AutomationMode are not caller-controlled — CreateSession hardcodes them.

type GitHubBranchRef

type GitHubBranchRef string

GitHubBranchRef is a branch name that must already exist on the GitHub remote backing a JulesSourceName — Jules cannot target a local worktree (research/stack.md §Sources).

func ParseGitHubBranchRef

func ParseGitHubBranchRef(s string) (GitHubBranchRef, error)

ParseGitHubBranchRef validates that s is a non-empty branch name.

type JulesAPIKey

type JulesAPIKey string

JulesAPIKey is a Jules API key, sent via the x-goog-api-key header. It never prints in full: String() always returns a redacted placeholder, and the underlying value is reachable only through the unexported reveal(), called solely by newRequest in client.go.

func ParseJulesAPIKey

func ParseJulesAPIKey(s string) (JulesAPIKey, error)

ParseJulesAPIKey validates that s is a non-empty API key.

func (JulesAPIKey) String

func (k JulesAPIKey) String() string

String never reveals the key value — it always returns a redacted placeholder, so both %v and %s formatting (and any accidental logging) are safe.

type JulesPullRequestOutput

type JulesPullRequestOutput struct {
	URL         string `json:"url"`
	Title       string `json:"title"`
	Description string `json:"description"`
}

JulesPullRequestOutput is a pull request Jules opened as the result of a session (AUTO_CREATE_PR automationMode).

type JulesSession

type JulesSession struct {
	Name       JulesSessionName     `json:"name"`
	ID         string               `json:"id"`
	State      JulesSessionState    `json:"state"`
	Title      string               `json:"title,omitempty"`
	Outputs    []JulesSessionOutput `json:"outputs,omitempty"`
	CreateTime string               `json:"createTime,omitempty"`
	UpdateTime string               `json:"updateTime,omitempty"`
	URL        string               `json:"url,omitempty"`
}

JulesSession is a Jules API session resource, as returned by CreateSession and GetSession.

type JulesSessionName

type JulesSessionName string

JulesSessionName is a Jules API resource name identifying a session, wire format "sessions/{id}".

func ParseJulesSessionName

func ParseJulesSessionName(s string) (JulesSessionName, error)

ParseJulesSessionName validates that s carries the "sessions/" resource-name prefix Jules requires.

type JulesSessionOutput

type JulesSessionOutput struct {
	PullRequest *JulesPullRequestOutput `json:"pullRequest,omitempty"`
}

JulesSessionOutput is one element of JulesSession.Outputs. Only PullRequest is populated for the MVP's AUTO_CREATE_PR flow.

type JulesSessionState

type JulesSessionState string

JulesSessionState is a closed sum type over the Jules session lifecycle, plus an Unknown variant so a wire value this package has never seen (an alpha API adding a new state) fails safely — distinguishable via IsKnown() — instead of silently aliasing a known state. The underlying string IS the raw wire value (rather than a private field on a struct), so the known values can be plain `const`s — the repo's gochecknoglobals lint rule (.golangci.yml) forbids new packages from adding package-level `var`s, which a struct-typed sum type would have required here.

const (
	JulesStateQueued               JulesSessionState = "QUEUED"
	JulesStatePlanning             JulesSessionState = "PLANNING"
	JulesStateAwaitingPlanApproval JulesSessionState = "AWAITING_PLAN_APPROVAL"
	JulesStateInProgress           JulesSessionState = "IN_PROGRESS"
	JulesStateCompleted            JulesSessionState = "COMPLETED"
	JulesStateFailed               JulesSessionState = "FAILED"
	JulesStateUnknown              JulesSessionState = ""
)

Known JulesSessionState values, matching the wire states documented in research/stack.md §Sessions. JulesStateUnknown is the zero value: parsing an unrecognized wire value does NOT collapse to this exact constant — it returns a JulesSessionState carrying the original raw text (see ParseJulesSessionState), so IsKnown()/Raw() can still report what was actually on the wire. Use IsKnown()==false, not equality against JulesStateUnknown, to test for "an unrecognized state".

func ParseJulesSessionState

func ParseJulesSessionState(raw string) JulesSessionState

ParseJulesSessionState never errors: it returns raw as a JulesSessionState unconditionally, so Raw() below always yields back exactly what was seen on the wire, known or not.

func (JulesSessionState) IsKnown

func (s JulesSessionState) IsKnown() bool

IsKnown reports whether s matched one of the states this package recognizes at compile time.

func (JulesSessionState) IsTerminal

func (s JulesSessionState) IsTerminal() bool

IsTerminal reports whether this state indicates the Jules session has finished executing and will not transition further. True only for COMPLETED and FAILED.

func (JulesSessionState) Raw

func (s JulesSessionState) Raw() string

Raw returns the original wire value this state was parsed from (empty for the zero value JulesStateUnknown).

func (JulesSessionState) String

func (s JulesSessionState) String() string

String renders the raw wire value, or "UNKNOWN" when s is the zero value (JulesStateUnknown).

func (*JulesSessionState) UnmarshalJSON

func (s *JulesSessionState) UnmarshalJSON(data []byte) error

UnmarshalJSON decodes a JSON string into a JulesSessionState via ParseJulesSessionState, which never errors on an unrecognized value.

type JulesSource

type JulesSource struct {
	Name JulesSourceName `json:"name"`
	ID   string          `json:"id"`
}

JulesSource is a GitHub repository Jules can dispatch sessions against, registered ahead of time through the Jules web UI's GitHub App (no create endpoint exists — sources are read-only via the API).

type JulesSourceName

type JulesSourceName string

JulesSourceName is a Jules API resource name identifying a registered GitHub source, wire format "sources/github-{owner}-{repo}".

func ParseJulesSourceName

func ParseJulesSourceName(s string) (JulesSourceName, error)

ParseJulesSourceName validates that s carries the "sources/" resource-name prefix Jules requires.

type JulesSourceRegistry

type JulesSourceRegistry struct {

	// TTL bounds how long a cached entry is served without re-listing
	// sources. Zero means defaultSourceRegistryTTL (set by
	// NewJulesSourceRegistry; a zero-value JulesSourceRegistry falls back
	// to the same default in Resolve).
	TTL time.Duration
	// contains filtered or unexported fields
}

JulesSourceRegistry resolves an "owner/repo" pair to the JulesSourceName Jules uses to address that GitHub repo, caching hits in memory so a dispatch does not re-list every source on every call. A miss re-lists sources once (in case the user just connected the repo) before returning ErrJulesSourceNotRegistered.

func NewJulesSourceRegistry

func NewJulesSourceRegistry(client sourceLister) *JulesSourceRegistry

NewJulesSourceRegistry creates a registry backed by client, with the default TTL and wall-clock time.Now.

func (*JulesSourceRegistry) Resolve

func (r *JulesSourceRegistry) Resolve(ctx context.Context, owner, repo string) (JulesSourceName, error)

Resolve returns the JulesSourceName for owner/repo, serving a live cache entry without calling ListSources. On a cache miss (including an expired entry) it re-lists sources once and re-checks before giving up.

type JulesTokenSource

type JulesTokenSource interface {
	APIKey(ctx context.Context) (JulesAPIKey, error)
}

JulesTokenSource resolves the API key used to authenticate Jules API calls. Implementations (e.g. a keychain-backed source, Epic 1.2) may return an error satisfying errors.Is(err, ErrJulesNotConfigured) when no key is available.

type KeyringTokenSource

type KeyringTokenSource struct {
	// contains filtered or unexported fields
}

KeyringTokenSource resolves and stores the Jules API key in the OS keychain under keychainService/keychainAccount. It implements JulesTokenSource (client.go).

Unlike session/sshremote/keystore.go's package-level keyringMu (which serializes every future keyring call behind a single hung one, forever), KeyringTokenSource pairs a short-TTL cache with a bounded single-probe circuit breaker plus singleflight-coalesced synchronous reads: at most one goroutine is ever blocked inside the underlying keyring call at a time -- whether that's the single background probe run while the circuit is open, or the shared synchronous call that concurrent cache-miss callers (e.g. the dispatch HTTP path and the poller racing right after the cache TTL expires) fan into via sfGroup -- regardless of call volume. See project_plans/google-jules-integration/implementation/plan.md Epic 1.2, Task 1.2.1a (pre-mortem P1 #4) for the incident this design closes.

func NewKeyringTokenSource

func NewKeyringTokenSource(opts ...KeyringTokenSourceOption) *KeyringTokenSource

NewKeyringTokenSource builds a KeyringTokenSource with production defaults.

func (*KeyringTokenSource) APIKey

APIKey resolves the Jules API key, in order: (1) serve from cache if still within cacheTTL; (2) if the circuit is open, launch at most one background probe (never blocking this call) and return ErrJulesKeychainPaused immediately; (3) otherwise run a timeout-raced synchronous keyring read for this call.

func (*KeyringTokenSource) DeleteJulesAPIKey

func (s *KeyringTokenSource) DeleteJulesAPIKey(ctx context.Context) error

DeleteJulesAPIKey removes the stored Jules API key from the OS keychain.

func (*KeyringTokenSource) SetJulesAPIKey

func (s *KeyringTokenSource) SetJulesAPIKey(ctx context.Context, key JulesAPIKey) error

SetJulesAPIKey stores key in the OS keychain, bypassing the cache/circuit read path -- a write must reach the keyring or fail honestly. It resets the cache and circuit afterward regardless of outcome, per Task 1.2.1a, so a subsequent APIKey call re-checks the keyring rather than serving a stale cached value or an unrelated open circuit.

type KeyringTokenSourceOption

type KeyringTokenSourceOption func(*KeyringTokenSource)

KeyringTokenSourceOption configures a KeyringTokenSource at construction.

type Option

type Option func(*Client)

Option configures a Client constructed by NewClient.

func WithBaseURL

func WithBaseURL(baseURL string) Option

WithBaseURL overrides the Jules API base URL — a test seam for pointing the client at an httptest.Server.

func WithHTTPClient

func WithHTTPClient(hc *http.Client) Option

WithHTTPClient overrides the underlying *http.Client. The rate-limit transport (rate_limit.go) is installed around whatever Transport the passed client carries (nil means http.DefaultTransport).

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL