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
1 change: 1 addition & 0 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ jobs:
- run: go build ./...
- run: go test -race ./...
- run: go vet ./...
- run: make lint

web:
runs-on: ubuntu-latest
Expand Down
69 changes: 69 additions & 0 deletions .golangci.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
version: "2"
linters:
# enable:
# - asasalint
# - bidichk
# - bodyclose
# - cyclop
# - decorder
# - dupl
# - err113
# - errname
# - errorlint
# - exhaustive
# - ginkgolinter
# - gocheckcompilerdirectives
# - gocognit
# - goconst
# - gocritic
# - goheader
# - lll
# - misspell
# - revive
# - unparam
exclusions:
generated: lax
rules:
- linters:
- errcheck
- err113
- revive
- lll
- ginkgolinter
path: _test.go
- text: should not use dot imports
path: _test.go
- text: lines are duplicate of
path: _test.go
- text: seems to be unused
path: _test.go
- text: not checked
path: .go
- linters:
- staticcheck
- unused
- ineffassign
path: .go
paths:
- third_party$
- builtin$
- examples$
formatters:
enable:
- gofmt
- goimports
exclusions:
generated: lax
paths:
- third_party$
- builtin$
- examples$
run:
timeout: 5m
issues:
max-issues-per-linter: 50
max-same-issues: 3
new: false
fix: true
uniq-by-line: true
whole-files: false
14 changes: 13 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -55,4 +55,16 @@ setup:
go mod download
cd ui/web && pnpm install --frozen-lockfile

ci: build test vet check-web
GOPATH ?= $(shell go env GOPATH)
export BIN_DIR := $(GOPATH)/bin
export GOLANGCI_LINT := $(BIN_DIR)/golangci-lint

$(GOLANGCI_LINT):
curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/HEAD/install.sh | sh -s -- -b $(BIN_DIR) v2.4.0

.PHONY: lint
lint: $(GOLANGCI_LINT)
$(GOLANGCI_LINT) --version
$(GOLANGCI_LINT) run -v

