Skip to content

feat: add CLI Agent Orchestrator (CAO) integration - #5

Closed
plauzy wants to merge 4 commits into
mainfrom
feat/cao-agui-integration
Closed

plauzy wants to merge 4 commits into
mainfrom
feat/cao-agui-integration

Conversation

@plauzy

@plauzy plauzy commented Jul 22, 2026 •

Copy link
Copy Markdown
Owner

This pull request was created by @kiro-agent on behalf of @plauzy 👻

Comment with /kiro fix to address specific feedback or /kiro all to address everything.
Learn about Kiro Web


Fixes ag-ui-protocol#2215. Adds awslabs/cli-agent-orchestrator (CAO) as a first-class AG-UI integration rendered in the Dojo. Implements the plan in .kiro/specs/cli-agent-orchestrator-integration/ (spec PR #4).

CAO runs each coding agent (Kiro CLI, Claude Code, Codex, …) as a real OS process in an isolated tmux session and coordinates them supervisor→worker over MCP, exposing an AG-UI-compliant run plane (POST /agui/v1/run — official EventEncoder frames + interrupt outcome and idempotent resume[]), merged in awslabs/cli-agent-orchestrator#458 (pinned edf61cad). This integration is a thin projection of that surface — not a re-implementation.

Why CAO is a different kind of AG-UI source

It sits on the empty axis between multi-CLI orchestrators (real processes, no protocol) and AG-UI frameworks (protocol, no real processes): a real-process runtime, one stream over N heterogeneous CLIs, permission prompts as standard AG-UI interrupts (the flagship), fleet/construct semantics, and metadata-only privacy. See integrations/cli-agent-orchestrator/ARCHITECTURE.md.

What ships

  • TS thin client @ag-ui/cli-agent-orchestrator — class CliAgentOrchestratorAgent extends HttpAgent {} (zero extra wire code). tsdown build, vitest, publint/attw.
  • Keyless example server (mock_cli fleet, no API keys) reusing CAO's merged run_plane_stream, exposing agentic_chat, shared_state, human_in_the_loop, and the flagship interrupt (real permission prompt → RUN_FINISHED outcome={type:"interrupt"} → resume[]), on port 8024 with /health + CORS. Standalone: depends on CAO's [agui] extra pinned to the awslabs commit edf61cad (AG-UI Phase 2 is not yet on PyPI — see note below), so the install is deterministic with no cross-repo drift.
  • Dojo wiring — menu.ts, agents.ts, env.ts (caoUrl), package.json workspace dep, generate-content-json mapper, prep/run-dojo-everything entries. (files.json regenerates from menu.ts via generate-content-json at dojo dev/start.)
  • Shift-left tests — keyless pytest contract suite asserting the wire contract of all four features (incl. interrupt approve and deny resume + the metadata-only privacy boundary); TS unit tests (HttpAgent-subclass + key-parity guard + a docs-canonical-links guard that fails on any non-canonical CAO URL); four Playwright specs; a secret-free dojo-e2e.yml matrix entry.
  • Docs & governance — integrations.mdx, introduction.mdx, CODEOWNERS, config-allowlist.txt.

Dependency pin (important)

The keyless server imports cli_agent_orchestrator.services.agui (run_plane, base, handoff_approval). AG-UI Phase 2 is not yet on PyPI — the latest published release (2.3.0) predates awslabs/cli-agent-orchestrator#458/ag-ui-protocol#485 and ships no services/agui. The example server therefore pins cli-agent-orchestrator[agui] to the exact awslabs commit edf61cad65a8183d37466f2d112d19a365cfca5c (a deterministic, keyless VCS pin — still standalone, not a local checkout; hatchling allow-direct-references enabled). Swap to a plain version constraint once Phase 2 is released to PyPI.

What was actually verified in this change

