cd /news/developer-tools/cadenza-microsoft-agent-framework-miโ€ฆ ยท home โ€บ topics โ€บ developer-tools โ€บ article
[ARTICLE ยท art-34647] src=gist.github.com โ†— pub= topic=developer-tools verified=true sentiment=ยท neutral

Cadenza + Microsoft Agent Framework + Microsoft Foundry

A developer demonstrated how to quickly start coding with Cadenza by using a vibe coding prompt, then run the resulting .NET 10 file-based app with dotnet run or chmod +x. The example included creating a ping tool for Naver, an OpenAPI calculator with a frontend, and an AI agent console using Microsoft Agent Framework and Foundry.

read14 min views21 publishedJun 18, 2026

๋‹ค์Œ๊ณผ ๊ฐ™์ด ๋ฐ”์ด๋ธŒ์ฝ”๋”ฉ ํ”„๋กฌํ”„ํŠธ๋ฅผ ์ž…๋ ฅํ•˜๋ฉด ์‰ฝ๊ฒŒ Cadenza ๊ธฐ๋ฐ˜ ์ฝ”๋“œ๋ฅผ ๋น ๋ฅด๊ฒŒ ์‹œ์ž‘ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ทธ ํ›„, dotnet run ๋ช…๋ น์–ด๋‚˜ chmod +x file.cs ๋ช…๋ น์–ด๋กœ ์‹คํ–‰ ๊ถŒํ•œ์„ ๋ถ€์—ฌํ•œ ๋‹ค์Œ ์‹คํ–‰ํ•˜๋ฉด ์†์‰ฝ๊ฒŒ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋จผ์ € https://raw.githubusercontent.com/rkttu/cadenza/main/README.ko.md ๋ฅผ ์ฝ๊ณ , ๊ฑฐ๊ธฐ ์ ํžŒ Cadenza ๊ทœ์น™(๋‹จ์ผ .cs, shebang + #:sdk ์ง€์‹œ๋ฌธ, ์ •ํ™•ํ•œ ๋ฒ„์ „ ํ•€, Console ๋Œ€์‹  ambient helper)์„ ๊ทธ๋Œ€๋กœ ์ง€์ผœ .NET 10 ํŒŒ์ผ ๊ธฐ๋ฐ˜ ์•ฑ์œผ๋กœ ๋‹ค์Œ์„ ๊ตฌํ˜„ํ•˜๊ณ  dotnet run์œผ๋กœ ์‹คํ–‰ํ•ด์ค˜:
๋จผ์ € https://raw.githubusercontent.com/rkttu/cadenza/main/README.ko.md ๋ฅผ ์ฝ๊ณ , ๊ฑฐ๊ธฐ ์ ํžŒ Cadenza ๊ทœ์น™(๋‹จ์ผ .cs, shebang + #:sdk ์ง€์‹œ๋ฌธ, ์ •ํ™•ํ•œ ๋ฒ„์ „ ํ•€, Console ๋Œ€์‹  ambient helper)์„ ๊ทธ๋Œ€๋กœ ์ง€์ผœ .NET 10 ํŒŒ์ผ ๊ธฐ๋ฐ˜ ์•ฑ์œผ๋กœ ๋‹ค์Œ์„ ๊ตฌํ˜„ํ•˜๊ณ  dotnet run์œผ๋กœ ์‹คํ–‰ํ•ด์ค˜:

https://www.naver.com ์‚ฌ์ดํŠธ๋ฅผ 1์ดˆ ๊ฐ„๊ฒฉ์œผ๋กœ 5ํšŒ https๋กœ ping ํ•˜๋Š” ์ฝ”๋“œ๋ฅผ ๋งŒ๋“ค์–ด์ค˜.
๋จผ์ € https://raw.githubusercontent.com/rkttu/cadenza/main/README.ko.md ๋ฅผ ์ฝ๊ณ , ๊ฑฐ๊ธฐ ์ ํžŒ Cadenza ๊ทœ์น™(๋‹จ์ผ .cs, shebang + #:sdk ์ง€์‹œ๋ฌธ, ์ •ํ™•ํ•œ ๋ฒ„์ „ ํ•€, Console ๋Œ€์‹  ambient helper)์„ ๊ทธ๋Œ€๋กœ ์ง€์ผœ .NET 10 ํŒŒ์ผ ๊ธฐ๋ฐ˜ ์•ฑ์œผ๋กœ ๋‹ค์Œ์„ ๊ตฌํ˜„ํ•˜๊ณ  dotnet run์œผ๋กœ ์‹คํ–‰ํ•ด์ค˜:

