Skip to content
Closed
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
29 changes: 28 additions & 1 deletion cli/azd/extensions/azure.ai.agents/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,34 @@

## Preview feature opt-ins

Three capabilities are independently disabled by default:
Three capabilities are independently disabled by default.

Run `azd ai agent list-alpha` to see every available agent preview feature and its
effective status, including disabled features. Use `--environment <name>` (`-e`)
to read a specific environment. The command is always available and follows the
default `azd config list-alpha` format:

```text
Name: agents.long_running
Description: Start background Responses invocations.
Status: Off

Name: agents.managed_harness
Description: Use a Microsoft Copilot harness with prompt agents.
Status: Off

Name: agents.voice
Description: Use managed voice agents and hosted voice bridges.
Status: Off
```

Records are sorted by feature ID. Each status uses the same opt-in checks as
agent operations and is either `On` or `Off`. The command ignores
`alpha.all` and does not list core-only features. Without a project
or default environment, available sources still apply. Explicit invalid
environments and source-loading errors fail before any records are printed.
The command adds no authentication, prompts, or configuration writes; existing
host project/environment loading behavior still applies.

| Feature ID | Scope | Environment variable |
| --- | --- | --- |
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ azure.ai.agents/
│ ├── cmd/ # Command discovery and runtime callers
│ ├── project/ # Definition classification and callers
│ └── featureflags/
│ ├── agents.go # Identities and thin Require check
│ ├── agents.go # Catalog and thin Require check
│ ├── featureflags.go # Validation, source reads, evaluation
│ └── featureflags_test.go # Source combinations and regressions
└── docs/specs/
Expand All @@ -44,10 +44,13 @@ Keep validation, reads, and small evaluation helpers together in one implementat

```go
type Feature struct {
ID string
EnvVar string
ID string
Description string
EnvVar string
}

func List() []Feature

func IsEnabled(
ctx context.Context,
client *azdext.AzdClient,
Expand Down Expand Up @@ -82,6 +85,33 @@ azd env set AZD_FOUNDRY_EXT_ENABLE_AGENTS_VOICE true

The [README](../../README.md#preview-feature-opt-ins) lists the equivalent commands for all three capabilities.

### Listing preview features

`azd ai agent list-alpha` is always available, including when all gates are off.
`List` returns copies of all three declarations sorted by ID, with descriptions
defined alongside their IDs and environment keys. The command calls `IsEnabled`
for each declaration with the parsed `ExtensionContext.Environment`; it does
not consult core's feature manager or infer a catalog from configured values.

Output matches the default `azd config list-alpha` shape: `Name: <ID>`,
`Description: <description>`, and `Status: On` or `Status: Off`, with blank lines
between records and a final newline. All statuses are resolved before anything
is printed to the Cobra command's injected writer. A later source error
therefore cannot leave earlier records looking like a successful partial list.
Writer failures also propagate.

`alpha.all` is ignored: setting it alone leaves all three statuses `Off`.
`agents.hosted` and core-only features are absent. The only supported output
format is `default`; unsupported explicit formats, including an empty value,
and positional arguments fail before source reads. Existing global flags and
SDK help, completion, and metadata behavior are preserved.

The command adds no cloud calls, authentication, prompts, writes, or
feature-specific telemetry. It inherits the optional-scope/error contract
below. Existing host project/environment loaders may still have their own
environment-selection or state-loading behavior; this is not a host-wide
no-side-effects guarantee.

## Documented enablement methods

The documented ways to opt into a feature are listed below. These opt-ins are additive: any enabling value is sufficient.
Expand Down Expand Up @@ -126,7 +156,7 @@ An explicit HTTP `--agent-endpoint` carries no kind or harness information. It r

Read, show, endpoint discovery, invocation show/follow/cancel, delete, and down retain access when opt-ins are disabled. There is no state migration or automatic enablement. Agents-private checks do **not** intercept evaluations or routines sending generic requests to existing agents. Those extensions cannot import this package, and this design makes no cross-extension enforcement claim.

The small API deliberately returns only an enablement decision and error, not a source report, registry entry, or policy object. OR semantics avoid accidental disabling by unrelated scopes, at the cost of having no local override for an existing opt-in. Exact optional-error matching avoids swallowing real failures but remains coupled to the existing host error contract.
The evaluator deliberately returns only an enablement decision and error, not a source report, registry entry, or policy object. The listing accessor exposes declarations, not source provenance. OR semantics avoid accidental disabling by unrelated scopes, at the cost of having no local override for an existing opt-in. Exact optional-error matching avoids swallowing real failures but remains coupled to the existing host error contract.

Disabled capabilities return the structured `feature_not_enabled` compatibility error with the exact per-feature config/environment enable commands. Explicit preview inputs are rejected rather than silently dropped. Enabling one capability does not enable another.

Expand All @@ -136,4 +166,10 @@ Tests cover source combinations, all supported boolean spellings and non-enablin

Command/provider regressions additionally cover independent gates, obsolete hosted opt-in isolation, selected-environment forwarding, voice discovery, explicit init/add inputs, staged manifests, local invocation bypasses, unchanged inputs on disabled-feature rejection, ordinary flows, and retained read access.

Listing regressions cover the complete sorted catalog, independent statuses from
each source despite `alpha.all=on`, ignored global values, the exact record format,
selected/default/explicit-empty environment forwarding, optional scopes, and
source or writer failures without partial source-success output. Help and
metadata advertise the command and its contextual enable examples.

Run offline tests, build, vet, and scoped lint against the pinned SDK. Tier 0 scenarios document the new opt-in UX; they require a separate scenario-run request. No Azure scenario execution or user auth/config mutation is needed for unit validation.
4 changes: 3 additions & 1 deletion cli/azd/extensions/azure.ai.agents/internal/cmd/help_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,9 @@ func TestAgentHelpColor(t *testing.T) {
color.NoColor = false
t.Cleanup(func() { color.NoColor = previous })

for _, path := range [][]string{nil, {"init"}, {"invoke"}, {"files", "list"}, {"optimize"}, {"eval", "show"}} {
for _, path := range [][]string{
nil, {"init"}, {"invoke"}, {"list-alpha"}, {"files", "list"}, {"optimize"}, {"eval", "show"},
} {
t.Run(strings.Join(path, "_"), func(t *testing.T) {
root := NewRootCommand()
var buf bytes.Buffer
Expand Down
85 changes: 85 additions & 0 deletions cli/azd/extensions/azure.ai.agents/internal/cmd/list_alpha.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

package cmd

import (
"fmt"
"strings"

"azureaiagent/internal/exterrors"
"azureaiagent/internal/featureflags"

"github.com/azure/azure-dev/cli/azd/pkg/azdext"
"github.com/spf13/cobra"
)

func newListAlphaCommand(extCtx *azdext.ExtensionContext) *cobra.Command {
extCtx = ensureExtensionContext(extCtx)
cmd := &cobra.Command{
Use: "list-alpha",
Short: "Display all available agent preview features and their status.",
Long: `Display all declared agent preview features, including disabled features,
sorted by name. Each entry includes its name, description, and On or Off status,
matching the default format of 'azd config list-alpha'.

