Skip to content
Merged
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
17 changes: 10 additions & 7 deletions backend/src/apis/shared/feature_flags.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,13 +67,16 @@ def agents_enabled() -> bool:
"""Whether the Agent Designer surface is enabled for this environment.

Gates the ``/agents/*`` alias router (the governed Agent read/write surface over
the evolved assistant store). **Defaults off** (the ``MEMORY_SPACES_ENABLED``
pattern): set ``AGENTS_API_ENABLED=true`` to turn it on. The feature ships
incrementally across several PRs (contract → surface → resolution → Designer UI),
so it stays dark until complete — the assistant store and its ``/assistants/*``
surface are unaffected while off.
the evolved assistant store). **Default ON with a kill switch** (house style,
mirroring ``scheduled_runs_enabled``): unset or empty resolves to enabled; only
the literal ``"false"`` (case-insensitive) disables. The CDK side threads
``config.agents.enabled`` into this env var with the same empty-string-safe
ternary, so an unset GitHub Actions variable can never silently turn it off. The
Agent Designer shipped across several PRs (contract → surface → resolution →
Designer UI → binding reflection); now complete, it defaults on.

Gates *feature existence* per environment; *who* may use a specific agent is the
identity-based access check already enforced by the assistant service.
identity-based access check already enforced by the assistant service, and the SPA
nav is separately preview-gated (system-admin) until Assistants are deprecated.
"""
return os.environ.get("AGENTS_API_ENABLED", "false").lower() == "true"
return os.environ.get("AGENTS_API_ENABLED", "").strip().lower() != "false"
25 changes: 14 additions & 11 deletions infrastructure/lib/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -172,11 +172,12 @@ export interface MemorySpacesConfig {
}

/**
* Agent Designer feature flag. Default OFF with an on switch — the governed
* `/agents/*` surface is opt-in per environment, enabled with
* CDK_AGENTS_API_ENABLED=true (or an `agents.enabled: true` cdk.json context).
* Sets the AGENTS_API_ENABLED env var on app-api. The feature ships
* incrementally, so it stays dark until complete; `/assistants/*` is unaffected.
* Agent Designer feature flag. Default ON with a kill switch — the governed
* `/agents/*` surface ships everywhere now that the Designer is complete; only
* CDK_AGENTS_API_ENABLED=false (or an `agents.enabled: false` cdk.json context)
* turns it off. Sets the AGENTS_API_ENABLED env var on app-api + inference-api.
* `/assistants/*` is unaffected, and the SPA nav stays preview-gated until
* Assistants are deprecated.
*/
export interface AgentsConfig {
enabled: boolean;
Expand Down Expand Up @@ -342,13 +343,15 @@ export function loadConfig(scope: cdk.App): AppConfig {
: scope.node.tryGetContext('memorySpaces')?.enabled ?? true,
},
agents: {
// Default OFF with an on switch (same pattern as memorySpaces): opt-in per
// environment. The workflow forwards an EMPTY STRING when unset, so treat
// empty/unset as the default (off) and only the literal "true" as on. An
// `agents.enabled` cdk.json context can also force it on.
// Default ON with a kill switch (house style, mirroring memorySpaces /
// scheduledRuns): the workflow forwards an EMPTY STRING when unset, so treat
// empty/unset as the default (on) and only the literal "false" as the off
// switch. An `agents.enabled` cdk.json context can also force it off. The
// Agent Designer is complete, so it ships on everywhere; the SPA nav stays
// preview-gated (system-admin) until Assistants are deprecated.
enabled: process.env.CDK_AGENTS_API_ENABLED
? process.env.CDK_AGENTS_API_ENABLED === 'true'
: scope.node.tryGetContext('agents')?.enabled ?? false,
? process.env.CDK_AGENTS_API_ENABLED !== 'false'
: scope.node.tryGetContext('agents')?.enabled ?? true,
},
fineTuning: {
additionalCorsOrigins: process.env.CDK_FINE_TUNING_CORS_ORIGINS || scope.node.tryGetContext('fineTuning')?.additionalCorsOrigins,
Expand Down
4 changes: 3 additions & 1 deletion infrastructure/test/app-api-environment.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,9 @@ describe('buildAppApiEnvironment — Memory Spaces', () => {
expect(on.MEMORY_SPACES_ENABLED).toBe('true');
});

it('gates the /agents surface on AGENTS_API_ENABLED (default off)', () => {
it('threads config.agents.enabled into the AGENTS_API_ENABLED env var', () => {
// The mock config sets agents.enabled=false explicitly; the loadConfig default
// is ON with a kill switch (covered in config.test.ts).
const off = buildAppApiEnvironment(createMockConfig(), stubParams());
expect(off.AGENTS_API_ENABLED).toBe('false');

Expand Down
38 changes: 38 additions & 0 deletions infrastructure/test/config.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -422,6 +422,44 @@ describe('RAG Ingestion Configuration', () => {
});
});

// ============================================================
// Agents API (Agent Designer) feature flag — default ON with a kill switch
// (complete feature; ships enabled for forkers, empty var must not disable)
// ============================================================

describe('Agents API feature flag', () => {
test('defaults to enabled when CDK_AGENTS_API_ENABLED is unset', () => {
delete process.env.CDK_AGENTS_API_ENABLED;

expect(loadConfig(app).agents.enabled).toBe(true);
});

test('treats empty string (unset GitHub Actions variable) as enabled', () => {
process.env.CDK_AGENTS_API_ENABLED = '';

expect(loadConfig(app).agents.enabled).toBe(true);
});

test('CDK_AGENTS_API_ENABLED="false" is the kill switch', () => {
process.env.CDK_AGENTS_API_ENABLED = 'false';

expect(loadConfig(app).agents.enabled).toBe(false);
});

test('CDK_AGENTS_API_ENABLED="true" stays enabled', () => {
process.env.CDK_AGENTS_API_ENABLED = 'true';

expect(loadConfig(app).agents.enabled).toBe(true);
});

test('cdk.json context agents.enabled=false disables when env is unset', () => {
delete process.env.CDK_AGENTS_API_ENABLED;
app.node.setContext('agents', { enabled: false });

expect(loadConfig(app).agents.enabled).toBe(false);
});
});

// ============================================================
// Configuration Validation Tests
// ============================================================
Expand Down