Skip to content

Add simple and agent presets for the AI bridge#75

Open
batuhan wants to merge 1 commit intomainfrom
batuhan/ai-simple-agent-modes
Open

Add simple and agent presets for the AI bridge#75
batuhan wants to merge 1 commit intomainfrom
batuhan/ai-simple-agent-modes

Conversation

@batuhan
Copy link
Member

@batuhan batuhan commented Mar 24, 2026

Summary

  • add agents.enabled gating to the AI bridge so simple model chats and agent chats can be globally split
  • keep models available while hiding/blocking agent discovery, agent chat creation, and agent-target execution when agents are disabled
  • add agentremote run agent as an AI-bridge preset/alias with agent mode enabled, while agentremote run ai defaults to simple mode

Testing

  • go test ./...

@coderabbitai
Copy link

coderabbitai bot commented Mar 24, 2026

📝 Walkthrough

Summary by CodeRabbit

Release Notes

  • New Features

    • Agents can now be disabled/enabled via the agents.enabled configuration setting
    • New agent bridge instance with dedicated configuration, database, and port
  • Configuration Changes

    • Added agents.enabled configuration option for controlling agent functionality (defaults to enabled)
    • New agent bridge entry with isolated setup and resources
  • Bug Fixes

    • Agent operations now properly enforce agent status—operations fail gracefully when agents are disabled
    • Portal selection correctly excludes agent portals when agent mode is disabled
    • Internal message dispatch validates agent access permissions

Walkthrough

This PR implements agent mode gating for the AI bridge, introducing a new agent bridge instance with agents enabled while disabling agents in the existing AI instance. Configuration options allow enabling/disabling agent functionality, with permission checks preventing agent-related operations when disabled. Bridge infrastructure updated to support the new agent instance type.

Changes

Cohort / File(s) Summary
Configuration & Manifest
bridges.manifest.yml, config.example.yaml, bridges/ai/integrations_config.go, bridges/ai/integrations_example-config.yaml
Added new agent bridge instance with agents enabled; disabled agents in existing ai instance. Extended AgentsConfig with optional Enabled boolean pointer field for runtime feature gating. Added commented configuration template lines.
Core Agent Mode Implementation
bridges/ai/agent_mode.go
New file introducing agent-mode gating with errAgentsDisabled sentinel error, helper methods on *Config, *OpenAIConnector, and *AIClient to determine agent enablement status, permission checks (agentTargetBlocked, ensureAgentTargetAllowed), portal exclusion logic, and chat portal selection helpers.
Chat & Portal Management
bridges/ai/chat.go, bridges/ai/default_chat_test.go
Added agentsEnabled() gating to agent-specific creation/resolution paths. Replaced local helper functions with instance methods for default-chat selection. Conditionally configure Beeper agent only when agents enabled. Updated tests to use new instance methods.
Access Control & Permission Checks
bridges/ai/agent_activity.go, bridges/ai/handleai.go, bridges/ai/handlematrix.go, bridges/ai/internal_dispatch.go
Added early-return guards when agentTargetBlocked() or ensureAgentTargetAllowed() checks fail, preventing further processing of agent-targeted portals/messages when agents disabled.
Agent Discovery & Catalog
bridges/ai/sdk_agent_catalog.go, bridges/ai/sdk_agent_catalog_test.go
Added guards in catalog methods (DefaultAgent, ListAgents, ResolveAgent) to return nil/empty when agents disabled. Extended test client setup with model cache and login state. Added test case verifying disabled agents return no catalog results.
Agent Provisioning
bridges/ai/provisioning.go, bridges/ai/provisioning_agents_test.go
Added agentsEnabled() checks to agent endpoints; updated listAgentsForResponse signature to accept *AIClient parameter for enablement checks. Extended error handling for disabled-agent errors. New tests verify empty list and forbidden responses when agents disabled.
Heartbeat Configuration
bridges/ai/heartbeat_config.go, bridges/ai/heartbeat_config_test.go, bridges/ai/heartbeat_execute.go
Updated heartbeat agent resolution to skip processing when agents disabled. Modified permission checks to immediately return false/empty when agent support disabled. Added test case verifying disabled agents prevent heartbeat resolution.
Bridge Infrastructure & CLI
cmd/agentremote/bridges.go, cmd/agentremote/main.go, cmd/agentremote/run_bridge.go, cmd/internal/bridgeentry/bridgeentry.go, cmd/agentremote/commands.go
Added new agent bridge entry mapping to AI connector with runtime/remote bridge type declarations and config overrides. Helper functions remoteBridgeType() and runtimeBridgeType() map bridge types across local/remote contexts. Updated subprocess invocation and bridge startup to use mapped types. Extended example commands to include agent variant.
Connector & Miscellaneous
bridges/ai/connector.go, bridges/ai/chat_login_redirect_test.go, bridges/ai/config_test.go, bridges/ai/sessions_tools.go
Updated ValidateUserID to respect agent enablement; changed error handling in createLogin. New helper newDiscoveryTestClient(agentsEnabled) for conditional agent disabling in tests. Added unit tests for Config.agentsEnabled() behavior. Updated portal exclusion logic to use instance method.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 8.82% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main changes: adding agent and simple presets/modes for the AI bridge via an agents.enabled configuration flag.
Description check ✅ Passed The description clearly relates to the changeset, explaining the agents.enabled gating, model/agent separation, and the new presets for agent vs. simple modes.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch batuhan/ai-simple-agent-modes

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (2)
bridges/ai/handleai_test.go (1)

