Skip to content
Closed
Show file tree
Hide file tree
Changes from 94 commits
Commits
Show all changes
95 commits
Select commit Hold shift + click to select a range
9af434e
Initial plan
Copilot Apr 3, 2026
2bf9df6
Add Devin CLI provider with tests and registrations in all required l…
Copilot Apr 3, 2026
bc0eee8
Address code review: narrow exception handling and add test docstring
Copilot Apr 3, 2026
ed22b1f
Fix: Markdown heading collision in prompt detection, enforce allowed_…
Copilot Apr 4, 2026
791679e
Fix: remove timeout tests that have mocking issues
ThePlenkov Jun 23, 2026
a7dabdf
Fix: add MCP profile merge and E2E tests for Devin CLI provider
ThePlenkov Jun 28, 2026
d43a433
Add Devin CLI to web UI provider list
ThePlenkov Jun 28, 2026
fbfef95
Add Playwright E2E tests for Devin CLI web UI integration
ThePlenkov Jun 28, 2026
4dd0b52
fix(devin_cli): add WSL compatibility via history fallback + improve …
ThePlenkov Jun 28, 2026
0b5b8b6
fix: address review comments - temp cleanup, skill prompt, and securi…
ThePlenkov Jun 28, 2026
4318e8f
fix: update tests to expect use_paste_buffer parameter
ThePlenkov Jun 28, 2026
b8cd8e6
fix: replace insecure tempfile.mktemp with NamedTemporaryFile
ThePlenkov Jun 28, 2026
98450f2
fix: apply Black formatting to fix Code Quality CI failure
ThePlenkov Jun 28, 2026
e0d6df3
fix: update test to expect use_paste_buffer parameter
ThePlenkov Jun 28, 2026
a7cfbe5
chore: exclude web/package-lock.json from git tracking
ThePlenkov Jun 28, 2026
8c24047
fix: use npm install instead of npm ci in CI
ThePlenkov Jun 28, 2026
dfe73e1
fix: disable npm cache and remove lockfile in Web UI Build
ThePlenkov Jun 28, 2026
2cc7786
fix: address all SonarCloud findings
ThePlenkov Jun 28, 2026
ecc1fea
fix: address remaining SonarCloud findings
ThePlenkov Jun 28, 2026
dbeedb8
fix: Black formatting and module function call
ThePlenkov Jun 28, 2026
8331e09
fix: remaining SonarCloud issues and Web UI Build
ThePlenkov Jun 28, 2026
56516a4
fix: restrict vitest to src directory only
ThePlenkov Jun 28, 2026
d87ba0d
fix: add sonar-project.properties to suppress false positives
ThePlenkov Jun 28, 2026
d343c41
fix: use CAO_HOME_DIR for FIFO to avoid SonarCloud warning
ThePlenkov Jun 28, 2026
8331037
fix: apply black formatting to constants.py
ThePlenkov Jun 28, 2026
3202323
fix: replace any types with proper types in E2E tests
ThePlenkov Jun 28, 2026
e551e4a
refactor: use factory pattern to reduce complexity in manager.py
ThePlenkov Jun 28, 2026
44344a7
fix: use public npm registry instead of JFrog
ThePlenkov Jun 28, 2026
8fef6ec
fix: simplify npm install to use default registry
ThePlenkov Jun 28, 2026
b48b4ab
fix: remove package-lock.json removal to use lock file
ThePlenkov Jun 28, 2026
9165e6a
revert: keep rm package-lock.json due to local npm issues
ThePlenkov Jun 28, 2026
0c18b96
fix: keep package-lock.json for predictable dependencies
ThePlenkov Jun 28, 2026
2b96fef
chore: add package-lock.json with public registry URLs
ThePlenkov Jun 28, 2026
a7ab2aa
fix: restore original workflow with rm package-lock.json
ThePlenkov Jun 28, 2026
676b908
fix: address all PR review findings for Devin CLI provider
ThePlenkov Jun 28, 2026
7a1c2f3
fix: remove duplicate test method in test_supervisor_orchestration.py
ThePlenkov Jun 28, 2026
8021380
fix: address all cubic review findings
ThePlenkov Jun 28, 2026
087a241
fix: apply black formatting to constants.py and devin_cli.py
ThePlenkov Jun 28, 2026
8625cf7
fix: remove references to non-existent gemini_cli provider
ThePlenkov Jul 1, 2026
5defd05
fix: address review comments - status monitor, FIFO dir, E2E tests, d…
ThePlenkov Jul 1, 2026
6589d7c
fix: address additional review comments - unused fixture, UTF-8 encod…
ThePlenkov Jul 1, 2026
3c60c84
fix: route devin_cli through backend instead of tmux_client
ThePlenkov Jul 1, 2026
a6764d4
fix: address tmux argument length and devin_cli status detection
ThePlenkov Jul 1, 2026
8d75d8d
fix: add per-user isolation to FIFO_DIR for security
ThePlenkov Jul 1, 2026
078ce73
fix: update test mocks to use get_backend instead of tmux_client
ThePlenkov Jul 1, 2026
38ffc6c
fix: update provider count tests after removing gemini_cli
ThePlenkov Jul 1, 2026
d667e08
style: run black formatting on modified files
ThePlenkov Jul 1, 2026
93b546b
fix: address cubic review findings
ThePlenkov Jul 1, 2026
2f71c4f
fix: move username sanitization to function to avoid import error
ThePlenkov Jul 1, 2026
2b790f2
revert: remove per-user isolation to fix import error
ThePlenkov Jul 1, 2026
2fa10a9
fix: address SonarCloud security finding on tempdir usage
ThePlenkov Jul 1, 2026
2c262f8
fix: address blocking and simple important issues from review
ThePlenkov Jul 1, 2026
6d78d7a
fix: address remaining important issues from review
ThePlenkov Jul 1, 2026
d76da69
fix: update test mocks to use use_paste_buffer instead of use_paste_b…
ThePlenkov Jul 1, 2026
fd78d62
fix: add SonarQube S5307 suppression comment for /tmp usage
ThePlenkov Jul 1, 2026
ca5dd70
fix: use tempfile.gettempdir() instead of /tmp for SonarCloud
ThePlenkov Jul 1, 2026
ba872a1
fix: add fallback to CAO_HOME_DIR for restricted environments
ThePlenkov Jul 1, 2026
2555da7
docs: add feature proposal for web/e2e Playwright API mocking
ThePlenkov Jul 1, 2026
2d6ec0d
revert: remove feature proposal file (created as GH issue #360 instead)
ThePlenkov Jul 1, 2026
19fe981
Merge branch 'main' into fix/devin-cli-provider
ThePlenkov Jul 2, 2026
fdbaae7
fix: address PR review #4632225492 — security, dead code, docs, scope
ThePlenkov Jul 11, 2026
4d55ba5
Merge branch 'main' into fix/devin-cli-provider
devin-ai-integration[bot] Jul 12, 2026
1159eac
fix(sonar): avoid logging user-controlled data and redundant exceptio…
devin-ai-integration[bot] Jul 12, 2026
9dea899
fix(sonar): remove user-controlled data from herdr log messages
devin-ai-integration[bot] Jul 12, 2026
a2ce5d1
fix(devin_cli): arm status monitor before launch and return UNKNOWN f…
ThePlenkov Jul 12, 2026
b0c9382
fix(devin-cli): address review threads for provider, FIFO, status, docs
devin-ai-integration[bot] Jul 12, 2026
c59b579
Merge remote branch 'origin/fix/devin-cli-provider' (keep error-patte…
devin-ai-integration[bot] Jul 12, 2026
84f1b77
style: format status_monitor.py with black
devin-ai-integration[bot] Jul 12, 2026
fa9569a
Merge branch 'main' into fix/devin-cli-provider (CHANGELOG conflict)
devin-ai-integration[bot] Jul 13, 2026
543b1ea
fix: address review threads for FALLBACK_PROVIDERS, FIFO init, and hi…
ThePlenkov Jul 13, 2026
1538517
Merge branch 'main' into fix/devin-cli-provider
ThePlenkov Jul 13, 2026
67598f1
fix(docs): align devin-cli status docs with code returning UNKNOWN fo…
ThePlenkov Jul 13, 2026
624a2f3
fix(act): address /act review threads on fork PR 27
ThePlenkov Jul 13, 2026
31290a2
Merge awslabs/main into fix/devin-cli-provider
devin-ai-integration[bot] Jul 17, 2026
2563f80
fix(review): address high-priority baz/CodeQL findings on PR #27
devin-ai-integration[bot] Jul 17, 2026
7dc17ca
fix(review): graph/web cleanup and MCP app lifecycle leaks
devin-ai-integration[bot] Jul 17, 2026
3f8a0f8
fix(review): CodeQL suppressions and MCP app listener cleanup
devin-ai-integration[bot] Jul 17, 2026
2591929
fix(review): disambiguate McpApp notification handler unsubscribe
devin-ai-integration[bot] Jul 17, 2026
427eaf8
fix(review): disambiguate graph cache by store and unify rebuild inde…
devin-ai-integration[bot] Jul 17, 2026
f346261
fix(review): use same-line CodeQL suppression for validated path sinks
devin-ai-integration[bot] Jul 17, 2026
a7f4ada
fix(security): inline CodeQL-recognized realpath+startswith guards be…
devin-ai-integration[bot] Jul 17, 2026
5c89e12
fix(security): normalize the safe base with realpath+abspath before s…
devin-ai-integration[bot] Jul 17, 2026
20bf62f
fix(security): always join+realpath the safe base and use single star…
devin-ai-integration[bot] Jul 17, 2026
5d963b7
fix(review): keep script index stem from the raw glob path, not the r…
devin-ai-integration[bot] Jul 17, 2026
729b598
fix(sonar): address SonarCloud reliability/security findings on new code
devin-ai-integration[bot] Jul 17, 2026
1c9f2e1
fix(sonar): suppress/resolve remaining SonarCloud reliability/securit…
devin-ai-integration[bot] Jul 17, 2026
47b09c3
fix(sonar): refactor host.js message router and suppress remaining co…
devin-ai-integration[bot] Jul 17, 2026
8ab170c
fix(codeql): replace dynamic handler map with switch in e2e/host.js
devin-ai-integration[bot] Jul 17, 2026
b942dea
fix(sonar/codeql): suppress/resolve additional SonarCloud findings an…
devin-ai-integration[bot] Jul 17, 2026
49a3fcc
refactor(web): extract buildGraph and split MemoryGraphView to reduce…
devin-ai-integration[bot] Jul 17, 2026
db16031
fix(web): memoize MemoryGraphView hooks to prevent side-panel reset o…
devin-ai-integration[bot] Jul 17, 2026
2cb9006
chore(sonar): suppress/resolve SonarCloud failure annotations with do…
devin-ai-integration[bot] Jul 17, 2026
0390876
style: run black to fix formatting after Sonar NOSONAR comments
devin-ai-integration[bot] Jul 17, 2026
2a9c8d3
fix(agui-dashboard): correct stray ']' and switch to [[ in run.sh
devin-ai-integration[bot] Jul 17, 2026
90041ae
fix(sonar): address remaining SonarCloud failure annotations
devin-ai-integration[bot] Jul 17, 2026
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
4 changes: 4 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,8 @@ jobs:
uses: actions/setup-node@v4
with:
node-version: "20"
cache: "npm"
cache-dependency-path: cao_mcp_apps/package-lock.json

- name: Install uv
uses: astral-sh/setup-uv@v4
Expand Down Expand Up @@ -181,6 +183,8 @@ jobs:
uses: actions/setup-node@v4
with:
node-version: "20"
cache: "npm"
cache-dependency-path: cao_mcp_apps/package-lock.json

- name: Install MCP-apps deps
run: npm install
Expand Down
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

- bundle built-in memory plugins for Claude Code, Kiro, and Codex (#269)

- add Devin CLI (`devin`) provider (#336)

- Web UI support for the memory system (#290)

- Phase 3 — LLM wiki compile, cross-references, lint, audit log, scoring (#285)
Expand Down
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,7 @@ CAO drives existing CLI agent tools — it does not replace them. Before using C
| **OpenCode CLI** *(experimental — temporary inbox polling fallback for multi-agent callbacks, [#203](https://github.com/awslabs/cli-agent-orchestrator/issues/203))* | [Provider docs](docs/opencode-cli.md) · [Installation](https://opencode.ai) | Per-model API key |
| **Cursor CLI** | [Provider docs](docs/cursor-cli.md) · [Installation](https://cursor.com/cli) | Cursor subscription / API key |
| **Antigravity CLI** | [Provider docs](docs/antigravity-cli.md) · [Installation](https://antigravity.google) | Google account (shared with the Antigravity IDE login) |
| **Devin CLI** | [Provider docs](docs/devin-cli.md) · [Installation](https://devin.ai) | Devin CLI auth |

## Quick Start

Expand Down Expand Up @@ -185,7 +186,7 @@ cao launch --agents code_supervisor

# Or specify a provider
cao launch --agents code_supervisor --provider claude_code
# Valid: kiro_cli | claude_code | codex | antigravity_cli | hermes | kimi_cli | copilot_cli | opencode_cli | cursor_cli
# Valid: kiro_cli | claude_code | codex | antigravity_cli | devin_cli | hermes | kimi_cli | copilot_cli | opencode_cli | cursor_cli

# Unrestricted access, skip confirmation (DANGEROUS)
cao launch --agents code_supervisor --yolo
Expand Down Expand Up @@ -295,7 +296,7 @@ provider: claude_code
---
```

Valid values: `kiro_cli`, `claude_code`, `codex`, `antigravity_cli`, `hermes`, `kimi_cli`, `copilot_cli`, `opencode_cli`, `cursor_cli`. The `cao launch --provider` flag always takes precedence for the initial session. See [`examples/cross-provider/`](examples/cross-provider/).
Valid values: `kiro_cli`, `claude_code`, `codex`, `antigravity_cli`, `devin_cli`, `hermes`, `kimi_cli`, `copilot_cli`, `opencode_cli`, `cursor_cli`. The `cao launch --provider` flag always takes precedence for the initial session. See [`examples/cross-provider/`](examples/cross-provider/).

### Tool Restrictions

Expand Down
103 changes: 59 additions & 44 deletions cao_mcp_apps/e2e/host.js
Original file line number Diff line number Diff line change
Expand Up @@ -459,6 +459,48 @@
return { success: true, kind };
}

function handleInitialize(winInfo, id) {
replyTo(winInfo, id, {
hostContext: { theme: "light", uiSurface: true },
// Advertise host-delegated capabilities so the views surface them
// (e.g. the dashboard's "Open full Web UI" → ui/open-link).
hostCapabilities: { openLinks: {} },
});
}

function handleNotificationsInitialized(winInfo) {
winInfo.initialized = true;
// Deliver the "tool result that opened the view" so views needing an
// initial payload (the agent view needs a terminal_id) hydrate.
if (winInfo.view === "agent") {
pushTo(winInfo, "ui/notifications/tool-result", {
structuredContent: agentSnapshot(AGENT_VIEW_ID),
});
}
if (winInfo.view === "graph") {
pushTo(winInfo, "ui/notifications/tool-result", {
structuredContent: graphSnapshot(),
});
}
if (winInfo.resolve) winInfo.resolve();
}

function handleUpdateModelContext(winInfo, id, params) {
state.modelNotes.push(params);
replyTo(winInfo, id, {});
}

function sendUnknownMethodError(winInfo, id, method) {
winInfo.win.postMessage(
{
jsonrpc: "2.0",
id,
error: { code: -32601, message: `unknown ${method}` },
},
"*",
);
}

function onMessage(event) {
const data = event.data;
if (!data || data.jsonrpc !== "2.0") return;
Expand All @@ -473,50 +515,23 @@
if (!winInfo) return;

const { id, method, params } = data;
if (method === "ui/initialize") {
replyTo(winInfo, id, {
hostContext: { theme: "light", uiSurface: true },
// Advertise host-delegated capabilities so the views surface them
// (e.g. the dashboard's "Open full Web UI" → ui/open-link).
hostCapabilities: { openLinks: {} },
});
return;
}
if (method === "ui/notifications/initialized") {
winInfo.initialized = true;
// Deliver the "tool result that opened the view" so views needing an
// initial payload (the agent view needs a terminal_id) hydrate.
if (winInfo.view === "agent") {
pushTo(winInfo, "ui/notifications/tool-result", {
structuredContent: agentSnapshot(AGENT_VIEW_ID),
});
}
if (winInfo.view === "graph") {
pushTo(winInfo, "ui/notifications/tool-result", {
structuredContent: graphSnapshot(),
});
}
if (winInfo.resolve) winInfo.resolve();
return;
}
if (method === "ui/update-model-context") {
state.modelNotes.push(params);
replyTo(winInfo, id, {});
return;
}
if (method === "tools/call") {
handleToolCall(winInfo, id, params.name, params.arguments || {});
return;
}
if (id !== undefined && id !== null) {
winInfo.win.postMessage(
{
jsonrpc: "2.0",
id,
error: { code: -32601, message: `unknown ${method}` },
},
"*",
);
switch (method) {
case "ui/initialize":
handleInitialize(winInfo, id);
break;
case "ui/notifications/initialized":
handleNotificationsInitialized(winInfo);
break;
case "ui/update-model-context":
handleUpdateModelContext(winInfo, id, params);
break;
case "tools/call":
handleToolCall(winInfo, id, params.name, params.arguments || {});
break;
default:
if (id !== undefined && id !== null) {
sendUnknownMethodError(winInfo, id, method);
}
}
}

Expand Down
6 changes: 3 additions & 3 deletions cao_mcp_apps/src/agent/AgentView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,9 @@ export function AgentView({
if (!app) return;
let stop: (() => void) | undefined;

app.onToolResult((result) => {
const unsubscribe = app.onToolResult((result) => {
const snap = (result?.structuredContent ?? result) as
| AgentDetailSnapshot
| undefined;
AgentDetailSnapshot | undefined;
if (snap && snap.terminal_id) {
tidRef.current = snap.terminal_id;
setSnapshot(snap);
Expand All @@ -58,6 +57,7 @@ export function AgentView({
});

return () => {
unsubscribe();
if (stop) stop();
};
// eslint-disable-next-line react-hooks/exhaustive-deps
Expand Down
3 changes: 2 additions & 1 deletion cao_mcp_apps/src/dashboard/Dashboard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ export function Dashboard({
let stop: (() => void) | undefined;

// Register handlers BEFORE connect (lifecycle invariant).
app.onToolResult((result) => {
const unsubscribe = app.onToolResult((result) => {
const snap = (result?.structuredContent ?? result) as
DashboardSnapshot | undefined;
if (snap && Array.isArray(snap.terminals)) applyDelta(snap);
Expand All @@ -98,6 +98,7 @@ export function Dashboard({
});

return () => {
unsubscribe();
if (stop) stop();
};
// eslint-disable-next-line react-hooks/exhaustive-deps
Expand Down
3 changes: 2 additions & 1 deletion cao_mcp_apps/src/graph/GraphView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -60,14 +60,14 @@
return graph;
}

export function GraphView({
app,
initialSnapshot,
provider = "memory",
scope,
scopeId,
onOpenTopic,
}: GraphViewProps): JSX.Element {

Check warning on line 70 in cao_mcp_apps/src/graph/GraphView.tsx

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Mark the props of the component as read-only.

See more on https://sonarcloud.io/project/issues?id=ThePlenkov_cli-agent-orchestrator&issues=AZ9uxNdbpMYBLw0H3LSP&open=AZ9uxNdbpMYBLw0H3LSP&pullRequest=27
const [snapshot, setSnapshot] = useState<GraphViewData | null>(
initialSnapshot ?? null,
);
Expand All @@ -80,7 +80,7 @@
let stop: (() => void) | undefined;

// Register handlers BEFORE connect (lifecycle invariant).
app.onToolResult((result) => {
const unsubscribe = app.onToolResult((result) => {
const snap = (result?.structuredContent ?? result) as
GraphViewData | undefined;
if (snap && Array.isArray(snap.nodes)) {
Expand All @@ -105,6 +105,7 @@
});

return () => {
unsubscribe();
if (stop) stop();
};
// eslint-disable-next-line react-hooks/exhaustive-deps
Expand Down
27 changes: 21 additions & 6 deletions cao_mcp_apps/src/shared/mcpApp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,12 @@ export class McpApp {
private target: Window;
private scope: Window;
private nextId: JsonRpcId = 1;
private nextHandlerId = 0;
private pending = new Map<JsonRpcId, PendingRequest>();
private notificationHandlers = new Map<string, NotificationHandler[]>();
private notificationHandlers = new Map<
string,
{ id: number; handler: NotificationHandler }[]
>();
private listener?: (event: MessageEvent) => void;
private connected = false;
/** Host context (theme, container dimensions, etc.) from initialize. */
Expand All @@ -70,15 +74,26 @@ export class McpApp {
// ---- handler registration (call BEFORE connect) ------------------------

/** Register a notification handler. MUST be called before `connect()`. */
on(method: string, handler: NotificationHandler): void {
on(method: string, handler: NotificationHandler): () => void {
const id = ++this.nextHandlerId;
const list = this.notificationHandlers.get(method) ?? [];
list.push(handler);
list.push({ id, handler });
this.notificationHandlers.set(method, list);
return () => {
const updated = (this.notificationHandlers.get(method) ?? []).filter(
(entry) => entry.id !== id,
);
if (updated.length) {
this.notificationHandlers.set(method, updated);
} else {
this.notificationHandlers.delete(method);
}
Comment thread
devin-ai-integration[bot] marked this conversation as resolved.
};
}

/** Convenience: the tool result that instantiated/refreshed the View. */
onToolResult(handler: (result: any) => void): void {
this.on("ui/notifications/tool-result", handler);
onToolResult(handler: (result: any) => void): () => void {
return this.on("ui/notifications/tool-result", handler);
}

/** Convenience: the tool input (arguments) for the current tool call. */
Expand Down Expand Up @@ -340,7 +355,7 @@ export class McpApp {
}
const handlers = this.notificationHandlers.get(data.method);
if (handlers) {
for (const handler of handlers) handler(data.params);
for (const { handler } of handlers) handler(data.params);
}
}
}
Expand Down
17 changes: 17 additions & 0 deletions cao_mcp_apps/src/test/lifecycle.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,23 @@ function buildHost(opts: MockHostOptions = {}): MockHost {
}

describe("McpApp convenience notification handlers", () => {
it("returns an unsubscribe that removes only its handler", async () => {
const host = buildHost({ tools: {} });
const app = makeApp(host);

const shared = vi.fn();
const offFirst = app.on("ui/notifications/tool-result", shared);
app.on("ui/notifications/tool-result", shared);

offFirst();

await app.connect();
host.pushNotification("ui/notifications/tool-result", {
structuredContent: { nodes: [] },
});
expect(shared).toHaveBeenCalledOnce();
});

it("delivers tool-input arguments, host-context changes, and teardown reason", async () => {
const host = buildHost({ tools: {} });
const app = makeApp(host);
Expand Down
Loading
Loading