๊ฐ„๋‹จํ•œ ์‚ฌ์น™ ์—ฐ์‚ฐ ๊ณ„์‚ฐ๊ธฐ๋ฅผ OpenAPI ํ˜•์‹์œผ๋กœ ๊ธฐ๋Šฅ์„ ๊ตฌํ˜„ํ•˜๊ณ  ํ”„๋ก ํŠธ์—”๋“œ HTML ํŽ˜์ด์ง€๋ฅผ ๋‹ด๋Š” ์›น ์‚ฌ์ดํŠธ๋ฅผ ๋งŒ๋“ค์—ˆ์œผ๋ฉด ํ•ด.
๋จผ์ € https://raw.githubusercontent.com/rkttu/cadenza/main/README.ko.md ๋ฅผ ์ฝ๊ณ , ๊ฑฐ๊ธฐ ์ ํžŒ Cadenza ๊ทœ์น™(๋‹จ์ผ .cs, shebang + #:sdk ์ง€์‹œ๋ฌธ, ์ •ํ™•ํ•œ ๋ฒ„์ „ ํ•€, Console ๋Œ€์‹  ambient helper)์„ ๊ทธ๋Œ€๋กœ ์ง€์ผœ .NET 10 ํŒŒ์ผ ๊ธฐ๋ฐ˜ ์•ฑ์œผ๋กœ ๋‹ค์Œ์„ ๊ตฌํ˜„ํ•˜๊ณ  dotnet run์œผ๋กœ ์‹คํ–‰ํ•ด์ค˜:

์ฝ˜์†” ํ˜•ํƒœ๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” AI ์—์ด์ „ํŠธ๋ฅผ ๋งŒ๋“ค๋ ค๊ณ  ํ•ด. OPENAPI_ENDPOINT, OPENAPI_MODEL, OPENAPI_KEY ๋ผ๋Š” ํ™˜๊ฒฝ ๋ณ€์ˆ˜๋ฅผ ๋ฏธ๋ฆฌ ์„ค์ •ํ•ด๋‘์—ˆ๊ณ , ์ž„์˜์˜ ๋‚œ์ˆ˜๋ฅผ ์ƒ์„ฑํ•ด์„œ ๋ฐ˜ํ™˜ํ•˜๋Š” ๋„๊ตฌ๋ฅผ ํ•˜๋‚˜ ๋‚ด์žฅํ•œ ์—์ด์ „ํŠธ๋ฅผ ๋งŒ๋“ค๋ ค๊ณ  ํ•ด.

