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 ¶
- Variables
- type Client
- func (c *Client) CreateSession(ctx context.Context, in CreateSessionRequest) (*JulesSession, error)
- func (c *Client) GetSession(ctx context.Context, name JulesSessionName) (*JulesSession, error)
- func (c *Client) IsLimited() bool
- func (c *Client) ListSources(ctx context.Context) ([]JulesSource, error)
- func (c *Client) RetryAfter() time.Duration
- type CreateSessionRequest
- type GitHubBranchRef
- type JulesAPIKey
- type JulesPullRequestOutput
- type JulesSession
- type JulesSessionName
- type JulesSessionOutput
- type JulesSessionState
- type JulesSource
- type JulesSourceName
- type JulesSourceRegistry
- type JulesTokenSource
- type KeyringTokenSource
- type KeyringTokenSourceOption
- type Option
Constants ¶
This section is empty.
Variables ¶
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 ¶
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 ¶
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 ¶
func (s *KeyringTokenSource) APIKey(ctx context.Context) (JulesAPIKey, error)
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 ¶
WithBaseURL overrides the Jules API base URL — a test seam for pointing the client at an httptest.Server.
func WithHTTPClient ¶
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).