Run against real dependencies, not mocks (in this environment):

  • Python contract test: 7/7 pass — uv sync installs cli-agent-orchestrator[agui] pinned to awslabs edf61cad and builds the example server; tests/test_server.py runs against the merged run_plane_stream: agentic_chat lifecycle, shared_state STATE_SNAPSHOT+RFC-6902 STATE_DELTA, HITL approval_card generative UI, interrupt outcome → approve/deny resume[], and the privacy boundary (no prompt body on the wire). The server was also booted as a live uvicorn process on :8024 (/health → {"status":"ok"}).
  • TS package: green — scoped pnpm install, built @ag-ui/core|encoder|client, then: typecheck ✓, build ✓, vitest 5/5 ✓ (incl. docs-canonical-links), publint --strict ✓, attw ✓ (all targets).
  • Repo guards — config-allowlist passes; no fork/non-canonical CAO URLs in shipped docs.

Keyless / CI

The mock_cli fleet needs no external keys, so the e2e matrix runs secret-free. With the awslabs-commit pin above, uv sync fetches the AG-UI Phase 2 build and the server boots on :8024, which is what unblocks the keyless dojo / cli-agent-orchestrator Playwright matrix entry.

Honest limitations

  • The Playwright specs were not executed locally in this environment (they need a full next build + browsers); they run in the secret-free CI dojo / cli-agent-orchestrator matrix entry. The authoritative keyless assertion of the CAO contract is the pytest suite (verified above); the Playwright specs are UI-level smoke. Standard Dojo feature frontends assume LLM-backed chat, whereas CAO's keyless fleet emits orchestration-lifecycle frames — so the hosted live multi-agent demo remains a maintainer-side follow-up per [Feature]: Add AWS Labs - CLI Agent Orchestrator (CAO) integration to the Dojo ag-ui-protocol/ag-ui#2215.
  • The publish (pkg-pr-new) CI check fails on this fork because the pkg-pr-new GitHub App is not installed on plauzy/ag-ui; it is environmental (affects every fork PR), not a regression in this change.
  • CODEOWNERS uses @plauzy (the fork/author handle); adjust for the upstream org on merge.
  • Reconciled against the earlier open PR feat: add CLI Agent Orchestrator (CAO) integration ag-ui-protocol/ag-ui#2216 (same intended surface); this supersedes it.

Testing

cd integrations/cli-agent-orchestrator/python/examples && uv run --extra test pytest · cd integrations/cli-agent-orchestrator/typescript && pnpm build && pnpm test && pnpm test:exports

Adds awslabs/cli-agent-orchestrator as a first-class AG-UI integration
rendered in the Dojo, closing ag-ui-protocol#2215.

CAO runs each coding agent as a real OS process in an isolated tmux session
and coordinates them supervisor->worker over MCP, exposing an AG-UI-compliant
run plane (POST /agui/v1/run, official EventEncoder frames + interrupt outcome
and idempotent resume[]) — awslabs/cli-agent-orchestrator#458 (merged).

What ships:
- @ag-ui/cli-agent-orchestrator — thin CliAgentOrchestratorAgent extends
  HttpAgent (zero extra wire code). tsdown build, vitest, publint/attw.
- Keyless example server (mock_cli fleet, no API keys) reusing CAO's merged
  run_plane_stream, exposing agentic_chat, shared_state, human_in_the_loop,
  and the flagship interrupt (real permission prompt -> RUN_FINISHED
  outcome={type:interrupt} -> resume[]) on port 8024 with /health + CORS.
- Dojo wiring: menu.ts, agents.ts, env.ts (caoUrl), package.json workspace
  dep, generate-content-json mapper, prep/run-dojo-everything entries.
- Shift-left: keyless pytest contract test (7 tests) asserting the wire
  contract of all four features incl. interrupt approve/deny + privacy
  boundary; TS unit tests (HttpAgent subclass + key-parity guard +
  docs-canonical-links guard); four Playwright specs; dojo-e2e.yml matrix
  entry (secret-free).
- Docs (integrations.mdx, introduction.mdx), CODEOWNERS, config-allowlist.

Keyless throughout, so the e2e matrix runs without secrets. Docs reference the
canonical github.com/awslabs/cli-agent-orchestrator repo (guarded by a vitest
test). Standalone example server (pinned cli-agent-orchestrator[agui] extra) —
no cross-repo drift.
@github-actions

github-actions Bot commented Jul 22, 2026 •

Copy link
Copy Markdown

Python Preview Packages — Publish Failed

