Commit Graph

6005 Commits

Author SHA1 Message Date
Shailesh Padave 51efdfcad8 test(a2a): add unit tests for A2ANativeAgentFacade and A2AAgentServerResource
Mirrors the existing A2AWorkflowAgentTest and A2AServerResourceTest patterns
for the new native-agent A2A endpoints at /api/a2a/agent.
2026-07-28 14:09:24 +05:30
Shailesh Padave 1081bab737 spotless apply 2026-07-28 13:48:30 +05:30
Shailesh Padave a3a6198a8a feat(a2a): expose native Conductor agents as A2A agents at /api/a2a/agent
Adds A2ANativeAgentFacade and A2AAgentServerResource in the agentspan module
to mirror the workflow-side A2A server at /api/a2a/agent/{name}. Native agents
are listed via AgentService, started via AgentService.start(), and execution
tracking reuses the same workflow-state mapping as the workflow side.

Closes #1407
2026-07-28 12:49:27 +05:30
Shailesh Padave eb3832bb04 refactor(a2a): move workflow agent endpoints to /api/a2a/workflow
Renames the default A2A server base path from /a2a to /api/a2a/workflow
to align with Conductor's /api prefix convention and reserve /api/a2a/agent
for future native-agent exposure.
2026-07-28 12:14:49 +05:30
Viren Baraiya ece518f69b Merge branch 'main' into feature/a2a-expose-all 2026-07-27 22:36:16 -07:00
Miguel Prieto e4898a62a6 Fix #1321: reserve an async system task's queue message while it runs (#1369) 2026-07-27 22:33:57 -07:00
Miguel Prieto bf19b22b43 ci: publish named test-result checks for unit-test, test-harness and e2e (#1404)
* ci(test-harness): publish a named test-result summary check

The test-harness job passed only report_paths to mikepenz/action-junit-report, so
a run gave no summary of what actually ran — a failure showed up as Gradle's
"There were failing tests" and an exit code, with the counts only reachable by
downloading the report artifact.

Configures the action to surface a "Test-Harness tests" check reading
"N tests run, N passed, N skipped, N failed":

- check_name so the result is its own check rather than folded into the job
- detailed_summary for the per-suite breakdown
- check_title_template for readable annotation titles
- check_retries and flaky_summary to distinguish retried/flaky tests
- include_passed=false to keep the annotation volume down

fail_on_failure is intentionally not set: the Gradle step already fails the job,
so adding it would only mark a second step red.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* ci(test-harness): publish a named test-result summary check

The test-harness job's junit-report step parsed results correctly but could not
publish them:

  JUnit Test Report - 338 tests run, 306 passed, 31 skipped, 1 failed.
  Failed to create checks using the provided token.
  (HttpError: Resource not accessible by integration)

ci.yml declares no permissions, so the job ran with the default read-only token
and the check run was rejected. fail_on_failure defaults to false, so the step
still went green and the failure was invisible — leaving Gradle's exit code as
the only signal of which test failed.

Grants the job checks: write, and names the check plus enables the per-suite
summary so the result is visible without opening the log.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* ci: publish named test-result checks for unit-test, test-harness and e2e

The junit-report steps parsed results correctly but could not publish them:

  Unit Test Report - 2802 tests run, 2713 passed, 89 skipped, 0 failed.
  Failed to create checks using the provided token.
  (HttpError: Resource not accessible by integration)

ci.yml declared no permissions, so these jobs ran with the default read-only
token and every check run was rejected. fail_on_failure defaults to false, so the
steps still went green and the failure was invisible — leaving Gradle's exit code
as the only signal of which test failed.

Grants checks: write to the three jobs that run tests, and names each check plus
enables the per-suite summary. e2e is a matrix, so its check name carries
matrix.name to keep one check per database combination instead of the legs
overwriting each other.

The build job is left alone: it runs with -x test and has no results to publish.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-27 15:33:48 -07:00
Patrick Ropp b479d83a9e Merge pull request #1396 from conductor-oss/harness/issue-1393
CI / detect-changes (push) Has been cancelled
CI / build (push) Has been cancelled
CI / test-harness (push) Has been cancelled
CI / agentspan-e2e (push) Has been cancelled
CI / generate-e2e-matrix (push) Has been cancelled
Publish docs via GitHub Pages / Deploy docs (push) Has been cancelled
Release Drafter / update_release_draft (push) Has been cancelled
CI / unit-test (push) Has been cancelled
CI / e2e (push) Has been cancelled
CI / build-ui (push) Has been cancelled
Resolve #1393: SchedulerClient.pauseSchedule() fails: 'Request method GET is not supported' (500) — GET-then-PUT fallback mishandles server response
2026-07-24 17:33:13 -04:00
Patrick Ropp 0aa66f7b19 Fixing CI 2026-07-24 16:50:57 -04:00
Patrick Ropp 0a69a19aac Revert "approved design docs (docs/design)"
This reverts commit 38840b5b33.
2026-07-24 16:34:07 -04:00
Patrick Ropp de0f928c6e code_subtask map-method-not-supported-405 2026-07-24 16:31:33 -04:00
Patrick Ropp 38840b5b33 approved design docs (docs/design) 2026-07-24 16:29:10 -04:00
Ling-Sen Peng 3a593c4439 test(ai): let Anthropic budget failures fail integration tests 2026-07-24 09:54:33 -07:00
Ling-Sen Peng e8584b731d test(ai): skip Anthropic media test on usage cap 2026-07-23 13:56:21 -07:00
ling-senpeng13 113ea16256 Merge branch 'main' into test/llm-media-integration 2026-07-23 12:55:13 -07:00
Kowser 0acae503f3 Fix mysql boot crashes #1387
Publish docs via GitHub Pages / Deploy docs (push) Has been cancelled
Release Drafter / update_release_draft (push) Has been cancelled
CI / detect-changes (push) Has been cancelled
CI / build (push) Has been cancelled
CI / test-harness (push) Has been cancelled
CI / agentspan-e2e (push) Has been cancelled
CI / generate-e2e-matrix (push) Has been cancelled
CI / unit-test (push) Has been cancelled
CI / e2e (push) Has been cancelled
CI / build-ui (push) Has been cancelled
2026-07-23 22:21:03 +06:00
Kowser 4d941c7982 fix(persistence): baseline-0 on main flyway so boot survives scheduler-first migration
- mirrors postgres scheduler config (@Qualifier postgresRetryTemplate)
- smoke-test fixture gets a mysqlRetryTemplate bean alongside postgresRetryTemplate
- scheduler flyway shares the schema (own history table, baselineVersion 0); main flyway
  must tolerate a non-empty schema the same way or boot depends on bean-init order
- postgres: baselineVersion 0 (default 1 would silently skip V1__initial_schema if second)
- sqlite: baselineOnMigrate + baselineVersion 0 (had neither)
- trade-off: legacy DBs w/ tables but no history now fail on V1 "already exists" instead of
  the baseline hint; same operator remedy

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-22 18:12:54 -07:00
Ling-Sen Peng 8debac0e96 ci: trigger media tests on provider adapter changes; add concurrency guard
PRs touching ai/.../providers/** now run the live media suite (the exact
changes these tests exist to catch), while everything else in the repo
still never triggers a paid run. Concurrency group cancels superseded
runs on rapid pushes so spend cannot stack.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-22 14:11:38 -07:00
Ling-Sen Peng 20205dd68c fix(test): align Grok key env alias; refresh stale live-test model ids
- conductor.ai.grok.api-key now falls back XAI_API_KEY -> GROK_API_KEY,
  so the org secret (GROK_API_KEY, which the tests already gate on) also
  configures the server-side provider; XAI_API_KEY still wins when set.
- BedrockTest.IntegrationTests chat test: legacy bare claude-3-haiku id
  -> current us. Haiku 4.5 inference profile (same two failures the
  media test hit live: profile required, Legacy model blocked).
- GeminiVertexTest Vertex chat test: retired gemini-1.5-flash ->
  gemini-2.5-flash.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-22 13:37:31 -07:00
Ling-Sen Peng 6500924f2d fix(test): use current claude-haiku-4-5 inference profile for Bedrock media test
claude-3-haiku is marked Legacy by the provider and Bedrock rejects it
after 30 days of inactivity ("Access denied. This Model is marked by
provider as Legacy"). Use the active Haiku 4.5 US inference profile.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-22 13:34:23 -07:00
Ling-Sen Peng 588471fb44 fix(test): use Bedrock inference-profile model id in media test
The bearer-token auth works, but Bedrock rejects the bare model id for
on-demand throughput ("Retry your request with the ID or ARN of an
inference profile") — use the us. cross-region profile id.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-22 13:26:34 -07:00
Ling-Sen Peng 597e49838b test(ai): add OpenAI, Azure OpenAI, and Bedrock media integration tests
- OpenAITest / AzureOpenAITest: testChatCompletionWithImageMedia on
  gpt-4o-mini — both ride OpenAIResponsesChatModel's input_image path
  (pre-existing support; this locks it live at each endpoint).
- BedrockTest: new BearerIntegrationTests nest gated on
  AWS_BEARER_TOKEN_BEDROCK (the dedicated org secret, matching the
  server's conductor.ai.bedrock.bearerToken binding) — kept separate
  from the access-key nest because the CI AWS keys are provisioned for
  artifact publishing and may lack Bedrock permissions. Media flows via
  Spring AI Converse image blocks; model claude-3-haiku (vision-capable,
  same id as the existing bedrock tests).
- Workflow wires the new secrets (OPENAI, AZURE_OPENAI key+endpoint,
  AWS_BEARER_TOKEN_BEDROCK, AWS_REGION).

Verified locally: Anthropic and OpenAI pass against the live APIs;
keyless providers skip. Azure/Bedrock get their first live run on this
PR's workflow run.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-22 13:18:43 -07:00
Ling-Sen Peng fd67c4fae4 test(ai): live media-input integration tests per provider + CI workflow
Adds testChatCompletionWithImageMedia to the key-gated IntegrationTests
nests of Anthropic (#1238), Grok and Perplexity (#1243), and a new
GEMINI_API_KEY-gated ApiKeyIntegrationTests nest for Gemini (#1241,
AI-Studio mode — the mode the server uses with conductor.ai.gemini.api-key),
complementing the existing Cohere test (#1246). Each sends the repo's
token image (MELON7391) to a vision model and asserts transcription —
a pass proves the adapter attached the media; the token cannot be guessed.

Vision models verified against provider docs: claude-haiku-4-5,
gemini-2.5-flash, grok-4.5 (docs.x.ai image understanding), sonar
(Perplexity image attachments, data-URI image_url).

New workflow llm-media-integration.yml runs the suite on GitHub with the
org-level provider secrets (weekly cron + workflow_dispatch + PR runs on
workflow changes). Tests self-skip per absent key (COHERE_API_KEY is not
configured today), so the run stays green with per-provider status.

Verified locally: Anthropic passes against the live API; keyless
providers skip. Full gemini/grok/perplexity verification happens on the
PR's own workflow run via org secrets.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-22 13:00:31 -07:00
Andrew Lai (AJ) 54f8369fa8 https://github.com/conductor-oss/conductor/issues/1315 (#1366)
CI / detect-changes (push) Has been cancelled
CI / build (push) Has been cancelled
CI / unit-test (push) Has been cancelled
CI / test-harness (push) Has been cancelled
CI / agentspan-e2e (push) Has been cancelled
CI / generate-e2e-matrix (push) Has been cancelled
CI / e2e (push) Has been cancelled
CI / build-ui (push) Has been cancelled
Publish docs via GitHub Pages / Deploy docs (push) Has been cancelled
Release Drafter / update_release_draft (push) Has been cancelled
UI v2 CI / Lint, Format & Test (push) Has been cancelled
UI v2 CI / E2E (Mocked) (push) Has been cancelled
UI v2 Integration CI / E2E (Integration) (push) Has been cancelled
2026-07-21 15:04:45 -07:00
Kowser 4ba6f76fa6 Guard pruneExecutions against overflow and non-positive age deleting recent executions #1360 v3.32.0-rc.15 2026-07-22 02:53:59 +06:00
Nikhil Bharadwaj Ramashasthri a96c77fd4a Guard pruneExecutions against overflow and non-positive age deleting recent executions
The cutoff was computed as Instant.now().minus(olderThanDays, DAYS)
with no bounds check. Very large values push the epoch negative, and
the search backend matched recent executions against the negative
bound, hard-deleting them. Non-positive values put the cutoff in the
future, matching every terminal execution. Reject olderThanDays < 1
and clamp the cutoff to epoch start, where it correctly matches
nothing. The cutoff computation is extracted into a VisibleForTesting
helper with an injectable clock for deterministic boundary tests.

Fixes #1331
2026-07-22 02:40:27 +06:00
Kowser bd5eb19289 fix(guardrail): guardrail retry→raise escalation never fires (tool + agent level) #1356 2026-07-22 02:35:09 +06:00
Kowser 6994047ad6 test(guardrail): prove the tool-call short-circuit and escalation compose correctly
Context: #1352 binds a toolCalls short-circuit into the same output-guardrail
  scripts this branch's escalation fix touches
- neither test suite covered the combination: tool-call turn +
  iteration already >= max_retries

Adds 2 tests to GuardrailEscalationScriptTest:
- tool-call turn (non-empty toolCalls) + iteration >= max_retries ->
  must still pass
  - short-circuit must win over escalate()
  - model is mid-tool-use, never a raise candidate
- empty toolCalls array (real final turn) -> must NOT short-circuit,
  escalation still fires

Matches the script ordering: toolCalls check runs before escalate().

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-21 09:25:59 -07:00
Kowser 4b28f10f3e fix(guardrail): tool-guardrail iteration ref + escalation script test
Bug: compileToolGuardrailTasks fed every tool guardrail type
(custom/regex/llm) a constant iteration of "1", not the live DoWhile
counter.
- SDK worker / JS scripts check iteration >= maxRetries
- constant "1" -> onFail=RETRY never escalates
- loop runs to maxTurns, workflow completes instead of failing

Fix: pass ${<agent>_loop.output.iteration} instead of "1"
- same live-counter expression as the agent-level path (previous commit)
- one line fixes all guardrail types (fans out to custom SIMPLE task,
  regex/llm scripts, normalizer)

Tests added:
- GuardrailCompilerTest: compiler wiring, live ref reaches all 3
  guardrail types
- GuardrailEscalationScriptTest (new): executes
  customGuardrailNormalizeScript() via real GraalJS
  - same evaluator as production
  - proves escalation logic itself (retry->raise, fix->raise only if
    no fixed_output, tripwire-style raw output)
  - no LLM, no workflow executor needed

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-21 09:24:57 -07:00
Kowser e4cc312c60 fix(guardrail): live loop counter
Fixes:
- live loop counter must be ${<loop>.output.iteration}, not bare ${<loop>.iteration}
  - bare form resolves to null mid-loop
  - touches: GuardrailCompiler (agent-level output guardrails), TerminationCompiler
    (4 sites), MultiAgentCompiler round-robin selector
- customGuardrailNormalizeScript never escalated retry/fix -> raise
  - wires iteration + max_retries into the normalize INLINE
  - adds escalate() coercion (same as regex/llm scripts already had)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-21 00:43:21 -07:00
Shailesh Padave 430c1b1a34 fix(a2a): initialize agentClients map lazily to avoid Spring circular dependency
@PostConstruct ran too early: ServiceConductorAgentClient could not initialize
because its chain (AgentService → WorkflowExecutorOps → SystemTaskRegistry
@DependsOn workerTaskAnnotationScanner → A2AWorkers) creates a circular
initialization dependency with A2AWorkers still in creation.

Replace @PostConstruct registerAgentClients() with a clients() accessor that
performs a double-checked lazy load on first AGENT/CANCEL_AGENT task execution,
at which point all Spring beans are fully initialized.
2026-07-21 11:05:40 +05:30
Shailesh Padave 59c7ac221d Merge branch 'main' into feature/external-agent-clients 2026-07-21 10:48:32 +05:30
Shailesh Padave 77b09397da fix(test-harness): add agentspan.embedded=true to ConductorAgentEndToEndTest
ServiceConductorAgentClient is gated on @ConditionalOnProperty(agentspan.embedded=true).
Without this flag the bean is absent, A2AWorkers#agentClients map has no 'conductor' entry,
and all agentType=conductor tasks fall through to executeRemote() → FAILED_WITH_TERMINAL_ERROR.
2026-07-21 10:16:06 +05:30
Nikhil Ramashasthri 1f4c17a62a Validate SWITCH javascript expressions by syntax only at registration (#1361)
* Validate SWITCH javascript expressions by syntax only at registration

validateScriptExpression executed the expression via ScriptEvaluator.eval
with inputParameters as bindings - but at registration time those still
hold unresolved ${...} placeholders, so any expression operating on
runtime-bound values (e.g. calling array methods on a value that is a
placeholder string until resolution) threw and wrongly rejected valid
definitions, making the javascript evaluator unusable for dynamic
dispatch. Add ScriptEvaluator.validateScriptSyntax which parses the
source without executing (Context.parse) and rejects only genuine
syntax errors.

Fixes #1311

* Update MetadataServiceTest to a genuine syntax error

1>abcd is syntactically valid javascript that only fails at evaluation,
which registration no longer performs. Use a true syntax error so the
test keeps guarding rejection of malformed expressions.
2026-07-20 20:48:39 -07:00
Nicholas Cole 5e22ec3740 Fix #1323: output guardrails must pass through on tool-call turns (#1352) v3.32.0-rc.14 2026-07-20 16:40:01 -07:00
Shailesh Padave 40cf12294e fix(agent): accept agentName as alias for name in conductor agent task
A2ACallRequest documents agentName as the field for specifying a
conductor agent, but ConductorAgentDelegate re-parses task input as
AgentStartRequest which only has name. Adding @JsonAlias("agentName")
lets both field names work, preventing silent null and the misleading
"AGENT (conductor) requires 'name'" error.
2026-07-20 23:35:05 +05:30
Shailesh Padave 3d414de0b5 feat(a2a): add expose-all flag to auto-expose all workflows as A2A agents
Add conductor.a2a.server.expose-all=true property so every registered
workflow is automatically available as an A2A agent without requiring
per-workflow metadata (a2a.enabled=true) or explicit exposedWorkflows
config. Useful for development and single-tenant deployments.
2026-07-20 23:34:45 +05:30
Shailesh Padave 04d7afb272 spotless apply 2026-07-20 20:42:52 +05:30
Shailesh Padave 492e628bb6 spotless apply 2026-07-20 20:37:07 +05:30
Shailesh Padave 558fb763df Add placeholder ConductorAgentClient implementations for Vertex AI and OpenAI Assistants
Both classes are intentionally not annotated with @Component so they do not
auto-register until implemented. Each documents the expected rawConfig keys,
auth approach, and API mapping for the eventual implementor.
2026-07-20 20:00:46 +05:30
Shailesh Padave 4a391d8c18 Fix ambiguous ConductorAgentClient autowiring in test-harness
Adding BedrockAgentClient and AzureFoundryAgentClient as @Component beans
created three candidates for the unqualified @Autowired field. Qualify it
to serviceConductorAgentClient, which is the in-process implementation the
test is designed to exercise.
2026-07-20 19:54:19 +05:30
Shailesh Padave 99fea7b03c spotless apply 2026-07-20 19:30:45 +05:30
Shailesh Padave 83d6b330bf Fix test compilation errors after A2AWorkers and ConductorAgentClient changes
- Add agentType() to UnusedConductorAgentClient and FakeConductorAgentClient
  (required by the new abstract method on the interface)
- Wrap single ConductorAgentClient args in List.of() in all test constructors
  (non-Spring A2AWorkers constructor now takes List<ConductorAgentClient>)
- Stub conductorAgentClient.agentType() to "conductor" in A2ACancelWorkerTest
  so the mock is registered under the expected routing key
2026-07-20 19:12:27 +05:30
Shailesh Padave d7dee74d8a spotless apply 2026-07-20 19:01:19 +05:30
Shailesh Padave 4fe55b3c8f fix: remove name-required guard from ConductorAgentDelegate for external agents
The 'name' field is conductor-internal (identifies which embedded agent
workflow to call). External agents (Azure Foundry, Bedrock) don't use it
and would fail unnecessarily. Each client's startAgent() already validates
its own required fields. Kept the prompt check which is universal.
2026-07-20 17:44:11 +05:30
Shailesh Padave 40406974f1 fix: break circular dependency in A2AWorkers Spring wiring
The constructor injection of List<ConductorAgentClient> caused a cycle:
A2AWorkers → ServiceConductorAgentClient → AgentService →
WorkflowServiceImpl → WorkflowExecutorOps → A2AWorkers.

Fix: Spring constructor now takes ApplicationContext instead of the
client list. A @PostConstruct method populates the agentClients map via
ApplicationContext.getBeansOfType() after all beans are constructed.
Non-Spring constructor (SDK/tests) is unchanged and still accepts a list.
2026-07-20 17:34:29 +05:30
Shailesh Padave 2f27b8fbda refactor(azure-foundry): rewrite client to use Azure AI Foundry Agents API
Replace the placeholder A2A implementation with the real Azure AI Foundry
Agents REST API (OpenAI Assistants-compatible). The client now:
- POST /threads → POST /threads/{id}/messages → POST /threads/{id}/runs
  to start an agent and return thread_id as executionId
- GET /threads/{id}/runs/{runId} to poll status
- Maps Azure states: completed→COMPLETED, requires_action→WAITING,
  failed/expired→FAILED, queued/in_progress→RUNNING
- GET /threads/{id}/messages on completion to extract the response text
- POST /threads/{id}/runs/{runId}/submit_tool_outputs for tool results
- POST /threads/{id}/runs/{runId}/cancel on cancellation
- rawConfig.assistantId (or agentId) selects which assistant to use
2026-07-20 17:16:53 +05:30
Shailesh Padave 423b8c783f fix: correct SdkBytes API usage in BedrockAgentClient and remove stale imports
SdkBytes.bytes() does not exist; use asUtf8String() directly on the
SdkBytes returned by PayloadPart.bytes(). Also removed the leftover
ConditionalOnProperty imports from BedrockAgentClient and
AzureFoundryAgentClient (annotations were removed earlier).
2026-07-20 17:03:39 +05:30
Shailesh Padave 5ca6ad9581 Route external agent clients by agentType — no feature flags needed
Add agentType() to ConductorAgentClient interface so each implementation
self-declares its routing key. A2AWorkers now takes List<ConductorAgentClient>,
builds a map at startup, and routes by agentType on the request — no
conditional logic, no @ConditionalOnProperty flags.

- conductor → ServiceConductorAgentClient (existing)
- bedrock   → BedrockAgentClient (new)
- azure-foundry → AzureFoundryAgentClient (new)
- anything else → A2A remote path (existing fallback)

Adding a new platform is now: implement ConductorAgentClient, declare
agentType(), register as a Spring bean — nothing else changes.
2026-07-20 15:58:12 +05:30
Shailesh Padave 9e8b2f5430 Add external agent client infrastructure for Bedrock and Azure Foundry
- TokenProvider interface + StaticTokenProvider + OAuthTokenProvider (with
  Entra ID factory, thread-safe token cache with 60s refresh buffer)
- AgentCredentialConfig model with Platform enum for extensibility
- credentialRef field on ConductorAgentStartRequest so callers can point
  to a named secret without embedding credentials in the request
- BedrockAgentClient: streams Bedrock Agent Runtime responses, buffers
  output, surfaces returnControl events as WAITING state; falls back to
  default AWS credential chain when no credentialRef is set
- AzureFoundryAgentClient: A2A over HTTP with Entra ID bearer token auth;
  per-execution state map so getAgentStatus/respond/cancelAgent all have
  endpoint context without callers needing to pass it again
- KnownProviderEnvVars: added AZURE_CLIENT_ID/SECRET/TENANT_ID,
  AZURE_FOUNDRY_ENDPOINT, BEDROCK_AGENT_ID/ALIAS_ID for auto-seeding
2026-07-20 15:37:15 +05:30