Repository navigation
Conversation
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.
Python Preview Packages — Publish FailedPreview publish failed for commit c139dbb. |
…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).
…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).
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.
|
Superseded by ag-ui-protocol/ag-ui#2216, where this work now lands as a single consolidated commit on top of Closing rather than merging: this PR targets the fork's own For the record, this branch's CI never produced a signal — all 24 Branch |
…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.
…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.
* 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>
test(spring-server-core): lock in protocol-required reasoning event wire shape
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>
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— officialEventEncoderframes + interrupt outcome and idempotentresume[]), merged in awslabs/cli-agent-orchestrator#458 (pinnededf61cad). 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
@ag-ui/cli-agent-orchestrator—class CliAgentOrchestratorAgent extends HttpAgent {}(zero extra wire code).tsdownbuild,vitest,publint/attw.mock_clifleet, no API keys) reusing CAO's mergedrun_plane_stream, exposingagentic_chat,shared_state,human_in_the_loop, and the flagshipinterrupt(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 commitedf61cad(AG-UI Phase 2 is not yet on PyPI — see note below), so the install is deterministic with no cross-repo drift.menu.ts,agents.ts,env.ts(caoUrl),package.jsonworkspace dep,generate-content-jsonmapper,prep/run-dojo-everythingentries. (files.jsonregenerates frommenu.tsviagenerate-content-jsonat dojodev/start.)pytestcontract 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 + adocs-canonical-linksguard that fails on any non-canonical CAO URL); four Playwright specs; a secret-freedojo-e2e.ymlmatrix entry.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 noservices/agui. The example server therefore pinscli-agent-orchestrator[agui]to the exact awslabs commitedf61cad65a8183d37466f2d112d19a365cfca5c(a deterministic, keyless VCS pin — still standalone, not a local checkout;hatchling allow-direct-referencesenabled). 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):
uv syncinstallscli-agent-orchestrator[agui]pinned to awslabsedf61cadand builds the example server;tests/test_server.pyruns against the mergedrun_plane_stream: agentic_chat lifecycle, shared_stateSTATE_SNAPSHOT+RFC-6902STATE_DELTA, HITLapproval_cardgenerative UI, interrupt outcome → approve/denyresume[], 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"}).pnpm install, built@ag-ui/core|encoder|client, then:typecheck✓,build✓,vitest5/5 ✓ (incl.docs-canonical-links),publint --strict✓,attw✓ (all targets).Keyless / CI
The
mock_clifleet needs no external keys, so the e2e matrix runs secret-free. With the awslabs-commit pin above,uv syncfetches the AG-UI Phase 2 build and the server boots on:8024, which is what unblocks the keylessdojo / cli-agent-orchestratorPlaywright matrix entry.Honest limitations
nextbuild + browsers); they run in the secret-free CIdojo / cli-agent-orchestratormatrix 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.publish(pkg-pr-new) CI check fails on this fork because the pkg-pr-new GitHub App is not installed onplauzy/ag-ui; it is environmental (affects every fork PR), not a regression in this change.@plauzy(the fork/author handle); adjust for the upstream org on merge.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