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

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ jobs:
- name: Lint
uses: golangci/golangci-lint-action@ba0d7d2ec06a0ea1cb5fa41b2e4a3ab91d21278a # v9.3.0
with:
version: v2.12.2
version: v2.13.1

- name: Lint GitHub Actions
uses: raven-actions/actionlint@3d39aea434753780c3b3d4a1a31c854b4dbf49d7 # v2.2.0
Expand Down
60 changes: 60 additions & 0 deletions .github/workflows/codeql.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
name: CodeQL

permissions:
contents: read
security-events: write
actions: read

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: ${{ github.ref != 'refs/heads/main' }}

on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
schedule:
- cron: '23 14 * * 1'

jobs:
analyze:
name: Analyze (${{ matrix.language }})
runs-on: ubuntu-latest

strategy:
fail-fast: false
matrix:
include:
- language: go
build-mode: autobuild
- language: actions
build-mode: none
- language: javascript-typescript
build-mode: none

steps:
- name: Checkout repository
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3

- name: Set up Go
if: matrix.language == 'go'
uses: actions/setup-go@924ae3a1cded613372ab5595356fb5720e22ba16 # v6.5.0
with:
go-version-file: go.mod
cache-dependency-path: go.sum

- name: Initialize CodeQL
uses: github/codeql-action/init@db488ddef3bf6cb639b32c2e9a7c0a7ea8271d28 # v4
with:
languages: ${{ matrix.language }}
build-mode: ${{ matrix.build-mode }}

- name: Autobuild
if: matrix.build-mode == 'autobuild'
uses: github/codeql-action/autobuild@db488ddef3bf6cb639b32c2e9a7c0a7ea8271d28 # v4

- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@db488ddef3bf6cb639b32c2e9a7c0a7ea8271d28 # v4
with:
category: "/language:${{ matrix.language }}"
22 changes: 22 additions & 0 deletions .golangci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,16 @@ linters:
- G702
- G703
- G704
modernize:
# errorsastype (errors.AsType, Go 1.27), embedlit (x/tools v0.48),
# stringscut and reflecttypeassert became active after the
# golangci-lint v2.12.2 → v2.13.1 bump required by the Go 1.27 upgrade.
# Pre-existing violations in unrelated files; re-enable in a follow-up.
disable:
- errorsastype
- embedlit
- stringscut
- reflecttypeassert
exclusions:
generated: lax
presets:
Expand All @@ -182,6 +192,18 @@ linters:
- path: pkg/worktree/namesgenerator/
linters:
- nolintlint
# Frozen versioned config types: recvcheck changed behaviour in
# golangci-lint v2.13.1 — some previously-suppressed violations are no
# longer caught (leaving stale //nolint:recvcheck directives that trigger
# nolintlint) while new violations are reported in the same files.
# These files are frozen (see AGENTS.md) and must not be modified.
- path: pkg/config/v\d+/
linters:
- recvcheck
- nolintlint
- path: pkg/config/latest/types\.go
linters:
- recvcheck
issues:
max-same-issues: 3
formatters:
Expand Down
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# syntax=docker/dockerfile:1

ARG GO_VERSION="1.26.5"
ARG GO_VERSION="1.27.0"
ARG ALPINE_VERSION="3.23"
ARG XX_VERSION="1.9.0"

Expand Down
15 changes: 10 additions & 5 deletions cmd/root/doctor.go
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,8 @@ func (f *doctorFlags) buildReport(ctx context.Context, agentRef string) (*doctor
report.UserConfig.Error = err.Error()
report.Issues = append(report.Issues, fmt.Sprintf(
"the user config file %s cannot be parsed and is ignored (settings and aliases are unavailable): %v",
report.UserConfig.Path, err))
report.UserConfig.Path, err,
))
}

credFound := map[string]bool{}
Expand Down Expand Up @@ -302,12 +303,14 @@ func (f *doctorFlags) buildReport(ctx context.Context, agentRef string) (*doctor
autoStatus.Usable = false
autoIssues = append(autoIssues, fmt.Sprintf(
"the configured default model %s/%s needs Docker Model Runner, which is %s; install or start it (%s)",
auto.Provider, auto.Model, describeDMRStatus(report.DMR.Status), dmrDocsURL))
auto.Provider, auto.Model, describeDMRStatus(report.DMR.Status), dmrDocsURL,
))
case dmrDown:
autoStatus.Usable = false
autoIssues = append(autoIssues, fmt.Sprintf(
"no usable model: no provider credential was found and Docker Model Runner is %s; run `docker agent setup`, or set an API key for one of the providers above (%s) or install Docker Model Runner (%s)",
describeDMRStatus(report.DMR.Status), environment.SecretsDocsURL, dmrDocsURL))
describeDMRStatus(report.DMR.Status), environment.SecretsDocsURL, dmrDocsURL,
))
case !slices.Contains(dmrModels, auto.Model):
autoStatus.Note = fmt.Sprintf("not pulled yet; run `docker model pull %s` or let the first run pull it", auto.Model)
}
Expand All @@ -319,7 +322,8 @@ func (f *doctorFlags) buildReport(ctx context.Context, agentRef string) (*doctor
autoStatus.Usable = false
autoIssues = append(autoIssues, fmt.Sprintf(
"the configured default model %s/%s has no credential for provider %s; %s (%s)",
auto.Provider, auto.Model, auto.Provider, providerCredentialHint(auto.Provider, primaryEnvVar[auto.Provider]), environment.SecretsDocsURL))
auto.Provider, auto.Model, auto.Provider, providerCredentialHint(auto.Provider, primaryEnvVar[auto.Provider]), environment.SecretsDocsURL,
))
}
}