| #!/usr/bin/env dotnet run | | | #:sdk Cadenza@1.0.15 | | | #:package Azure.AI.Projects@2.0.1 | | | #:package Microsoft.Agents.AI@1.10.0 | | | #:package Microsoft.Agents.AI.Foundry@1.5.0 | | | #:package Azure.Identity@1.21.0 | | | using Azure.AI.Projects; | | | using Azure.Identity; | | | using Microsoft.Extensions.AI; | | | // Last successful run context: | | | // - FOUNDRY_PROJECT_ENDPOINT | | | // - FOUNDRY_MODEL_DEPLOYMENT | | | // - AZURE_TENANT_ID | | | // - PATH included Azure CLI dir: C:\Program Files\Microsoft SDKs\Azure\CLI2\wbin | | | // Installed software used: | | | // - Microsoft Azure CLI (az.cmd) at C:\Program Files\Microsoft SDKs\Azure\CLI2\wbin\az.cmd | | | var endpoint = Env.Get("FOUNDRY_PROJECT_ENDPOINT") | | | ?? throw new InvalidOperationException("FOUNDRY_PROJECT_ENDPOINT environment variable is required."); | | | var modelDeployment = Env.Get("FOUNDRY_MODEL_DEPLOYMENT") | | | ?? throw new InvalidOperationException("FOUNDRY_MODEL_DEPLOYMENT environment variable is required."); | | | var projectClient = new AIProjectClient(new Uri(endpoint), new DefaultAzureCredential()); | | | var tools = new List<AITool> | | | { | | | (AITool)AIFunctionFactory.Create( | | | (Func<string>)(() => DateTimeOffset.Now.ToString("O")), | | | name: "get_current_time", | | | description: "Returns the current local time in ISO-8601 format.", | | | serializerOptions: null) | | | }; | | | var agent = projectClient.AsAIAgent( | | | model: modelDeployment, | | | instructions: "You are a concise assistant. If the user asks for the current time, call get_current_time.", | | | name: "cadenza-file-agent-console", | | | description: "Single-file agent with a sample time tool and console chat.", | | | tools: tools); | | | // Keep local transcript so each turn includes prior context. | | | var history = new List<ChatMessage>(); | | | WriteLine("Console chat started. Type '/exit' to quit."); | | | while (true) | | | { | | | Write("you> "); | | | var userInput = Console.ReadLine(); | | | if (string.IsNullOrWhiteSpace(userInput)) | | | { | | | continue; | | | } | | | if (string.Equals(userInput.Trim(), "/exit", StringComparison.OrdinalIgnoreCase)) | | | { | | | WriteLine("bye."); | | | break; | | | } | | | history.Add(new ChatMessage(ChatRole.User, userInput)); | | | var runResult = await agent.RunAsync(history, session: null, options: null, cancellationToken: default); | | | // AgentResponse concrete shape may evolve; reflect common output fields safely. | | | var resultType = runResult.GetType(); | | | var text = resultType.GetProperty("Text")?.GetValue(runResult)?.ToString(); | | | var answer = text ?? runResult.ToString() ?? string.Empty; | | | WriteLine($"agent> {answer}"); | | | history.Add(new ChatMessage(ChatRole.Assistant, answer)); | | | } |

| #!/usr/bin/env dotnet run | | | #:sdk Cadenza@1.0.15 | | | #:package Azure.AI.Projects@2.0.1 | | | #:package Microsoft.Agents.AI@1.10.0 | | | #:package Microsoft.Agents.AI.Foundry@1.5.0 | | | #:package Azure.Identity@1.21.0 | | | // Last successful run context: | | | // - FOUNDRY_PROJECT_ENDPOINT | | | // - FOUNDRY_MODEL_DEPLOYMENT | | | // - AZURE_TENANT_ID | | | // - PATH included Azure CLI dir: C:\Program Files\Microsoft SDKs\Azure\CLI2\wbin | | | // Installed software used: | | | // - Microsoft Azure CLI (az.cmd) at C:\Program Files\Microsoft SDKs\Azure\CLI2\wbin\az.cmd | | | using Azure.AI.Projects; | | | using Azure.Identity; | | | var endpoint = Env.Get("FOUNDRY_PROJECT_ENDPOINT") | | | ?? throw new InvalidOperationException("FOUNDRY_PROJECT_ENDPOINT environment variable is required."); | | | var modelDeployment = Env.Get("FOUNDRY_MODEL_DEPLOYMENT") | | | ?? throw new InvalidOperationException("FOUNDRY_MODEL_DEPLOYMENT environment variable is required."); | | | var projectClient = new AIProjectClient(new Uri(endpoint), new DefaultAzureCredential()); | | | var agent = projectClient.AsAIAgent( | | | model: modelDeployment, | | | instructions: "You are a concise assistant. Respond in one sentence.", | | | name: "cadenza-file-agent", | | | description: "Single-file agent built with Cadenza + Microsoft Agent Framework + Foundry."); | | | var prompt = "Say exactly 'Cadenza Foundry Agent is running.'"; | | | var runResult = await agent.RunAsync(prompt, session: null, options: null, cancellationToken: default); | | | // AgentResponse concrete shape may evolve; reflect common output fields safely. | | | var resultType = runResult.GetType(); | | | var text = resultType.GetProperty("Text")?.GetValue(runResult)?.ToString(); | | | WriteLine($"Prompt: {prompt}"); | | | WriteLine($"Response: {text ?? runResult.ToString()}"); |

