Repository: cli/cli
File: pkg/cmd/agent-task/create/create.go (Go)

Code before:
			backoff.WithMaxElapsedTime(4*time.Second),
			backoff.WithInitialInterval(300*time.Millisecond),
			backoff.WithMaxInterval(2*time.Second),
			backoff.WithMultiplier(1.5),
		)
	}

	jobWithPR, err := fetchJobWithBackoff(ctx, client, repo, job.ID, opts.IO.ErrOut, opts.BackOff)
	if err != nil {
		return err
	}

	if jobWithPR != nil {
		opts.IO.StopProgressIndicator()
		fmt.Fprintln(opts.IO.Out, agentSessionWebURL(repo, jobWithPR))
		return nil
	}

	// Fallback if PR not yet ready
	opts.IO.StopProgressIndicator()
	fmt.Fprintf(opts.IO.Out, "job %s queued. View progress: https://github.com/copilot/agents\n", job.ID)
	return nil
}

func agentSessionWebURL(repo ghrepo.Interface, j *capi.Job) string {
	if j == nil || j.PullRequest == nil {
		return ""
	}
	if j.SessionID == "" {
		return fmt.Sprintf("https://github.com/%s/%s/pull/%d", repo.RepoOwner(), repo.RepoName(), j.PullRequest.Number)
	}
	return fmt.Sprintf("https://github.com/%s/%s/pull/%d/agent-sessions/%s", repo.RepoOwner(), repo.RepoName(), j.PullRequest.Number, j.SessionID)
}

// fetchJobWithBackoff polls the job resource until a PR number is present or the overall
// timeout elapses. It returns the updated Job on success, (nil, nil) on timeout,
// and (nil, error) only for non-retryable failures.
func fetchJobWithBackoff(ctx context.Context, client capi.CapiClient, repo ghrepo.Interface, jobID string, errOut io.Writer, bo backoff.BackOff) (*capi.Job, error) {
	// sentinel error to signal retry without surfacing to caller
	var errPRNotReady = errors.New("job not ready")

	var result *capi.Job
	retryErr := backoff.Retry(func() error {
		j, getErr := client.GetJob(ctx, repo.RepoOwner(), repo.RepoName(), jobID)
		if getErr != nil {
			fmt.Fprintf(errOut, "warning: failed to get job status: %v\n", getErr)
			return errPRNotReady
		}
		if j.PullRequest != nil && j.PullRequest.Number > 0 {
			result = j
			return nil

Review comment (bug):
This func is called from two places both of which provide a non-`nil` job argument. So, we can drop the `nil` check and let the potential bugs surface with a panic.


```suggestion
	if j.PullRequest == nil {
```

Code after:

	// Otherwise, poll using exponential backoff until we either observe a PR or hit the overall timeout.
	// Ensure we have a backoff strategy.
	if opts.BackOff == nil {
		opts.BackOff = backoff.NewExponentialBackOff(
			backoff.WithMaxElapsedTime(4*time.Second),
			backoff.WithInitialInterval(300*time.Millisecond),
			backoff.WithMaxInterval(2*time.Second),
			backoff.WithMultiplier(1.5),
		)
	}

	jobWithPR, err := fetchJobWithBackoff(ctx, client, repo, job.ID, opts.BackOff)
	if err != nil {
		// If this does happen ever, we still want the user to get the
		// fallback message and URL. So, we don't return with this error,
		// but we do still want to print it.
		fmt.Fprintf(opts.IO.ErrOut, "%v\n", err)
	}

	if jobWithPR != nil {
		opts.IO.StopProgressIndicator()
		fmt.Fprintln(opts.IO.Out, agentSessionWebURL(repo, jobWithPR))
		return nil
	}

	// Fallback if PR not yet ready
	opts.IO.StopProgressIndicator()
	fmt.Fprintf(opts.IO.Out, "job %s queued. View progress: https://github.com/copilot/agents\n", job.ID)
	return nil
}

func agentSessionWebURL(repo ghrepo.Interface, j *capi.Job) string {
	if j.PullRequest == nil {
		return ""
	}
	if j.SessionID == "" {
		return fmt.Sprintf("https://github.com/%s/%s/pull/%d", url.PathEscape(repo.RepoOwner()), url.PathEscape(repo.RepoName()), j.PullRequest.Number)
	}
	return fmt.Sprintf("https://github.com/%s/%s/pull/%d/agent-sessions/%s", url.PathEscape(repo.RepoOwner()), url.PathEscape(repo.RepoName()), j.PullRequest.Number, url.PathEscape(j.SessionID))
}

// fetchJobWithBackoff polls the job resource until a PR number is present or the overall
// timeout elapses. It returns the updated Job on success, (nil, nil) on timeout,
// and (nil, error) only for non-retryable failures.
func fetchJobWithBackoff(ctx context.Context, client capi.CapiClient, repo ghrepo.Interface, jobID string, bo backoff.BackOff) (*capi.Job, error) {
	// sentinel error to signal timeout
	var errPRNotReady = errors.New("job not ready")

	var result *capi.Job
	retryErr := backoff.Retry(func() error {