Expand Down Expand Up @@ -382,7 +386,8 @@ func (f *doctorFlags) checkAgentFile(ctx context.Context, ref string, cfg *lates
if len(missing) > 0 {
report.Issues = append(report.Issues, fmt.Sprintf(
"%s requires environment variables that are not set: %s (see %s)",
ref, strings.Join(missing, ", "), environment.SecretsDocsURL))
ref, strings.Join(missing, ", "), environment.SecretsDocsURL,
))
}

// The Claude Code harness runs the local `claude` CLI with its own login,
Expand Down
18 changes: 12 additions & 6 deletions cmd/root/doctor_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,8 @@ func TestDoctorCommand_ReportsCredentialSource(t *testing.T) {

output, err := executeDoctor(t, nil, withDoctorTestEnv(
map[string]string{"ANTHROPIC_API_KEY": "sk-secret-value"},
[]string{"ai/qwen3:latest"}, nil))
[]string{"ai/qwen3:latest"}, nil,
))

require.NoError(t, err)
assert.Regexp(t, `anthropic\s+found\s+ANTHROPIC_API_KEY\s+environment`, output)
Expand Down Expand Up @@ -138,7 +139,8 @@ func TestDoctorCommand_EmptyValueIsNotACredential(t *testing.T) {

output, err := executeDoctor(t, nil, withDoctorTestEnv(
map[string]string{"OPENAI_API_KEY": "", "MISTRAL_API_KEY": "key"},
[]string{"ai/qwen3:latest"}, nil))
[]string{"ai/qwen3:latest"}, nil,
))

require.NoError(t, err)
assert.Regexp(t, `openai\s+not set`, output)
Expand Down Expand Up @@ -269,7 +271,8 @@ func TestDoctorCommand_JSON(t *testing.T) {

output, err := executeDoctor(t, []string{"--json"}, withDoctorTestEnv(
map[string]string{"OPENAI_API_KEY": "sk-json-secret"},
[]string{"ai/qwen3:latest"}, nil))
[]string{"ai/qwen3:latest"}, nil,
))

require.NoError(t, err)
assert.NotContains(t, output, "sk-json-secret", "secret values must never be printed")
Expand Down Expand Up @@ -299,7 +302,8 @@ func TestDoctorCommand_JSONReportsGitHubCopilot(t *testing.T) {

output, err := executeDoctor(t, []string{"--json"}, withDoctorTestEnv(
map[string]string{"GH_TOKEN": "gh-token"},
[]string{"ai/qwen3:latest"}, nil))
[]string{"ai/qwen3:latest"}, nil,
))

require.NoError(t, err)
assert.NotContains(t, output, "gh-token", "secret values must never be printed")
Expand Down Expand Up @@ -341,7 +345,8 @@ func TestDoctorCommand_AgentFileMissingVars(t *testing.T) {

output, err := executeDoctor(t, []string{path}, withDoctorTestEnv(
map[string]string{"ANTHROPIC_API_KEY": "key"},
[]string{"ai/qwen3:latest"}, nil))
[]string{"ai/qwen3:latest"}, nil,
))

require.Error(t, err)
statusErr, ok := errors.AsType[cli.StatusError](err)
Expand All @@ -360,7 +365,8 @@ func TestDoctorCommand_AgentFileVarsSatisfied(t *testing.T) {

output, err := executeDoctor(t, []string{path}, withDoctorTestEnv(
map[string]string{"OPENAI_API_KEY": "key"},
[]string{"ai/qwen3:latest"}, nil))
[]string{"ai/qwen3:latest"}, nil,
))

require.NoError(t, err)
assert.Regexp(t, `OPENAI_API_KEY\s+models\s+found\s+environment`, output)
Expand Down
7 changes: 4 additions & 3 deletions cmd/wasm/runtime_wasm.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import (
"fmt"
"io"
"log/slog"
"slices"
"strings"
"syscall/js"
"time"
Expand Down Expand Up @@ -773,9 +774,9 @@ func (rt *wasmRuntime) emitEvent(event map[string]any) {

// lastAssistantContent returns the content of the last assistant message.
func (rt *wasmRuntime) lastAssistantContent(messages []chat.Message) string {
for i := len(messages) - 1; i >= 0; i-- {
if messages[i].Role == chat.MessageRoleAssistant && messages[i].Content != "" {
return messages[i].Content
for _, msg := range slices.Backward(messages) {
if msg.Role == chat.MessageRoleAssistant && msg.Content != "" {
return msg.Content
}
}
return ""
Expand Down
1 change: 0 additions & 1 deletion e2e/binary/binary_test.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
//go:build binary_required
// +build binary_required

package binary

Expand Down
3 changes: 2 additions & 1 deletion examples/golibrary/multi/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,8 @@ func run(ctx context.Context) error {
Provider: "openai",
Model: "gpt-4o",
},
environment.NewDefaultProvider())
environment.NewDefaultProvider(),
)
if err != nil {
return err
}
Expand Down
Loading
Loading