| #!/usr/bin/env dotnet run | | | #:sdk Cadenza.Agent@1.0.15 | | | using System.ClientModel; | | | using OpenAI; | | | var endpoint = Env.Get("OPENAPI_ENDPOINT") | | | ?? throw new InvalidOperationException("OPENAPI_ENDPOINT environment variable is required."); | | | var model = Env.Get("OPENAPI_MODEL") | | | ?? throw new InvalidOperationException("OPENAPI_MODEL environment variable is required."); | | | var apiKey = Env.Get("OPENAPI_KEY") | | | ?? throw new InvalidOperationException("OPENAPI_KEY environment variable is required."); | | | SystemPrompt(""" | | | You are a helpful console AI assistant. | | | When users ask for randomness, prefer calling the random_int tool. | | | Explain the generated random number briefly and clearly. | | | """); | | | Tool("random_int", "Generate a random integer between min and max (inclusive).", | | | (int min, int max) => | | | { | | | if (min > max) | | | return new { min, max, value = (int?)null, error = "min must be less than or equal to max." }; | | | var value = Random.Shared.Next(min, max + 1); | | | return new { min, max, value = (int?)value, error = (string?)null }; | | | }); | | | var options = new OpenAIClientOptions | | | { | | | Endpoint = new Uri(endpoint) | | | }; | | | var chatClient = new OpenAI.Chat.ChatClient(model, new ApiKeyCredential(apiKey), options) | | | .AsIChatClient(); | | | UseChatClient(chatClient); | | | WriteLine("Console AI agent is ready."); | | | WriteLine("- Backend endpoint: " + endpoint); | | | WriteLine("- Model: " + model); | | | WriteLine("- Tool: random_int(min, max)"); | | | WriteLine(); | | | WriteLine("Usage:"); | | | WriteLine("- dotnet run .\agent_console_random.cs"); | | | WriteLine(" (interactive REPL)"); | | | WriteLine("- dotnet run .\agent_console_random.cs "Generate a random number between 1 and 100""); | | | WriteLine(" (one-shot response)"); | | | WriteLine(); | | | if (args.Length > 0) | | | { | | | var prompt = string.Join(" ", args); | | | var reply = await Reply(prompt); | | | WriteLine(reply); | | | return; | | | } | | | await ChatLoop(); |