164-169: Assert the disabled-agent sentinel, not just the message.

This still passes if dispatchInternalMessage starts returning a different error type with the same text. Please also pin errors.Is(err, errAgentsDisabled) so the contract used by writeAgentError stays covered.

Suggested test tightening
 import (
 	"encoding/base64"
+	"errors"
 	"strings"
 	"testing"
@@
 	_, _, err := oc.dispatchInternalMessage(t.Context(), portal, agentModeTestMeta("beeper"), "hello", "test", false)
 	if err == nil {
 		t.Fatal("expected dispatchInternalMessage to fail")
 	}
+	if !errors.Is(err, errAgentsDisabled) {
+		t.Fatalf("expected errAgentsDisabled, got %v", err)
+	}
 	if !strings.Contains(strings.ToLower(err.Error()), "agents are disabled") {
 		t.Fatalf("unexpected error: %v", err)
 	}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@bridges/ai/handleai_test.go` around lines 164 - 169, The test should assert
the sentinel error rather than only matching text: after calling
dispatchInternalMessage in the failing case, add an assertion using
errors.Is(err, errAgentsDisabled) (importing errors if needed) to ensure the
returned error is the expected sentinel; keep the existing string check if you
want to preserve message validation but make errors.Is(err, errAgentsDisabled)
the primary contractual check tied to writeAgentError and
dispatchInternalMessage.
bridges/ai/provisioning_agents_test.go (1)

37-40: Make this sentinel test exercise wrapped errors.

errors.Is(errAgentsDisabled, errAgentsDisabled) only proves the standard library works. The behavior worth pinning here is that a wrapped errAgentsDisabled still matches and still returns 403 from writeAgentError.

Suggested replacement
 import (
 	"context"
 	"errors"
+	"fmt"
 	"net/http/httptest"
 	"strings"
 	"testing"
 )
@@
 func TestWriteAgentErrorDisabledMatchesSentinel(t *testing.T) {
-	if !errors.Is(errAgentsDisabled, errAgentsDisabled) {
-		t.Fatal("expected sentinel error to match itself")
-	}
+	wrapped := fmt.Errorf("wrapped: %w", errAgentsDisabled)
+	if !errors.Is(wrapped, errAgentsDisabled) {
+		t.Fatal("expected wrapped sentinel error to match errAgentsDisabled")
+	}
+
+	rec := httptest.NewRecorder()
+	writeAgentError(rec, wrapped)
+	if rec.Code != 403 {
+		t.Fatalf("expected 403 for wrapped disabled error, got %d", rec.Code)
+	}
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@bridges/ai/provisioning_agents_test.go` around lines 37 - 40, Replace the
trivial self-comparison in TestWriteAgentErrorDisabledMatchesSentinel with a
test that wraps errAgentsDisabled and verifies both errors.Is and
writeAgentError behavior: create a wrapped error (e.g., fmt.Errorf("...%w",
errAgentsDisabled)), assert errors.Is(wrapped, errAgentsDisabled), then call
writeAgentError with an httptest.ResponseRecorder and the wrapped error and
assert the recorder.Code equals http.StatusForbidden (403). Ensure you reference
the symbols TestWriteAgentErrorDisabledMatchesSentinel, errAgentsDisabled,
errors.Is, writeAgentError, httptest.ResponseRecorder, and http.StatusForbidden
when making the change.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@bridges/ai/handlematrix.go`:
- Around line 38-41: The new agent gate (ensureAgentTargetAllowed) is only
applied to fresh inbound messages but can be bypassed via edit regeneration; add
the same check at the start of HandleMatrixEdit (and/or inside
regenerateFromEdit) so edits that would re-trigger agent execution first call
oc.ensureAgentTargetAllowed(meta) and, on error, call oc.sendSystemNotice(ctx,
portal, err.Error()) and return nil, agentremote.UnsupportedMessageStatus(err)
to mirror the original gate behavior; reference the existing
ensureAgentTargetAllowed, HandleMatrixEdit, regenerateFromEdit,
sendSystemNotice, and agentremote.UnsupportedMessageStatus symbols to locate and
apply the guard.

In `@bridges/ai/provisioning_agents_test.go`:
- Around line 11-23: The test TestListAgentsForResponseDisabledReturnsEmpty
should assert that the returned items slice is initialized (not nil) in addition
to being empty; after calling listAgentsForResponse in that test, add an
assertion that items != nil (e.g., use t.Fatalf or t.Fatalf-style check) before
or alongside the existing len(items) check to ensure the response encodes an
empty array rather than null.

---

Nitpick comments:
In `@bridges/ai/handleai_test.go`:
- Around line 164-169: The test should assert the sentinel error rather than
only matching text: after calling dispatchInternalMessage in the failing case,
add an assertion using errors.Is(err, errAgentsDisabled) (importing errors if
needed) to ensure the returned error is the expected sentinel; keep the existing
string check if you want to preserve message validation but make errors.Is(err,
errAgentsDisabled) the primary contractual check tied to writeAgentError and
dispatchInternalMessage.

In `@bridges/ai/provisioning_agents_test.go`:
- Around line 37-40: Replace the trivial self-comparison in
TestWriteAgentErrorDisabledMatchesSentinel with a test that wraps
errAgentsDisabled and verifies both errors.Is and writeAgentError behavior:
create a wrapped error (e.g., fmt.Errorf("...%w", errAgentsDisabled)), assert
errors.Is(wrapped, errAgentsDisabled), then call writeAgentError with an
httptest.ResponseRecorder and the wrapped error and assert the recorder.Code
equals http.StatusForbidden (403). Ensure you reference the symbols
TestWriteAgentErrorDisabledMatchesSentinel, errAgentsDisabled, errors.Is,
writeAgentError, httptest.ResponseRecorder, and http.StatusForbidden when making
the change.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 14b9ad4d-8ae4-420d-adee-26eb0060dd2a

📥 Commits

Reviewing files that changed from the base of the PR and between dab7b83 and 14d4a78.

📒 Files selected for processing (28)
  • bridges.manifest.yml
  • bridges/ai/agent_activity.go
  • bridges/ai/agent_mode.go
  • bridges/ai/chat.go
  • bridges/ai/chat_login_redirect_test.go
  • bridges/ai/config_test.go
  • bridges/ai/connector.go
  • bridges/ai/default_chat_test.go
  • bridges/ai/handleai.go
  • bridges/ai/handleai_test.go
  • bridges/ai/handlematrix.go
  • bridges/ai/heartbeat_config.go
  • bridges/ai/heartbeat_config_test.go
  • bridges/ai/heartbeat_execute.go
  • bridges/ai/integrations_config.go
  • bridges/ai/integrations_example-config.yaml
  • bridges/ai/internal_dispatch.go
  • bridges/ai/provisioning.go
  • bridges/ai/provisioning_agents_test.go
  • bridges/ai/sdk_agent_catalog.go
  • bridges/ai/sdk_agent_catalog_test.go
  • bridges/ai/sessions_tools.go
  • cmd/agentremote/bridges.go
  • cmd/agentremote/commands.go
  • cmd/agentremote/main.go
  • cmd/agentremote/run_bridge.go
  • cmd/internal/bridgeentry/bridgeentry.go
  • config.example.yaml
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
  • GitHub Check: Lint
  • GitHub Check: build-agentremote-docker (arm64)
  • GitHub Check: build-agentremote-docker (amd64)
  • GitHub Check: build-docker
  • GitHub Check: Lint
  • GitHub Check: build-docker
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2026-03-16T09:01:24.464Z
Learnt from: batuhan
Repo: beeper/agentremote PR: 71
File: bridges/ai/connector.go:53-63
Timestamp: 2026-03-16T09:01:24.464Z
Learning: In package ai, the AI connector’s configuration type (Config) defines Bridge as a value field of type BridgeConfig (not a pointer). Therefore, accessing oc.Config.Bridge.CommandPrefix in OpenAIConnector.applyRuntimeDefaults (bridges/ai/connector.go) is safe and does not require a nil-check.

Applied to files:

  • bridges/ai/sdk_agent_catalog.go
  • bridges/ai/integrations_config.go
  • cmd/agentremote/run_bridge.go
  • cmd/internal/bridgeentry/bridgeentry.go
  • bridges/ai/handleai.go
  • bridges/ai/connector.go
  • bridges/ai/handleai_test.go
  • cmd/agentremote/main.go
  • cmd/agentremote/bridges.go
  • bridges/ai/agent_mode.go
  • bridges/ai/chat.go
🔇 Additional comments (31)
cmd/internal/bridgeentry/bridgeentry.go (1)

27-32: Looks good — new agent bridge definition is consistent and isolated.

Name, Port, and DBName align with the new preset wiring and avoid collisions with existing bridge entries.

cmd/agentremote/run_bridge.go (1)

26-28: Good parity update for AI and Agent presets.

Applying PortalEventBuffer = 0 to both bridge types keeps runtime behavior consistent between run ai and run agent.

bridges/ai/heartbeat_config_test.go (1)

47-60: Nice targeted coverage for disabled-agents heartbeat behavior.

This test closes an important regression gap by asserting both isHeartbeatEnabledForAgent and resolveHeartbeatAgents behavior when agents are explicitly disabled.

config.example.yaml (1)

202-202: Good config-template addition for discoverability.

Including agents.enabled in the example config makes the mode split explicit for operators.

bridges/ai/internal_dispatch.go (1)

34-36: Good early guard in internal dispatch path.

Blocking before trimming, context build, and DB writes prevents unintended execution and side effects in disabled-agent mode.

bridges/ai/integrations_example-config.yaml (1)

225-225: LGTM — helpful example update.

Adding agents.enabled to the integration example keeps the docs aligned with runtime behavior.

bridges/ai/heartbeat_execute.go (1)

33-34: Behavioral gate is correctly enforced in heartbeat agent resolution.

Returning an empty list when agents are disabled cleanly prevents heartbeat runs from being scheduled in that mode.

bridges/ai/config_test.go (1)

81-99: Good coverage for default vs explicit-disable behavior.

These tests clearly lock in the intended semantics for agentsEnabled() and reduce regression risk around pointer/nil config handling.

bridges/ai/sessions_tools.go (1)

75-76: Nice unification of portal visibility checks.

Using oc.shouldExcludeVisiblePortal(...) in both paths keeps session listing and label resolution aligned with the new agent-mode gate.

Also applies to: 544-545

bridges/ai/sdk_agent_catalog.go (1)

25-27: Consistent gating across catalog entry points.

The early exits make disabled-agent mode predictable for default, listing, and resolution paths.

Also applies to: 40-42, 69-71

bridges/ai/handleai.go (1)

265-267: Auto-greeting blocking is correctly enforced.

The pre-check and loop-time re-check close the gap for agent-disabled state transitions and prevent unintended greeting dispatches.

Also applies to: 304-307

bridges/ai/integrations_config.go (1)

110-110: Config schema and upgrader are in sync.

Adding agents.enabled to both AgentsConfig and upgradeConfig is the right migration-safe implementation.

Also applies to: 570-570

bridges/ai/agent_activity.go (1)

24-26: Good alignment with agent-mode-aware routing rules.

These changes correctly stop blocked-agent activity tracking and ensure default-chat selection uses the same exclusion policy.

Also applies to: 99-99, 104-104

bridges/ai/connector.go (1)

78-80: Validation and error semantics look cleaner.

Rejecting agent IDs when disabled and using ErrInvalidLoginFlowID makes behavior both safer and more uniform.

Also applies to: 103-103

bridges/ai/heartbeat_config.go (1)

10-11: Heartbeat gating is now consistently enforced.

The added checks correctly prevent explicit/default heartbeat enablement when agents.enabled is false.

Also applies to: 82-84

cmd/agentremote/main.go (1)

441-441: Nice: the alias resolves through the registry everywhere it matters.

Foreground exec, example-config generation, background start, and remote registration all now go through runtimeBridgeType(...) / remoteBridgeType(...), which keeps agent as a thin preset over ai instead of a forked code path.

Also applies to: 1156-1156, 1190-1190, 1215-1215

bridges/ai/provisioning.go (3)

225-236: LGTM! Clean sentinel error handling.

The errAgentsDisabled case is correctly placed first in the switch to ensure it's checked before other agent-related errors. Using errors.Is() ensures proper sentinel comparison even if the error is wrapped.


330-333: LGTM! Returning empty slice instead of nil is good for JSON serialization.

When agents are disabled, returning []*AgentDefinitionContent{} ensures the JSON response contains "agents": [] rather than "agents": null, providing a consistent API contract.


370-373: LGTM! Consistent guard placement across agent CRUD handlers.

All agent endpoints check agentsEnabled() early (before request parsing), returning a 403 Forbidden via the sentinel error. This is efficient and consistent.

Also applies to: 388-391, 420-423, 458-461

cmd/agentremote/bridges.go (2)

22-40: LGTM! Separate bridge instances with distinct config overrides.

Both "ai" and "agent" entries call aibridge.NewAIConnector() which creates independent instances (confirmed by context snippet showing it allocates new struct and maps each time). The ConfigOverrides approach cleanly separates the agent-enabled vs agent-disabled configurations.


63-77: LGTM! Defensive fallback in helper functions.

Both remoteBridgeType and runtimeBridgeType correctly fall back to localBridgeType when the registry entry doesn't exist or the field is empty. This ensures graceful degradation for unknown bridge types.

bridges/ai/agent_mode.go (4)

1-9: LGTM! Well-defined sentinel error for feature gating.

Using errors.New() creates a comparable sentinel that works correctly with errors.Is(). The error message is descriptive and user-facing appropriate.


11-34: LGTM! Nil-safe delegation chain with safe defaults.

The agentsEnabled() methods at each layer (Config → OpenAIConnector → AIClient) correctly default to true when any part of the chain is nil, maintaining backwards compatibility. The pointer dereference at line 15 is safe due to the nil checks.


36-52: LGTM! Clean predicate composition.

agentTargetBlocked correctly combines the three conditions (non-nil client, agents disabled, portal has agent). shouldExcludeVisiblePortal cleanly composes model-level exclusion with agent blocking.


58-79: LGTM! Correct portal selection with slug-based priority.

The chooseDefaultChatPortal logic correctly uses the haveSlug flag to gate comparisons against minIdx, avoiding any issues with the zero-initialized minIdx. Portals with the lowest slug index are preferred, with a fallback to the first eligible non-slug portal.

bridges/ai/chat.go (6)

412-414: LGTM! Early guard for agent ghost chat creation.

When the ghost ID parses as an agent, checking agentsEnabled() before proceeding prevents agent chat creation when the feature is disabled. The error is correctly wrapped with mautrix.MForbidden.


431-433: LGTM! Entry guard for resolveAgentIdentifier.

Placing the guard at the function entry ensures all code paths that resolve agent identifiers are protected when agents are disabled.


739-741: LGTM! Consistent guards in resolveNewChatTarget.

Both explicit (!ai new agent <id>) and implicit (current room has agent) agent-based chat creation paths are correctly blocked when agents are disabled.

Also applies to: 765-767


1108-1108: LGTM! Refactored to use instance methods for default chat selection.

The default chat candidate/selection logic now uses oc.isDefaultChatCandidate() and oc.chooseDefaultChatPortal() instance methods, which internally respect the agentsEnabled() state via shouldExcludeVisiblePortal.

Also applies to: 1136-1136, 1150-1150


1191-1210: LGTM! Conditional agent configuration in ensureDefaultChat.

When agents are enabled, the Beeper AI agent is configured as the chat target. When disabled, the portal is created as a simple model chat (using modelUserID set by initPortalForChat at line 678). This correctly implements the simple/agent mode split.


1226-1228: LGTM! Guard in ensureExistingChatPortalReady.

Using oc.isDefaultChatCandidate() ensures hidden or agent-blocked portals are rejected consistently.

Comment on lines +38 to +41
if err := oc.ensureAgentTargetAllowed(meta); err != nil {
oc.sendSystemNotice(ctx, portal, err.Error())
return nil, agentremote.UnsupportedMessageStatus(err)
}
Copy link

@coderabbitai coderabbitai bot Mar 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Agent-disabled policy can still be bypassed through edit regeneration.

The new gate at Line 38 protects fresh inbound messages, but HandleMatrixEdit/regenerateFromEdit can still trigger execution in an existing agent-target room when agents are disabled. That leaves a policy bypass path.

🔧 Suggested fix (apply in HandleMatrixEdit)
 func (oc *AIClient) HandleMatrixEdit(ctx context.Context, edit *bridgev2.MatrixEdit) error {
@@
 	portal := edit.Portal
 	if portal == nil {
 		return errors.New("portal is nil")
 	}
 	meta := portalMeta(portal)
+	if err := oc.ensureAgentTargetAllowed(meta); err != nil {
+		oc.sendSystemNotice(ctx, portal, err.Error())
+		return agentremote.UnsupportedMessageStatus(err)
+	}
@@
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@bridges/ai/handlematrix.go` around lines 38 - 41, The new agent gate
(ensureAgentTargetAllowed) is only applied to fresh inbound messages but can be
bypassed via edit regeneration; add the same check at the start of
HandleMatrixEdit (and/or inside regenerateFromEdit) so edits that would
re-trigger agent execution first call oc.ensureAgentTargetAllowed(meta) and, on
error, call oc.sendSystemNotice(ctx, portal, err.Error()) and return nil,
agentremote.UnsupportedMessageStatus(err) to mirror the original gate behavior;
reference the existing ensureAgentTargetAllowed, HandleMatrixEdit,
regenerateFromEdit, sendSystemNotice, and agentremote.UnsupportedMessageStatus
symbols to locate and apply the guard.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

its ok for now if existing rooms have this issue becuase there are no existing rooms right now

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@batuhan Understood — since there are no existing agent-target rooms today, the edit-regeneration path poses no practical risk. Happy to revisit this guard in a future PR if rooms start being created.

Comment on lines +11 to +23
func TestListAgentsForResponseDisabledReturnsEmpty(t *testing.T) {
disabled := false
client := newCatalogTestClient()
client.connector.Config.Agents = &AgentsConfig{Enabled: &disabled}

items, err := listAgentsForResponse(context.Background(), client, NewAgentStoreAdapter(client))
if err != nil {
t.Fatalf("listAgentsForResponse returned error: %v", err)
}
if len(items) != 0 {
t.Fatalf("expected no agents when disabled, got %#v", items)
}
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Assert that items is initialized, not just empty.

len(items) == 0 also passes for a nil slice, so this test won’t catch a regression from [] to null in the JSON response. Please assert items != nil as well.

Suggested assertion
 	items, err := listAgentsForResponse(context.Background(), client, NewAgentStoreAdapter(client))
 	if err != nil {
 		t.Fatalf("listAgentsForResponse returned error: %v", err)
 	}
+	if items == nil {
+		t.Fatal("expected initialized empty slice, got nil")
+	}
 	if len(items) != 0 {
 		t.Fatalf("expected no agents when disabled, got %#v", items)
 	}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
func TestListAgentsForResponseDisabledReturnsEmpty(t *testing.T) {
disabled := false
client := newCatalogTestClient()
client.connector.Config.Agents = &AgentsConfig{Enabled: &disabled}
items, err := listAgentsForResponse(context.Background(), client, NewAgentStoreAdapter(client))
if err != nil {
t.Fatalf("listAgentsForResponse returned error: %v", err)
}
if len(items) != 0 {
t.Fatalf("expected no agents when disabled, got %#v", items)
}
}
func TestListAgentsForResponseDisabledReturnsEmpty(t *testing.T) {
disabled := false
client := newCatalogTestClient()
client.connector.Config.Agents = &AgentsConfig{Enabled: &disabled}
items, err := listAgentsForResponse(context.Background(), client, NewAgentStoreAdapter(client))
if err != nil {
t.Fatalf("listAgentsForResponse returned error: %v", err)
}
if items == nil {
t.Fatal("expected initialized empty slice, got nil")
}
if len(items) != 0 {
t.Fatalf("expected no agents when disabled, got %#v", items)
}
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@bridges/ai/provisioning_agents_test.go` around lines 11 - 23, The test
TestListAgentsForResponseDisabledReturnsEmpty should assert that the returned
items slice is initialized (not nil) in addition to being empty; after calling
listAgentsForResponse in that test, add an assertion that items != nil (e.g.,
use t.Fatalf or t.Fatalf-style check) before or alongside the existing
len(items) check to ensure the response encodes an empty array rather than null.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

1 participant