Preview publish failed for commit c139dbb.
See the workflow run for details.

plauzy pushed a commit that referenced this pull request Jul 23, 2026
…andoff

- Section 4: cross-links PR #4 (spec) / #5 (implementation) / #6 (handoff);
  notes the upstream-bound code in #5 stays free of fork PR numbers.
- Section 5: how to execute tasks.md with a heterogeneous CAO fleet
  (supervisor->workers over MCP), gating each task on its fail-closed test,
  answering worker permission prompts through the AG-UI interrupt lifecycle, and
  recording the run via ag-ui-meta-dogfood as PR evidence (dogfood the orchestrator).
plauzy pushed a commit that referenced this pull request Jul 23, 2026
…andoff

- Section 4: cross-links PR #4 (spec) / #5 (implementation) / #6 (handoff);
  notes the upstream-bound code in #5 stays free of fork PR numbers.
- Section 5: how to execute tasks.md with a heterogeneous CAO fleet
  (supervisor->workers over MCP), gating each task on its fail-closed test,
  answering worker permission prompts through the AG-UI interrupt lifecycle, and
  recording the run via ag-ui-meta-dogfood as PR evidence (dogfood the orchestrator).
kiro-agent and others added 2 commits July 23, 2026 21:52
Runs scripts/generate-content-json.ts to add the four cli-agent-orchestrator
feature entries (agentic_chat, shared_state, human_in_the_loop, interrupt) to the
Dojo code viewer — the one generated artifact from the spec's task 4.5, and the
only file in upstream ag-ui-protocol#2216 not previously in this branch. Purely additive.
…Phase 2 commit

The keyless example server imports cli_agent_orchestrator.services.agui
(run_plane, base, handoff_approval), but that module is not in any PyPI
release yet — 2.3.0 predates AG-UI Phase 2 (awslabs/cli-agent-orchestrator
ag-ui-protocol#458/ag-ui-protocol#485) and ships no services/agui. uv sync therefore installed 2.3.0 and
the server failed to import, so pytest could not collect and the keyless
dojo/cli-agent-orchestrator e2e (wait_on tcp:8024) could never bind.

Pin the dep to the exact awslabs commit edf61cad that introduced Phase 2
(a deterministic, keyless, canonical VCS pin — still standalone, not a local
checkout) and enable hatchling allow-direct-references so the example server
builds. Regenerated uv.lock.

Verified: uv sync + pytest 7/7 green; server boots on :8024 with /health ok.
@plauzy

plauzy commented Jul 26, 2026

Copy link
Copy Markdown
Owner Author

Superseded by ag-ui-protocol/ag-ui#2216, where this work now lands as a single consolidated commit on top of upstream/main.

Closing rather than merging: this PR targets the fork's own main, and merging it would diverge the fork's main from upstream/main, complicating every future rebase or force-push of cao-integration-458.

For the record, this branch's CI never produced a signal — all 24 dojo/* jobs and both dotnet jobs terminated at exactly 24h0m1s, GitHub Actions' hard per-job timeout after runner-queue starvation, with zero steps executed. Nothing here was validated or invalidated by those runs.

Branch feat/cao-agui-integration is retained as the record of the pre-consolidation history.

@plauzy plauzy closed this Jul 26, 2026
plauzy pushed a commit that referenced this pull request Jul 30, 2026
…p (crewai 1.x)

CPK-7718 #2: crewai 1.x no longer dispatches bus handlers inline on the
caller's thread — sync handlers (all of ours) run on a ThreadPoolExecutor
worker. Capture the request loop in create_queue (keyed alongside QUEUES) and
route every listener enqueue through a new _enqueue helper that hops back onto
that loop via call_soon_threadsafe; a bare put_nowait from off-loop corrupts
the queue getter-wakeup. Interim fix (CPK-7719/StreamFrame removes the
listener).

CPK-7718 #3: dispatch is now exact-type, not isinstance — audited: every
.on(...) registers on the exact event type we/crewai emit, none on a base
class. Documented.

CPK-7718 #5: flush the event bus at run end (best-effort, off the loop,
gated on the probe — 1.0.0 has no flush) so in-flight off-thread handlers
settle before teardown.