| #!/usr/bin/env dotnet run | | | #:sdk Cadenza.Web@1.0.15 | | | using System.Globalization; | | | using Microsoft.AspNetCore.Http; | | | const string HtmlPage = """ | | | <!doctype html> | | | <html lang="en"> | | | <head> | | | <meta charset="utf-8" /> | | | <meta name="viewport" content="width=device-width, initial-scale=1" /> | | | <title>Cadenza Calculator</title> | | | <style> | | | :root { | | | --bg: #f4f7ff; | | | --panel: #ffffff; | | | --ink: #1d2433; | | | --muted: #5f697d; | | | --accent: #0b7a57; | | | --line: #d4ddef; | | | } | | | * { box-sizing: border-box; } | | | body { | | | margin: 0; | | | min-height: 100vh; | | | display: grid; | | | place-items: center; | | | padding: 20px; | | | color: var(--ink); | | | font-family: "Segoe UI", Tahoma, sans-serif; | | | background: | | | radial-gradient(circle at 10% 10%, #e9f0ff 0%, transparent 40%), | | | radial-gradient(circle at 90% 90%, #def6ea 0%, transparent 35%), | | | var(--bg); | | | } | | | main { | | | width: min(760px, 100%); | | | background: var(--panel); | | | border: 1px solid var(--line); | | | border-radius: 14px; | | | box-shadow: 0 14px 30px rgba(16, 34, 74, 0.12); | | | overflow: hidden; | | | } | | | header { | | | padding: 18px 20px; | | | border-bottom: 1px solid var(--line); | | | background: linear-gradient(135deg, #f8fbff, #eefaf4); | | | } | | | h1 { margin: 0; font-size: 1.35rem; } | | | p { margin: 8px 0 0; color: var(--muted); } | | | .content { padding: 20px; display: grid; gap: 12px; } | | | .row { display: grid; gap: 10px; grid-template-columns: 1fr 130px 1fr auto; } | | | input, select, button { font: inherit; } | | | input, select { | | | width: 100%; | | | border: 1px solid var(--line); | | | border-radius: 10px; | | | padding: 10px 12px; | | | background: #fff; | | | } | | | button { | | | border: 0; | | | border-radius: 10px; | | | padding: 10px 14px; | | | background: var(--accent); | | | color: #fff; | | | font-weight: 600; | | | cursor: pointer; | | | } | | | pre { | | | margin: 0; | | | min-height: 130px; | | | border: 1px solid var(--line); | | | border-radius: 10px; | | | padding: 12px; | | | background: #fff; | | | overflow: auto; | | | } | | | .links { display: flex; gap: 12px; flex-wrap: wrap; } | | | .links a { color: var(--accent); text-underline-offset: 3px; } | | | @media (max-width: 640px) { .row { grid-template-columns: 1fr; } } | | | </style> | | | </head> | | | <body> | | | <main> | | | <header> | | | <h1>Arithmetic Calculator</h1> | | | <p>Cadenza.Web single-file app with OpenAPI JSON endpoint.</p> | | | </header> | | | <section class="content"> | | | <div class="row"> | | | <input id="a" type="number" step="any" value="12" aria-label="a" /> | | | <select id="op" aria-label="op"> | | | <option value="add">add (+)</option> | | | <option value="sub">sub (-)</option> | | | <option value="mul">mul ()</option> | | | <option value="div">div (/)</option> | | | </select> | | | <input id="b" type="number" step="any" value="3" aria-label="b" /> | | | <button id="run" type="button">Calculate</button> | | | </div> | | | <pre id="result">No calculation yet.</pre> | | | <div class="links"> | | | <a href="/openapi/v1.json" target="_blank" rel="noreferrer">OpenAPI JSON</a> | | | <a href="/api/calc?a=12&b=3&op=div" target="_blank" rel="noreferrer">Sample API call</a> | | | </div> | | | </section> | | | </main> | | | <script> | | | const resultEl = document.getElementById('result'); | | | const runBtn = document.getElementById('run'); | | | runBtn.addEventListener('click', async () => { | | | const a = document.getElementById('a').value; | | | const b = document.getElementById('b').value; | | | const op = document.getElementById('op').value; | | | const url = /api/calc?a=${encodeURIComponent(a)}&b=${encodeURIComponent(b)}&op=${encodeURIComponent(op)}; | | | resultEl.textContent = 'Calculating...'; | | | try { | | | const response = await fetch(url); | | | const data = await response.json(); | | | resultEl.textContent = JSON.stringify(data, null, 2); | | | } catch (err) { | | | resultEl.textContent = String(err); | | | } | | | }); | | | </script> | | | </body> | | | </html> | | | """; | | | const string OpenApiJson = """ | | | { | | | "openapi": "3.0.3", | | | "info": { | | | "title": "Cadenza Arithmetic Calculator API", | | | "version": "1.0.0", | | | "description": "Simple arithmetic operations: add, sub, mul, div" | | | }, | | | "servers": [ | | | { "url": "http://localhost:8080" } | | | ], | | | "paths": { | | | "/api/calc": { | | | "get": { | | | "summary": "Calculate with query parameters", | | | "parameters": [ | | | { "name": "a", "in": "query", "required": true, "schema": { "type": "number", "format": "double" } }, | | | { "name": "b", "in": "query", "required": true, "schema": { "type": "number", "format": "double" } }, | | | { "name": "op", "in": "query", "required": true, "schema": { "type": "string", "enum": ["add", "sub", "mul", "div"] } } | | | ], | | | "responses": { | | | "200": { "description": "Calculation succeeded" }, | | | "400": { "description": "Invalid input" } | | | } | | | } | | | } | | | } | | | } | | | """; | | | Get("/", () => Results.Content(HtmlPage, "text/html; charset=utf-8")); | | | Get("/api/calc", (string a, string b, string op) => | | | { | | | if (!double.TryParse(a, NumberStyles.Float, CultureInfo.InvariantCulture, out var left)) | | | return Results.BadRequest(new { error = "Parameter 'a' must be a valid number." }); | | | if (!double.TryParse(b, NumberStyles.Float, CultureInfo.InvariantCulture, out var right)) | | | return Results.BadRequest(new { error = "Parameter 'b' must be a valid number." }); | | | var normalized = NormalizeOp(op); | | | if (!TryCalculate(left, right, normalized, out var result, out var error)) | | | return Results.BadRequest(new { error }); | | | return Results.Ok(new | | | { | | | a = left, | | | b = right, | | | op = normalized, | | | expression = $"{left} {OpSymbol(normalized)} {right}", | | | result | | | }); | | | }); | | | Get("/openapi/v1.json", () => Results.Content(OpenApiJson, "application/json; charset=utf-8")); | | | await Run(); | | | static bool TryCalculate(double a, double b, string op, out double value, out string error) | | | { | | | value = 0; | | | error = string.Empty; | | | switch (op) | | | { | | | case "add": | | | value = a + b; | | | return true; | | | case "sub": | | | value = a - b; | | | return true; | | | case "mul": | | | value = a * b; | | | return true; | | | case "div": | | | if (b == 0) | | | { | | | error = "Division by zero is not allowed."; | | | return false; | | | } | | | value = a / b; | | | return true; | | | default: | | | error = "Parameter 'op' must be one of: add, sub, mul, div."; | | | return false; | | | } | | | } | | | static string NormalizeOp(string? op) => | | | (op ?? string.Empty).Trim().ToLowerInvariant() switch | | | { | | | "+" => "add", | | | "-" => "sub", | | | "" => "mul", | | | "/" => "div", | | | var x => x, | | | }; | | | static string OpSymbol(string op) => op switch | | | { | | | "add" => "+", | | | "sub" => "-", | | | "mul" => "*", | | | "div" => "/", | | | _ => "?" | | | }; |