ci: build test lint vet check-web
7 changes: 4 additions & 3 deletions cmd/onboard.go
Original file line number Diff line number Diff line change
Expand Up @@ -477,17 +477,18 @@ func runOnboard() {
// --- Apply collected values to config ---

// Provider & model
if providerChoice == "claude_cli" {
switch providerChoice {
case "claude_cli":
cfg.Agents.Defaults.Provider = "claude-cli"
cfg.Agents.Defaults.Model = cliModel
cfg.Providers.ClaudeCLI.CLIPath = cliPath
cfg.Providers.ClaudeCLI.Model = cliModel
} else if providerChoice == "custom" {
case "custom":
cfg.Agents.Defaults.Provider = "openai"
cfg.Providers.OpenAI.APIBase = customAPIBase
cfg.Providers.OpenAI.APIKey = apiKey
cfg.Agents.Defaults.Model = customModel
} else {
default:
pi := providerMap[providerChoice]
cfg.Agents.Defaults.Provider = pi.name
applyProviderAPIKey(cfg, pi.name, apiKey)
Expand Down
21 changes: 10 additions & 11 deletions cmd/onboard_auto.go
Original file line number Diff line number Diff line change
Expand Up @@ -244,14 +244,14 @@ func saveCleanConfig(cfgPath string, cfg *config.Config) error {
// Build agents section.
agents := map[string]interface{}{
"defaults": map[string]interface{}{
"workspace": cfg.Agents.Defaults.Workspace,
"workspace": cfg.Agents.Defaults.Workspace,
"restrict_to_workspace": cfg.Agents.Defaults.RestrictToWorkspace,
"provider": cfg.Agents.Defaults.Provider,
"model": cfg.Agents.Defaults.Model,
"max_tokens": cfg.Agents.Defaults.MaxTokens,
"temperature": cfg.Agents.Defaults.Temperature,
"max_tool_iterations": cfg.Agents.Defaults.MaxToolIterations,
"context_window": cfg.Agents.Defaults.ContextWindow,
"provider": cfg.Agents.Defaults.Provider,
"model": cfg.Agents.Defaults.Model,
"max_tokens": cfg.Agents.Defaults.MaxTokens,
"temperature": cfg.Agents.Defaults.Temperature,
"max_tool_iterations": cfg.Agents.Defaults.MaxToolIterations,
"context_window": cfg.Agents.Defaults.ContextWindow,
},
}

Expand Down Expand Up @@ -283,16 +283,15 @@ func saveCleanConfig(cfgPath string, cfg *config.Config) error {

// Build root config map.
root := map[string]interface{}{
"agents": agents,
"gateway": gateway,
"tools": tools,
"agents": agents,
"gateway": gateway,
"tools": tools,
}

if len(channels) > 0 {
root["channels"] = channels
}


data, err := json.MarshalIndent(root, "", " ")
if err != nil {
return err
Expand Down
7 changes: 3 additions & 4 deletions internal/agent/loop.go
Original file line number Diff line number Diff line change
Expand Up @@ -461,7 +461,7 @@ func (l *Loop) runLoop(ctx context.Context, req RunRequest) (*RunResult, error)
iteration := 0
totalToolCalls := 0
var finalContent string
var asyncToolCalls []string // track async spawn tool names for fallback
var asyncToolCalls []string // track async spawn tool names for fallback
var mediaResults []MediaResult // media files from tool MEDIA: results
var deliverables []string // actual content from tool outputs (for team task results)
var blockReplies int // count of block.reply events emitted (for dedup in consumer)
Expand All @@ -474,7 +474,7 @@ func (l *Loop) runLoop(ctx context.Context, req RunRequest) (*RunResult, error)
// Team task orphan detection: track team_tasks create vs spawn calls.
// If the LLM creates tasks but forgets to spawn, inject a reminder.
var teamTaskCreates int // count of team_tasks action=create calls
var teamTaskSpawns int // count of spawn calls with team_task_id
var teamTaskSpawns int // count of spawn calls with team_task_id
var teamTaskRetried bool // only retry once to prevent infinite loops

// Inject retry hook so channels can update placeholder on LLM retries.
Expand Down Expand Up @@ -669,7 +669,7 @@ func (l *Loop) runLoop(ctx context.Context, req RunRequest) (*RunResult, error)
Content: resp.Content,
Thinking: resp.Thinking, // reasoning_content passback for thinking models (Kimi, DeepSeek)
ToolCalls: resp.ToolCalls,
Phase: resp.Phase, // preserve Codex phase metadata (gpt-5.3-codex)
Phase: resp.Phase, // preserve Codex phase metadata (gpt-5.3-codex)
RawAssistantContent: resp.RawAssistantContent, // preserve thinking blocks for Anthropic passback
}
messages = append(messages, assistantMsg)
Expand Down Expand Up @@ -1080,4 +1080,3 @@ func (l *Loop) scanWebToolResult(toolName string, result *tools.Result) {
strings.Join(injMatches, ", "), result.ForLLM)
}
}

5 changes: 3 additions & 2 deletions internal/agent/loop_history.go
Original file line number Diff line number Diff line change
Expand Up @@ -372,9 +372,10 @@ func (l *Loop) maybeSummarize(ctx context.Context, sessionKey string) {
var sb string
var mediaKinds []string
for _, m := range toSummarize {
if m.Role == "user" {
switch m.Role {
case "user":
sb += fmt.Sprintf("user: %s\n", m.Content)
} else if m.Role == "assistant" {
case "assistant":
sb += fmt.Sprintf("assistant: %s\n", SanitizeAssistantContent(m.Content))
}
for _, ref := range m.MediaRefs {
Expand Down
2 changes: 1 addition & 1 deletion internal/agent/loop_history_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,7 @@ func TestSanitizeHistory_DropsOrphanedToolMidHistory(t *testing.T) {

func TestEstimateTokens(t *testing.T) {
msgs := []providers.Message{
{Role: "user", Content: "Hello world!"}, // 12 chars → ~4 tokens
{Role: "user", Content: "Hello world!"}, // 12 chars → ~4 tokens
{Role: "assistant", Content: "Hi there, how are you?"}, // 22 chars → ~7 tokens
}
got := EstimateTokens(msgs)
Expand Down
Loading