CPK-7718 #8: stamp _agui_queue_key via setattr with an object.__setattr__
fallback so it works whether or not a stricter Pydantic Flow rejects the
undeclared attribute.

CPK-7718 ag-ui-protocol#10: route the per-request flow copy through _copyutil.safe_deepcopy.
plauzy pushed a commit that referenced this pull request Jul 30, 2026
…wn (CPK-7719 review)

Two should-fixes from the CPK-7719 StreamFrame-path review:

#4 contextvar leak + RUN_ERROR-taxonomy escape: astream()/__aiter__() sat
before the try, so a raising astream escaped _run_flow_frame_stream with no
terminal event AND leaked the flow_context token into the caller. Register the
sink and open the stream INSIDE the try (session/sink_token predeclared for the
finally) so any error maps through the existing RUN_ERROR taxonomy and the
token is always reset -- mirroring the legacy path's token-then-finally
discipline.

#5 happy-path cancel-grace: the driver broke on RUN_FINISHED and let the
finally's aclose() cancel crewai's still-finalizing kickoff task on every
happy-path run (verified on the 1.15.7 wheel: session ended is_cancelled=True,
result lost). crewai enqueues its end sentinel only after kickoff_async fully
returns, so drain the terminal tail to natural exhaustion -- bounded by
_CANCEL_GRACE_SECONDS -- letting the run task record its result before aclose()
becomes a no-op.

Tests: raising-astream -> single taxonomy-coded RUN_ERROR + flow_context reset;
tail drained after RUN_FINISHED with no post-terminal wire event; real Flow run
leaves the kickoff task uncancelled with its result intact.
plauzy pushed a commit that referenced this pull request Aug 18, 2026
* docs(tools): document ToolMessage.error in tool result examples

`ToolMessage.error` is documented on the messages concept page and in the
JS, Python, and Ruby SDK references, but two pages omit it -- both pages an
integration author is likely to work from.

- concepts/tools.mdx: the Tool Results example showed only `content` +
  `toolCallId`. Adds a failure example alongside the existing success one,
  reusing the page's confirmAction / tool-123 scenario.
- sdk/kotlin/core/types.mdx: the ToolMessage declaration omitted `error`,
  which the Kotlin SDK does have. Also corrects the name / toolCallId order
  to match the source.

concepts/events.mdx was checked and left alone: ToolCallResultEvent has no
`error` field in either SDK, so its property table is accurate.

Refs ag-ui-protocol#2306, ag-ui-protocol#2226

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

* ci(python): fail the build on a stale uv.lock