| { | | | "servers": { | | | "brave-search": { | | | "command": "npx", | | | "args": [ | | | "-y", | | | "@brave/brave-search-mcp-server", | | | "--transport", | | | "stdio" | | | ], | | | "env": { | | | "BRAVE_API_KEY": "${input:brave_api_key}" | | | }, | | | "type": "stdio" | | | }, | | | "handmirrormcp": { | | | "type": "stdio", | | | "command": "dnx", | | | "args": ["HandMirrorMcp@0.1.1", "--yes"] | | | }, | | | "microsoft-learn": { | | | "type": "http", | | | "url": "https://learn.microsoft.com/api/mcp" | | | } | | | }, | | | "inputs": [ | | | { | | | "id": "brave_api_key", | | | "type": "promptString", | | | "description": "Brave Search API Key" | | | } | | | ] | | | } |

๋‹ค์Œ๊ณผ ๊ฐ™์ด ๋ฐ”์ด๋ธŒ์ฝ”๋”ฉ ํ”„๋กฌํ”„ํŠธ๋ฅผ ์ž…๋ ฅํ•˜๋ฉด ์‰ฝ๊ฒŒ Cadenza ๊ธฐ๋ฐ˜ ์ฝ”๋“œ๋ฅผ ๋น ๋ฅด๊ฒŒ ์‹œ์ž‘ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ทธ ํ›„, dotnet run ๋ช…๋ น์–ด๋‚˜ chmod +x file.cs ๋ช…๋ น์–ด๋กœ ์‹คํ–‰ ๊ถŒํ•œ์„ ๋ถ€์—ฌํ•œ ๋‹ค์Œ ์‹คํ–‰ํ•˜๋ฉด ์†์‰ฝ๊ฒŒ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

โƒWPNCODE0โƒ

โƒWPNCODE1โƒ

โƒWPNCODE2โƒ

โƒWPNCODE3โƒ

โ”€โ”€ more in #developer-tools 4 stories ยท sorted by recency
โ”€โ”€ more on @cadenza 3 stories trending now
sponsored brought to you by zahid.host 4,200+ EU-deployed projects
reading about agents? ship yours in a single git push.

Run your AI side-project on zahid.host

EU-based hosting, git-push deploys, automatic HTTPS, no cold starts. Free tier with a custom domain โ€” perfect for shipping the agent you just read about.

$git push zahid main
โ†’ Live at https://your-agent.zahid.host โœ“
Get free account โ†’ Pricing
from โ‚ฌ0/mo ยท no card required
LIVE [news/cadenza-microsoft-agโ€ฆ] indexed:0 read:14min 2026-06-18 ยท โ€”