From 8cf4ca6a610ee2cc9e8ff6abb78a97304e129dde Mon Sep 17 00:00:00 2001 From: paulthanson082-glitch Date: Sun, 8 Feb 2026 06:31:28 -0800 Subject: [PATCH 1/9] Create new_agent.yaml --- examples/new_agent.yaml | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 examples/new_agent.yaml diff --git a/examples/new_agent.yaml b/examples/new_agent.yaml new file mode 100644 index 000000000..7c65f7944 --- /dev/null +++ b/examples/new_agent.yaml @@ -0,0 +1,21 @@ +#!/usr/bin/env cagent run +## Generated by GitHub Copilot on 2026-02-05 +agents: + new_agent: + model: openai/gpt-4o + description: "Scaffolded agent for concise help, code, and file tasks." + instruction: | + You are the `new_agent`. Follow these rules: + - Be concise, helpful, and actionable. + - Prefer short examples; provide runnable snippets when applicable. + - Ask a clarifying question if the user's intent is ambiguous. + toolsets: + - type: filesystem + - type: shell + - type: memory + path: ./examples/new_agent_memory.db + - type: think + max_iterations: 10 + num_history_items: 20 + add_date: true + code_mode_tools: true From bf3099c663fa3ef99abc5c263cd8d9a611ebc74f Mon Sep 17 00:00:00 2001 From: paulthanson082-glitch Date: Wed, 11 Feb 2026 06:09:57 -0800 Subject: [PATCH 2/9] Create scaffolded_default_agent.yaml --- examples/scaffolded_default_agent.yaml | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 examples/scaffolded_default_agent.yaml diff --git a/examples/scaffolded_default_agent.yaml b/examples/scaffolded_default_agent.yaml new file mode 100644 index 000000000..b18064049 --- /dev/null +++ b/examples/scaffolded_default_agent.yaml @@ -0,0 +1,9 @@ +agents: + scaffolded_default: + model: openai/gpt-5-mini + description: "Scaffolded default agent" + instruction: | + You are a helpful assistant. Respond concisely and ask clarifying questions when necessary. + toolsets: + - type: think + - type: todo From 15fbd8a233ad0c6d2e1064bcb0690af533b50696 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 12 Feb 2026 08:11:20 +0000 Subject: [PATCH 3/9] Initial plan From 1eb938b0cc4ce74163cb4d7630ef4d8747f7670c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 12 Feb 2026 08:13:03 +0000 Subject: [PATCH 4/9] Fix nightly scan workflow to skip when API keys are missing Co-authored-by: paulthanson082-glitch <252514830+paulthanson082-glitch@users.noreply.github.com> --- .github/workflows/nightly-scan.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/nightly-scan.yml b/.github/workflows/nightly-scan.yml index d4740f36f..dcb88404c 100644 --- a/.github/workflows/nightly-scan.yml +++ b/.github/workflows/nightly-scan.yml @@ -23,6 +23,11 @@ jobs: scan: runs-on: ubuntu-latest timeout-minutes: 30 + # Skip if no API keys are configured + if: | + secrets.ANTHROPIC_API_KEY != '' || + secrets.OPENAI_API_KEY != '' || + secrets.GEMINI_API_KEY != '' env: HAS_APP_SECRETS: ${{ secrets.CAGENT_REVIEWER_APP_ID != '' }} From 6028e68b7e89c1a3fd550ea7a64a56b85882ede2 Mon Sep 17 00:00:00 2001 From: paulthanson082-glitch Date: Thu, 2 Apr 2026 23:49:51 -0700 Subject: [PATCH 5/9] Use actual version in OTel service resource Replace hardcoded "dev" string with version.Version from pkg/version so OTel traces report the real build version. Co-Authored-By: Claude Sonnet 4.6 --- cmd/root/otel.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cmd/root/otel.go b/cmd/root/otel.go index 1d29573db..13dd6e3f6 100644 --- a/cmd/root/otel.go +++ b/cmd/root/otel.go @@ -6,6 +6,7 @@ import ( "os" "time" + "github.com/docker/cagent/pkg/version" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp" "go.opentelemetry.io/otel/sdk/resource" @@ -22,7 +23,7 @@ func initOTelSDK(ctx context.Context) (err error) { resource.NewWithAttributes( semconv.SchemaURL, semconv.ServiceName(AppName), - semconv.ServiceVersion("dev"), // TODO: use actual version + semconv.ServiceVersion(version.Version), ), ) if err != nil { From 360a3696cc2871a6ac9edac3784a6f48b95cd076 Mon Sep 17 00:00:00 2001 From: paulthanson082-glitch Date: Fri, 3 Apr 2026 00:03:51 -0700 Subject: [PATCH 6/9] Handle rollback error and make OTel TLS configurable - migrations: use named return to surface tx.Rollback errors, ignoring sql.ErrTxDone (expected after a successful commit) - otel: drop hardcoded WithInsecure(); infer from endpoint URL scheme so http:// endpoints use insecure and https:// use TLS Co-Authored-By: Claude Sonnet 4.6 --- cmd/root/otel.go | 10 ++++++---- pkg/session/migrations.go | 7 ++++--- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/cmd/root/otel.go b/cmd/root/otel.go index 13dd6e3f6..4e036f5d3 100644 --- a/cmd/root/otel.go +++ b/cmd/root/otel.go @@ -4,6 +4,7 @@ import ( "context" "fmt" "os" + "strings" "time" "github.com/docker/cagent/pkg/version" @@ -35,10 +36,11 @@ func initOTelSDK(ctx context.Context) (err error) { // Only initialize if endpoint is configured if endpoint != "" { - traceExporter, err = otlptracehttp.New(ctx, - otlptracehttp.WithEndpoint(endpoint), - otlptracehttp.WithInsecure(), // TODO: make configurable - ) + opts := []otlptracehttp.Option{otlptracehttp.WithEndpoint(endpoint)} + if strings.HasPrefix(endpoint, "http://") { + opts = append(opts, otlptracehttp.WithInsecure()) + } + traceExporter, err = otlptracehttp.New(ctx, opts...) if err != nil { return fmt.Errorf("failed to create trace exporter: %w", err) } diff --git a/pkg/session/migrations.go b/pkg/session/migrations.go index 4a364e8b3..ce0c6442e 100644 --- a/pkg/session/migrations.go +++ b/pkg/session/migrations.go @@ -96,14 +96,15 @@ func (m *MigrationManager) isMigrationApplied(ctx context.Context, name string) } // applyMigration applies a single migration -func (m *MigrationManager) applyMigration(ctx context.Context, migration *Migration) error { +func (m *MigrationManager) applyMigration(ctx context.Context, migration *Migration) (retErr error) { tx, err := m.db.BeginTx(ctx, nil) if err != nil { return err } defer func() { - // TODO: handle error - _ = tx.Rollback() + if rbErr := tx.Rollback(); rbErr != nil && !errors.Is(rbErr, sql.ErrTxDone) { + retErr = errors.Join(retErr, fmt.Errorf("failed to rollback migration transaction: %w", rbErr)) + } }() // Execute SQL migration if present From 78504722ab477380974687674ec31e14d39e74cf Mon Sep 17 00:00:00 2001 From: paulthanson082-glitch Date: Fri, 3 Apr 2026 00:15:53 -0700 Subject: [PATCH 7/9] Pass actual tool response to post-tool hooks executeToolWithHandler now returns the ToolCallResult so runTool can forward the output to post-tool hook inputs instead of nil. Co-Authored-By: Claude Sonnet 4.6 --- pkg/runtime/runtime.go | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/pkg/runtime/runtime.go b/pkg/runtime/runtime.go index 18ad4574f..b04364524 100644 --- a/pkg/runtime/runtime.go +++ b/pkg/runtime/runtime.go @@ -1614,7 +1614,7 @@ func (r *LocalRuntime) executeToolWithHandler( a *agent.Agent, spanName string, execute func(ctx context.Context) (*tools.ToolCallResult, time.Duration, error), -) { +) *tools.ToolCallResult { ctx, span := r.startSpan(ctx, spanName, trace.WithAttributes( attribute.String("tool.name", toolCall.Function.Name), attribute.String("agent", a.Name()), @@ -1660,6 +1660,7 @@ func (r *LocalRuntime) executeToolWithHandler( CreatedAt: time.Now().Format(time.RFC3339), } addAgentMessage(sess, a, &toolResponseMsg, events) + return res } // runTool executes agent tools from toolsets (MCP, filesystem, etc.). @@ -1693,7 +1694,7 @@ func (r *LocalRuntime) runTool(ctx context.Context, tool tools.Tool, toolCall to } } - r.executeToolWithHandler(ctx, toolCall, tool, events, sess, a, "runtime.tool.handler", + toolResult := r.executeToolWithHandler(ctx, toolCall, tool, events, sess, a, "runtime.tool.handler", func(ctx context.Context) (*tools.ToolCallResult, time.Duration, error) { res, err := tool.Handler(ctx, toolCall) return res, 0, err @@ -1702,13 +1703,17 @@ func (r *LocalRuntime) runTool(ctx context.Context, tool tools.Tool, toolCall to // Execute post-tool hooks if configured if hooksExec != nil && hooksExec.HasPostToolUseHooks() { toolInput := parseToolInput(toolCall.Function.Arguments) + var toolResponse any + if toolResult != nil { + toolResponse = toolResult.Output + } input := &hooks.Input{ SessionID: sess.ID, Cwd: r.workingDir, ToolName: toolCall.Function.Name, ToolUseID: toolCall.ID, ToolInput: toolInput, - ToolResponse: nil, // TODO: pass actual tool response if needed + ToolResponse: toolResponse, } result, err := hooksExec.ExecutePostToolUse(ctx, input) From 999c8fcf2e8ec2e846fa499e479b24a8b8411565 Mon Sep 17 00:00:00 2001 From: paulthanson082-glitch Date: Fri, 3 Apr 2026 06:00:38 -0700 Subject: [PATCH 8/9] Rename JSON field to snake_case for consistency Change agentName JSON tag to agent_name to match API naming conventions and style guides. Co-Authored-By: Claude Haiku 4.5 --- pkg/session/session.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/session/session.go b/pkg/session/session.go index 731901003..c584b0ff6 100644 --- a/pkg/session/session.go +++ b/pkg/session/session.go @@ -148,7 +148,7 @@ type PermissionsConfig struct { type Message struct { // ID is the database ID of the message (used for persistence tracking) ID int64 `json:"-"` - AgentName string `json:"agentName"` // TODO: rename to agent_name + AgentName string `json:"agent_name"` Message chat.Message `json:"message"` // Implicit is an optional field to indicate if the message shouldn't be shown to the user. It's needed for special situations // like when an agent transfers a task to another agent - new session is created with a default user message, but this shouldn't be shown to the user. From d5533d56c60c8589462e82ffc1fed880bdc671ea Mon Sep 17 00:00:00 2001 From: paulthanson082-glitch Date: Fri, 3 Apr 2026 06:01:44 -0700 Subject: [PATCH 9/9] Use environment provider for OPENAI_API_KEY Replace direct os.Getenv with environment.OsEnvProvider for consistency with codebase patterns. Co-Authored-By: Claude Haiku 4.5 --- pkg/tui/tui.go | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/pkg/tui/tui.go b/pkg/tui/tui.go index ced3b6566..ea22ea7ce 100644 --- a/pkg/tui/tui.go +++ b/pkg/tui/tui.go @@ -15,6 +15,7 @@ import ( "github.com/docker/cagent/pkg/app" "github.com/docker/cagent/pkg/audio/transcribe" + "github.com/docker/cagent/pkg/environment" "github.com/docker/cagent/pkg/runtime" "github.com/docker/cagent/pkg/tui/animation" "github.com/docker/cagent/pkg/tui/commands" @@ -117,6 +118,11 @@ func DefaultKeyMap() KeyMap { } } +func getEnvVar(key string) string { + val, _ := environment.NewOsEnvProvider().Get(context.Background(), key) + return val +} + // New creates and initializes a new TUI application model func New(ctx context.Context, a *app.App) tea.Model { sessionState := service.NewSessionState(a.Session()) @@ -131,7 +137,7 @@ func New(ctx context.Context, a *app.App) tea.Model { completions: completion.New(), application: a, sessionState: sessionState, - transcriber: transcribe.New(os.Getenv("OPENAI_API_KEY")), // TODO(dga): should use envProvider + transcriber: transcribe.New(getEnvVar("OPENAI_API_KEY")), // Set up theme subscription using the subscription package themeSubscription: subscription.NewChannelSubscription(themeEventCh, func(themeRef string) tea.Msg { return messages.ThemeFileChangedMsg{ThemeRef: themeRef}