Nothing validated Python lockfiles. Every --frozen-lockfile check in CI is pnpm,
which is how three released packages drifted -- ag_ui_adk five releases deep --
without a single red build (ag-ui-protocol#2313 repaired it, ag-ui-protocol#2314 removes the cause).

Adds --locked to the nine `uv sync` steps rather than nine new `uv lock --check`
steps: uv sync --locked asserts the lockfile is already in step with
pyproject.toml and exits 1 if not, so the gate rides along with a command CI
already runs. Measured on the aws-strands package -- with the lock's self-version
knocked back a patch it exits 1 with "The lockfile at `uv.lock` needs to be
updated, but `--locked` was provided", and exits 0 once restored.

Safe to merge as-is: verified that all eight uv-managed packages this workflow
covers currently pass `uv lock --check` against main, so no job goes red on
landing. Note the check is resolution-level, not byte-level -- it tolerates a lock
whose metadata representation is merely older than what today's uv would emit,
and fails only when the lock genuinely disagrees with pyproject. That is the right
sensitivity here: the version drift this exists to catch trips it, and a uv
upgrade alone does not.

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

* fix(release): re-lock uv-managed Python packages after a version bump

uv.lock carries an entry for the package it locks -- the one whose source is
{ editable = "." } -- so editing pyproject.toml alone leaves that entry one
version stale. prepare-release.ts did exactly that, so every release shipped a
stale lock: four consecutive aws-strands releases are each a one-line, one-file
commit, and ag_ui_adk drifted five releases deep before anyone noticed. ag-ui-protocol#2313
repaired the accumulated drift; this stops it recurring.

Runs `uv lock`, which is what the tooling is for. Note it may also flush latent
metadata corrections unrelated to the bump: it rewrites the whole file once it
has any reason to, and what it writes reflects the package metadata in uv's cache
at that moment. Measured -- the same uv 0.9.24 binary added an exceptiongroup
dependency marker on Aug 4 that it had not added on Jul 30 from an identical
starting state, because the cache had refreshed from PyPI in between. Those are
corrections rather than corruption (exceptiongroup genuinely needs
typing-extensions only below 3.13), and the companion `uv lock --check` CI gate
(ag-ui-protocol#2315) keeps them from accumulating: with locks continuously current, a release
bump has nothing extra to flush and its diff stays to the version line.

Packages with no uv.lock (poetry-managed, or unlocked) are skipped; a missing uv
is fatal rather than skipped, since silently publishing a stale lock is the
failure this exists to prevent. uv's stdout is discarded so the script's JSON
summary stays parseable, with stderr passed through for diagnostics.

Testability: repoRoot was pinned to the script's own location, so a non-dry-run
would have edited the real repo and --dry-run (which never writes) was the only
safe mode -- leaving the write path uncovered. It now honours
PREPARE_RELEASE_ROOT, so a test can point config, package files and lockfiles at
a throwaway tree. The added test builds a dependency-free fixture package (so
`uv lock` needs no network), seeds its lock with a real uv run rather than
hand-writing one, bumps it through the actual script, and asserts pyproject.toml
and uv.lock both land on the new version. Commenting out the re-lock makes it
fail with "uv.lock self-entry not re-locked", so it guards the regression rather
than the implementation.

The test skips itself when uv is absent, so test-release-scripts.yml gains the uv
install -- otherwise it would pass vacuously in CI and give false confidence.

Rejected: a main-guard refactor to make the script importable. A guard whose
condition silently mismatched would stop main() running at all -- a quiet no-op in
release tooling, worse than the bug being fixed. The env override achieves the
same testability with no effect on normal invocation.

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

* chore(deps): update github/codeql-action action to v4.37.6

* fix(repo): reconcile Git LFS policy with repository contents

`git lfs fsck --pointers HEAD` reported 34 files committed as ordinary Git
blobs on paths that .gitattributes declares LFS-governed, and every clone
printed a 34-line "should have been pointers, but weren't" notice.

PNI-185 proposed converting those 34 files to pointers. Investigating each
one showed the policy, not the contents, is what is wrong:

  * docs/** (9 files, 7.4 MB) is served straight from a repository checkout
    by Mintlify, which does not hydrate Git LFS -- an open, unanswered
    feature request since March 2026. Converting these would serve 130-byte
    pointer text in place of every docs image and the landing-page video.

  * apps/dojo/public|src (14 files, 3.1 MB) is served the same way by Render.

  * The Kotlin example's launcher icons (10 files) are Android Studio
    template output at 1-8 KB each -- smaller than the overhead LFS adds.

  * config_test.dill (10.3 MB, 48% of the total) is a Dart kernel snapshot:
    compiler output committed by accident in ag-ui-protocol#316, referenced by nothing.
    It escaped .gitignore only because the compiler wrote it beside its
    source rather than into .dart_tool/ or build/.

Converting would also not have shrunk the repository. A forward-only change
leaves the original blobs in history, so full clones keep carrying all
21.3 MB and would additionally fetch the LFS objects on every hydrating
clone -- against ~95k clones/month, purely additive bandwidth.

So: exempt the four path groups, delete the build artifact, and drop the now
dead *.dill LFS rule. No file is converted and no LFS object is uploaded.

Adds .github/scripts/check-lfs-pointers.sh, which derives the governed set
from `git check-attr` rather than any filename list, and fails in both
directions -- a plain blob on a governed path, and a stranded pointer left
behind by a future exemption. Verified: 34 violations on the previous main,
zero here; fails on a plain blob added to a governed path and passes once
the same file is re-added through LFS.

Refs PNI-185

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

* chore(ci): name the LFS pointer check job

Clears zizmor/anonymous-definition (note severity) on the job added by the
previous commit.

Only the new job is named. The two pre-existing jobs in this file trip the
same rule, but renaming them would change their reported check context, and
`main` is protected with required checks that are not readable without admin
access -- if either is required by name, the rename would leave the context
permanently pending.

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

* docs: document chat platforms as an AG-UI client surface

The Clients listings covered web, terminal, and React Native, which left
the impression that AG-UI targets web applications. Add chat platforms
(Slack, Microsoft Teams) alongside the existing entries, and state that a
client need not be a web app because the protocol describes an event
stream rather than a rendering target.

The added text separates the layers: a chat-platform client is something
anyone can build against AG-UI, the Channels SDK is one implementation,
OpenTag is a readable application built on it, and CopilotKit
Intelligence is named as a hosted service outside the protocol.

No protocol, specification, or event-set changes.

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

* fix(client): make TOOL_CALL_START idempotent

The TOOL_CALL_START handler appended to the parent assistant message's
`toolCalls` with no check for an existing entry, so any re-application of
a start event corrupted message state. The same start can reach the
reducer twice — a tool call already carried in `agent.messages` from an
earlier run and then replayed by the backend (the HITL path does this
when the run re-syncs after `respond()`), or one stream re-delivered over
two transports.

The duplicate's `arguments` stay empty, because TOOL_CALL_ARGS deltas
resolve to the first match. That malformed assistant message — the same
tool call twice, one copy with no arguments — is what travels back to the
provider on the next turn.

Resolve any existing entry for the id and update it in place instead of
pushing a second one, leaving `arguments` untouched (a start event carries
none, so the copy already in state holds the only streamed args). A start
that reuses an id under a different name warns and updates the name.

The check runs before resolveOrCreateAssistantMessage, which also closes a
second latent defect on the same path: a replay whose parentMessageId is
no longer in state would otherwise create a stray empty assistant message
to hang the duplicate off. A replay is now a true no-op — no duplicate, no
stray message, and no state emission at all.

Note that TOOL_CALL_ARGS remains non-idempotent by nature (deltas append).
The evidence in CPK-7770 shows only the start event was replayed, so this
covers the observed defect; whole-stream re-delivery would surface next as
doubled arguments, which cannot be deduped without sequence numbers.

Refs CPK-7772, follow-up to CPK-7770 / CopilotKit#6407

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

* chore(deps): update swatinem/rust-cache action to v2.9.2

---------

Co-authored-by: Mark Fogle <mark@copilotkit.ai>
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Markus Ecker <markus.ecker@gmail.com>
Co-authored-by: Ben Taylor <ben@copilotkit.ai>
plauzy pushed a commit that referenced this pull request Sep 18, 2026
test(spring-server-core): lock in protocol-required reasoning event wire shape
plauzy added a commit that referenced this pull request Sep 21, 2026
Verified every cross-reference in the spec against GitHub and fixed the
ones that no longer hold:

- Fork PRs: #4 was labelled "this spec", but #4 (spec/cao-agui-integration)
  is closed and this spec is now #6 (spec/cao-agui-handoff). Point at #6 and
  keep #4/#5 as explicitly-closed predecessors for provenance.
- The worker prompt and honesty guardrail referred to "PR #5" as the live
  implementation; #5 is closed, so refer to the feat/cao-agui-integration
  branch (which still exists on the fork) instead.
- "PRs ag-ui-protocol#485/ag-ui-protocol#458" was inaccurate: ag-ui-protocol#485 is the merged PR, ag-ui-protocol#458 is the
  closed tracking issue. Attribute each correctly.
- Noted that ag-ui-protocol#2215 and ag-ui-protocol#2216 are both still open
  upstream, which the Closes/supersedes wording depends on.

Left unchanged because still accurate: awslabs PR ag-ui-protocol#436 (merged), issue
ag-ui-protocol#386, and the upstream ag-ui-protocol#2215/ag-ui-protocol#2216 references.

Co-authored-by: plauzy <4451274+plauzy@users.noreply.github.com>
Co-authored-by: Kiro Agent <244629292+kiro-agent@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature]: Add AWS Labs - CLI Agent Orchestrator (CAO) integration to the Dojo

2 participants