doc: add design spec for azd ai agent routine commands#8200
Conversation
📋 Prioritization NoteThanks for the contribution! The linked issue isn't in the current milestone yet. |
There was a problem hiding this comment.
Pull request overview
Adds a design specification for the proposed azd ai agent routine command subtree in the azure.ai.agents extension, covering command behavior, endpoint resolution, wire mappings, telemetry, and tests.
Changes:
- Adds the routine command design spec and v1 command surface.
- Documents endpoint resolution, API routes, output shapes, telemetry, and test plan.
- Adds a cspell override for terminology in the new design doc.
Reviewed changes
Copilot reviewed 1 out of 1 changed files in this pull request and generated 8 comments.
| File | Description |
|---|---|
cli/azd/docs/design/ai-routine-design-spec.md |
New design spec for routine commands. |
cli/azd/.vscode/cspell.yaml |
Adds a file-specific spelling override for the new doc. |
| - Multi-trigger routines via the CLI — deferred ([§7 OQ-2](#7-open-questions)). | ||
| - Changing `--trigger` or `--action` *type* on an existing routine — delete and | ||
| recreate, mirroring the `connection` auth-type rule ([§4.2](#42-create-vs-update)). | ||
| - Creating routines from a file (`--file`) — tracked as [#8187](https://github.com/Azure/azure-dev/issues/8187). |
There was a problem hiding this comment.
Add this back to the scope. We need to be able to create from a file so routines can live in the repo as source controlled items.
|
|
||
| ### Out of scope | ||
|
|
||
| - Declarative routines (`routine.yaml`, `azd provision` integration, `azd up`) — |
There was a problem hiding this comment.
sorry my naive question - is azd provision and azd up needed? thought azd ai routine create would cover the JTBD
| | --------------- | ---------------- | -------------------------------------------------------------------- | ------ | | ||
| | `recurring` | `schedule` | `--cron "<expr>"`, `--time-zone <tz>` | v1 | | ||
| | `timer` | `timer` | `--at "<ISO 8601>"`, `--time-zone <tz>` | v1 | | ||
| | `github-issue` | `github_issue` | `--connection <id>`, `--owner <o>`, `--repository <r>`, `--event-action <a>` (repeatable) | Deferred — pending workspace connection model | |
There was a problem hiding this comment.
it seems typespec is making this only specific to github, checking with the team
There was a problem hiding this comment.
there is only github-issue strong typed trigger, we plan to add a few other strong typed trigger and a generic event based trigger, coming soon next week. Would this risking azd plan?
There was a problem hiding this comment.
we will update the typespec next monday with the generic and a few more strong typed ones
| | Flag | Notes | | ||
| | --------------------- | -------------------------------------------------------------------- | | ||
| | `--async` | Switches to `:dispatchAsync`. Returns `dispatch_id` immediately. | | ||
| | `--input "<text>"` | User-message payload wrapped into `RoutineDispatchPayload`. | |
There was a problem hiding this comment.
this --input ''" can accept json format if customer provides?
lindazqli
left a comment
There was a problem hiding this comment.
Reviewed against TypeSpec PR #43186 (adding routines, now merged into feature/foundry-release). Six issues found — four are blockers before shipping v1:
- [Line 56] TypeSpec PR reference is stale (#42779 → should be #43186).
- [Line 165]
enable/disableshould call the dedicated:enable/:disableroutes that already exist in the TypeSpec, not GET-then-PUT. - [Line 173] No sync
:dispatchroute exists in TypeSpec — onlyPOST :dispatch_async. The "sync by default" contract has no API backing. - [Line 249]
--agent-id/agent_idshould be--agent-name/agent_nameto match the TypeSpec field name. - [Line 201]
--orderbyhas noorderByquery param inListRoutineRunsParameters. - [Line 239] GitHub trigger:
--ownerserializes toownerbut TypeSpec field isassignee;--event-actionhas no TypeSpec counterpart.
| ### In scope | ||
|
|
||
| - The commands listed in [§1](#1-summary). | ||
| - Mapping from CLI flags onto the wire format in [TypeSpec PR #42779](https://github.com/Azure/azure-rest-api-specs/pull/42779). |
There was a problem hiding this comment.
Stale TypeSpec PR reference. This spec cites PR #42779 throughout (§1, §4.8, §7 OQ-3). The routines TypeSpec landed in PR #43186 (now merged into feature/foundry-release). Please update all references to #43186 so readers can cross-check the wire format accurately.
|
|
||
| Dedicated verbs that hide the wire format. Today: GET-then-PUT toggling | ||
| `enabled: true | false`. If the service later adds `:enable` / `:disable` action | ||
| routes, the CLI flips silently — the verb contract does not change. |
There was a problem hiding this comment.
enable/disable routes already exist in TypeSpec. The spec says these use "GET-then-PUT toggling enabled: true | false" and speculates "If the service later adds :enable / :disable action routes, the CLI flips silently." However, TypeSpec PR #43186 already defines these as dedicated operations:
POST /routines/{routine_name}:enable → enableRoutine
POST /routines/{routine_name}:disable → disableRoutine
The CLI should call these directly rather than doing a GET-then-PUT. The GET-then-PUT fallback is unnecessary and creates a TOCTOU race.
|
|
||
| ### 4.6 `routine dispatch <name>` | ||
|
|
||
| Sync by default → `POST /routines/{name}:dispatch`. |
There was a problem hiding this comment.
No sync dispatch route exists in TypeSpec. This line proposes POST /routines/{name}:dispatch as the sync path, but TypeSpec PR #43186 only defines one dispatch operation:
POST /routines/{routine_name}:dispatch_async → dispatchRoutineAsync
There is no synchronous :dispatch route. The "Sync by default" contract described here has no backing API. Either:
- Clarify that
dispatchalways maps to:dispatch_async(both sync and--asyncmodes), or - Track getting a sync
:dispatchroute added to the TypeSpec before shipping this verb.
Also note the route uses an underscore (dispatch_async), not camel case — the routes table in §5.3 should reflect that.
|
|
||
| | CLI `--action` | TypeSpec `type` | Required CLI flags | Optional CLI flags | | ||
| | ----------------------- | -------------------------------- | ----------------------------------------------- | --------------------- | | ||
| | `agent-response` (def.) | `invoke_agent_responses_api` | one of `--agent-id` / `--agent-endpoint-id` | `--conversation-id` | |
There was a problem hiding this comment.
--agent-id maps to the wrong TypeSpec field. The TypeSpec field for the project-scoped agent name is agent_name, not agent_id:
@doc("The project-scoped agent name for responses API dispatch.")
@maxLength(256)
agent_name?: string;The flag should be --agent-name (or the wire key should be documented as agent_name). Using agent_id / --agent-id implies an ID (UUID/numeric), whereas this is a human-readable name used for project-scoped lookup. This also affects the telemetry property name in §6 (agent_id → agent_name or agentName).
| | CLI flag | Query param | | ||
| | ------------- | ------------------ | | ||
| | `--top N` | `maxResults` per page; CLI stops auto-paging once `N` items have been returned | | ||
| | `--orderby` | `orderBy` (repeatable) | |
There was a problem hiding this comment.
--orderby has no backing TypeSpec query parameter. ListRoutineRunsParameters in TypeSpec PR #43186 only spreads CommonPageQueryParameters (which covers pagination) plus the filter query param. There is no orderBy parameter defined. Either remove --orderby from v1 scope or get orderBy added to the TypeSpec before shipping run list.
| | --------------- | ---------------- | -------------------------------------------------------------------- | ------ | | ||
| | `recurring` | `schedule` | `--cron "<expr>"`, `--time-zone <tz>` | v1 | | ||
| | `timer` | `timer` | `--at "<ISO 8601>"`, `--time-zone <tz>` | v1 | | ||
| | `github-issue` | `github_issue` | `--connection <id>`, `--owner <o>`, `--repository <r>`, `--event-action <a>` (repeatable) | Deferred — pending workspace connection model | |
There was a problem hiding this comment.
Field name mismatch on the GitHub issue trigger. The spec maps --owner → GitHub assignee/org filter, but the TypeSpec field is assignee, not owner:
@doc("The GitHub assignee or organization filter that scopes which issues can fire the trigger.")
@maxLength(128)
assignee: string;--owner would serialize to owner on the wire, which the service won't recognize. The flag should be --assignee to match the TypeSpec (or the spec should explicitly document the CLI-to-wire mapping for this field).
Additionally, --event-action has no corresponding field in GitHubIssueOpenedRoutineTrigger — it only has connection_id, assignee, and repository. Since this trigger type is deferred anyway, please remove --event-action from the table or track it as a separate TypeSpec gap.
lindazqli
left a comment
There was a problem hiding this comment.
Correction: the previous review was submitted as REQUEST_CHANGES by mistake — please treat all inline comments as informational notes for discussion, not as blockers. The comments remain valid observations to align the spec with TypeSpec PR #43186.
jongio
left a comment
There was a problem hiding this comment.
Spec follows the extension's existing patterns well for endpoint resolution, flag naming, and prompt/no-prompt behavior. The TypeSpec alignment feedback from existing reviews is the main thing to address before implementation.
One gap: error behavior isn't defined for any of the 9 commands. See inline comment.
| **Not registered in v1.** The data-plane endpoints are not in | ||
| [TypeSpec PR #42779](https://github.com/Azure/azure-rest-api-specs/pull/42779). | ||
| These will be added as a strictly additive change when the APIs land, with no | ||
| churn on already-shipped verbs. |
There was a problem hiding this comment.
Missing from the spec: what does the user see on errors? Some cases the implementer will need to decide on:
createwithout--forceon an existing name: 409 conflict? Structured error with a 'delete and recreate' hint?show/delete/dispatchon a non-existent routine: how does the service 404 surface?update's GET succeeds, then the routine gets deleted before the PUT lands: 404 on the PUT with no context?
The existing design specs in this repo don't have explicit error sections, so this isn't blocking. But since azd routes errors through error_suggestions.yaml and ErrorWithSuggestion, a one-liner like 'errors follow the connection command pattern' would prevent the implementer from having to reverse-engineer the expected error handling approach. The test plan would also benefit from negative cases covering service-side failures (404, 409, auth).
Summary
Adds the design spec for the new
routinecommand subtree (cli/azd/docs/design/ai-routine-design-spec.md). A routine pairs one trigger (when) with one action (what) on a Foundry project, for example "every weekday at 8 AM UTC, invoke the daily report agent", without standing up Logic Apps, Functions, or cron infra.Related issues:
azd ai routine create/update/show/list/delete(plusenable,disable,dispatch, andrunsubcommands) to manage and run Foundry Routines from any directory #8159--fileforroutine create)Key points
azure.ai.agentsextension. Today the commands surface asazd ai agent routine …; they becomeazd ai routine …after the planned extension split (registration only). Noazure.yamlorregistry.jsonimpact, no new persistent state.create,update,show,list,delete,enable,disable,dispatch, andrun list.run showandrun deleteare deferred until the data plane lands them.connection,toolbox, andskill. TheRoutines=V1Previewopt in header is sent on every call.createfails on existing (or upserts with--force).updateis GET then PUT preserving untouched fields, with type switching of--trigger/--actionrejected client side.dispatchis sync by default and streams the agent response;--asyncswitches to the async route.recurring,agent-response,agent-invoke) read naturally and absorb upstream API renames via a single mapping table.