Status uses the same opt-in checks as agent operations. Enable individual
features through user config, the process environment, or the selected azd
environment (-e). An enabling value turns a feature on; false does not override
another opt-in.
Only alpha.<ID> config keys apply; alpha.all does not enable agent previews.

Without a project or default environment, the available sources still apply.
An invalid explicit environment or a source read error fails without printing
feature records. This command is always available and needs no preview opt-in.`,
Example: fmt.Sprintf(` # List every agent preview feature and its effective status
azd ai agent list-alpha

# Read values from a specific environment
azd ai agent list-alpha --environment dev

# Enable voice through user config
azd config set alpha.%s on

# Alternatively, enable voice in the selected environment
azd env set %s true`, featureflags.Voice.ID, featureflags.Voice.EnvVar),
Args: cobra.NoArgs,
RunE: func(cmd *cobra.Command, _ []string) error {
client, err := azdext.NewAzdClient()
if err != nil {
return exterrors.Internal(
exterrors.CodeAzdClientFailed,
fmt.Sprintf("connecting to azd to list preview features: %s", err),
)
}
defer client.Close()

features := featureflags.List()
records := make([]string, 0, len(features))
for _, feature := range features {
enabled, err := featureflags.IsEnabled(cmd.Context(), client, feature, extCtx.Environment)
if err != nil {
return err
}
status := "Off"
if enabled {
status = "On"
}
records = append(records, fmt.Sprintf(
"Name: %s\nDescription: %s\nStatus: %s", feature.ID, feature.Description, status,
))
}

if _, err := fmt.Fprintln(cmd.OutOrStdout(), strings.Join(records, "\n\n")); err != nil {
return fmt.Errorf("writing preview feature list: %w", err)
}
return nil
},
}
azdext.RegisterFlagOptions(cmd, azdext.FlagOptions{
Name: "output",
AllowedValues: []string{"default"},
Default: "default",
})
return cmd
}
Loading
Loading