{"id":644,"date":"2026-06-29T18:17:32","date_gmt":"2026-06-29T18:17:32","guid":{"rendered":"https:\/\/codingwithramin.com\/?p=644"},"modified":"2026-06-29T18:17:33","modified_gmt":"2026-06-29T18:17:33","slug":"building-a-conversational-work-iq-agent-in-c-the-a2a-sdk-multi-turn-and-streaming","status":"publish","type":"post","link":"https:\/\/codingwithramin.com\/?p=644","title":{"rendered":"Building a Conversational Work IQ Agent in C#: The A2A SDK, Multi-Turn, and Streaming"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">In <a href=\"https:\/\/codingwithramin.com\/?p=624\">Part 1<\/a> we proved the concept: register an Entra app, POST a JSON-RPC envelope to the Work IQ gateway, and get back a grounded, permission-trimmed answer with citations. About forty lines of code, no RAG pipeline, no permission logic.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">It worked \u2014 but it isn&#8217;t an agent. It was one synchronous question, with me hand-building the JSON-RPC envelope and hand-parsing the response, and the user staring at nothing until the whole answer was computed server-side. Three things are missing before this is something you&#8217;d actually ship:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>The envelope and parsing should be the SDK&#8217;s job, not mine.<\/strong> Hand-rolling JSON-RPC is fine to <em>see once<\/em>; it&#8217;s a liability to maintain.<\/li>\n\n\n\n<li><strong>The agent has no memory.<\/strong> Ask a follow-up like &#8220;tell me more about the 2 PM call&#8221; and Part 1&#8217;s code has no idea what call you mean \u2014 every request starts from zero.<\/li>\n\n\n\n<li><strong>There&#8217;s no streaming.<\/strong> Copilot-quality answers take a few seconds to assemble. Making the user wait in silence for all of it is a bad experience when you could stream tokens as they arrive.<\/li>\n<\/ol>\n\n\n\n<p class=\"wp-block-paragraph\">In this part we fix all three with the <strong>A2A .NET SDK<\/strong>: we&#8217;ll rebuild the call cleanly, add multi-turn conversations with contextId, and then upgrade to streaming, including the genuinely interesting bit, which is how Work IQ chunks its answer over the wire.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>The A2A SDK<\/strong><\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">A2A is an open protocol, and there&#8217;s a first-party .NET SDK for it on NuGet. It&#8217;s what Microsoft&#8217;s own Work IQ samples use, and it gives you typed <strong>Message<\/strong>, <strong>Task<\/strong>, and <strong>Artifact <\/strong>objects, the JSON-RPC plumbing, and \u2014 the reason we&#8217;re here \u2014 clean streaming primitives.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">One thing that doesn&#8217;t change from Part 1: <strong>you still bring your own authenticated HttpClient<\/strong>. The SDK handles the protocol; you handle the token and the <strong>A2A-Version<\/strong> header, then hand the client over. That&#8217;s actually a nice separation \u2014 your MSAL setup stays exactly as it was.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Set up the project:<\/p>\n\n\n\n<div class=\"wp-block-kevinbatdorf-code-block-pro\" data-code-block-pro-font-family=\"Code-Pro-JetBrains-Mono\" style=\"font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)\"><span style=\"display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#2b2b2b;color:#c7c7c7\">PowerShell<\/span><span role=\"button\" tabindex=\"0\" style=\"color:#D4D4D4;display:none\" aria-label=\"Copy\" class=\"code-block-pro-copy-button\"><pre class=\"code-block-pro-copy-button-pre\" aria-hidden=\"true\"><textarea class=\"code-block-pro-copy-button-textarea\" tabindex=\"-1\" aria-hidden=\"true\" readonly>dotnet new console -n Part2.ConversationalAgent\ncd Part2.ConversationalAgent\ndotnet add package A2A --prerelease\ndotnet add package Microsoft.Identity.Client<\/textarea><\/pre><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" style=\"width:24px;height:24px\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\"><path class=\"with-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4\"><\/path><path class=\"without-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2\"><\/path><\/svg><\/span><pre class=\"shiki dark-plus\" style=\"background-color: #1E1E1E\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color: #D4D4D4\">dotnet new console -n Part2.ConversationalAgent<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">cd Part2.ConversationalAgent<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">dotnet add package A2A --prerelease<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">dotnet add package Microsoft.Identity.Client<\/span><\/span><\/code><\/pre><\/div>\n\n\n\n<p class=\"wp-block-paragraph\">The A2A package is still prerelease at the time of writing (<strong>1.0.0-preview2<\/strong>), hence <strong>&#8211;prerelease<\/strong>. Here&#8217;s the .csproj so you can pin to exactly what I&#8217;m using:<\/p>\n\n\n\n<div class=\"wp-block-kevinbatdorf-code-block-pro\" data-code-block-pro-font-family=\"Code-Pro-JetBrains-Mono\" style=\"font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)\"><span style=\"display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#2b2b2b;color:#c7c7c7\">XML<\/span><span role=\"button\" tabindex=\"0\" style=\"color:#D4D4D4;display:none\" aria-label=\"Copy\" class=\"code-block-pro-copy-button\"><pre class=\"code-block-pro-copy-button-pre\" aria-hidden=\"true\"><textarea class=\"code-block-pro-copy-button-textarea\" tabindex=\"-1\" aria-hidden=\"true\" readonly>&lt;Project Sdk=\"Microsoft.NET.Sdk\">\n\n  &lt;PropertyGroup>\n    &lt;OutputType>Exe&lt;\/OutputType>\n    &lt;TargetFramework>net10.0&lt;\/TargetFramework>\n    &lt;ImplicitUsings>enable&lt;\/ImplicitUsings>\n    &lt;Nullable>enable&lt;\/Nullable>\n  &lt;\/PropertyGroup>\n\n  &lt;ItemGroup>\n    &lt;PackageReference Include=\"A2A\" Version=\"1.0.0-preview2\" \/>\n    &lt;PackageReference Include=\"Microsoft.Identity.Client\" Version=\"4.84.2\" \/>\n  &lt;\/ItemGroup>\n\n&lt;\/Project><\/textarea><\/pre><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" style=\"width:24px;height:24px\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\"><path class=\"with-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4\"><\/path><path class=\"without-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2\"><\/path><\/svg><\/span><pre class=\"shiki dark-plus\" style=\"background-color: #1E1E1E\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color: #808080\">&lt;<\/span><span style=\"color: #569CD6\">Project<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">Sdk<\/span><span style=\"color: #D4D4D4\">=<\/span><span style=\"color: #CE9178\">&quot;Microsoft.NET.Sdk&quot;<\/span><span style=\"color: #808080\">&gt;<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">  <\/span><span style=\"color: #808080\">&lt;<\/span><span style=\"color: #569CD6\">PropertyGroup<\/span><span style=\"color: #808080\">&gt;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">    <\/span><span style=\"color: #808080\">&lt;<\/span><span style=\"color: #569CD6\">OutputType<\/span><span style=\"color: #808080\">&gt;<\/span><span style=\"color: #D4D4D4\">Exe<\/span><span style=\"color: #808080\">&lt;\/<\/span><span style=\"color: #569CD6\">OutputType<\/span><span style=\"color: #808080\">&gt;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">    <\/span><span style=\"color: #808080\">&lt;<\/span><span style=\"color: #569CD6\">TargetFramework<\/span><span style=\"color: #808080\">&gt;<\/span><span style=\"color: #D4D4D4\">net10.0<\/span><span style=\"color: #808080\">&lt;\/<\/span><span style=\"color: #569CD6\">TargetFramework<\/span><span style=\"color: #808080\">&gt;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">    <\/span><span style=\"color: #808080\">&lt;<\/span><span style=\"color: #569CD6\">ImplicitUsings<\/span><span style=\"color: #808080\">&gt;<\/span><span style=\"color: #D4D4D4\">enable<\/span><span style=\"color: #808080\">&lt;\/<\/span><span style=\"color: #569CD6\">ImplicitUsings<\/span><span style=\"color: #808080\">&gt;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">    <\/span><span style=\"color: #808080\">&lt;<\/span><span style=\"color: #569CD6\">Nullable<\/span><span style=\"color: #808080\">&gt;<\/span><span style=\"color: #D4D4D4\">enable<\/span><span style=\"color: #808080\">&lt;\/<\/span><span style=\"color: #569CD6\">Nullable<\/span><span style=\"color: #808080\">&gt;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">  <\/span><span style=\"color: #808080\">&lt;\/<\/span><span style=\"color: #569CD6\">PropertyGroup<\/span><span style=\"color: #808080\">&gt;<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">  <\/span><span style=\"color: #808080\">&lt;<\/span><span style=\"color: #569CD6\">ItemGroup<\/span><span style=\"color: #808080\">&gt;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">    <\/span><span style=\"color: #808080\">&lt;<\/span><span style=\"color: #569CD6\">PackageReference<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">Include<\/span><span style=\"color: #D4D4D4\">=<\/span><span style=\"color: #CE9178\">&quot;A2A&quot;<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">Version<\/span><span style=\"color: #D4D4D4\">=<\/span><span style=\"color: #CE9178\">&quot;1.0.0-preview2&quot;<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #808080\">\/&gt;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">    <\/span><span style=\"color: #808080\">&lt;<\/span><span style=\"color: #569CD6\">PackageReference<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">Include<\/span><span style=\"color: #D4D4D4\">=<\/span><span style=\"color: #CE9178\">&quot;Microsoft.Identity.Client&quot;<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">Version<\/span><span style=\"color: #D4D4D4\">=<\/span><span style=\"color: #CE9178\">&quot;4.84.2&quot;<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #808080\">\/&gt;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">  <\/span><span style=\"color: #808080\">&lt;\/<\/span><span style=\"color: #569CD6\">ItemGroup<\/span><span style=\"color: #808080\">&gt;<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #808080\">&lt;\/<\/span><span style=\"color: #569CD6\">Project<\/span><span style=\"color: #808080\">&gt;<\/span><\/span><\/code><\/pre><\/div>\n\n\n\n<p class=\"wp-block-paragraph\"><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">A heads-up: because preview packages are fussy: if your environment has a custom NuGet feed configured, restore can fail to find <code>A2A<\/code>. Add a <code>nuget.config<\/code> next to the project that points at nuget.org and you&#8217;re set. Microsoft&#8217;s own sample ships one for exactly this reason.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Step 1: The same call, but with the SDK<\/strong><\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Let&#8217;s rebuild Part 1&#8217;s one-shot question, this time with the SDK. Notice how much disappears, no manual envelope, no JsonDocument spelunking:<\/p>\n\n\n\n<div class=\"wp-block-kevinbatdorf-code-block-pro\" data-code-block-pro-font-family=\"Code-Pro-JetBrains-Mono\" style=\"font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)\"><span style=\"display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#2b2b2b;color:#c7c7c7\">C#<\/span><span role=\"button\" tabindex=\"0\" style=\"color:#D4D4D4;display:none\" aria-label=\"Copy\" class=\"code-block-pro-copy-button\"><pre class=\"code-block-pro-copy-button-pre\" aria-hidden=\"true\"><textarea class=\"code-block-pro-copy-button-textarea\" tabindex=\"-1\" aria-hidden=\"true\" readonly>using System.Net.Http.Headers;\nusing System.Text.Json;\nusing A2A;\nusing Microsoft.Identity.Client;\n\nconst string ClientId = \"&lt;your-app-client-id>\";\nconst string TenantId = \"&lt;your-tenant-id>\";\nconst string Scope    = \"api:\/\/workiq.svc.cloud.microsoft\/.default\";\nconst string Endpoint = \"https:\/\/workiq.svc.cloud.microsoft\/a2a\/\";\n\n\/\/ --- Auth: identical to Part 1 ---\nvar app = PublicClientApplicationBuilder\n    .Create(ClientId)\n    .WithAuthority($\"https:\/\/login.microsoftonline.com\/{TenantId}\")\n    .WithDefaultRedirectUri()\n    .Build();\n\nvar auth = await app.AcquireTokenInteractive(new[] { Scope }).ExecuteAsync();\n\n\/\/ --- Bring your own authed HttpClient, then hand it to the SDK ---\nvar http = new HttpClient();\nhttp.DefaultRequestHeaders.Authorization =\n    new AuthenticationHeaderValue(\"Bearer\", auth.AccessToken);\n\/\/ Still required \u2014 opts in to the A2A v1.0 wire format (see Part 1).\nhttp.DefaultRequestHeaders.TryAddWithoutValidation(\"A2A-Version\", \"1.0\");\n\nvar client = new A2AClient(new Uri(Endpoint), http);\n\n\/\/ --- Build a typed message and send it ---\nvar message = new Message\n{\n    Role      = Role.User,\n    MessageId = Guid.NewGuid().ToString(),\n    Parts     = &#91;Part.FromText(\"What meetings do I have today?\")&#93;,\n    Metadata  = new Dictionary&lt;string, JsonElement>\n    {\n        &#91;\"Location\"&#93; = JsonSerializer.SerializeToElement(new\n        {\n            timeZoneOffset = (int)TimeZoneInfo.Local.BaseUtcOffset.TotalMinutes,\n            timeZone = TimeZoneInfo.Local.Id\n        })\n    }\n};\n\nvar response = await client.SendMessageAsync(new SendMessageRequest { Message = message });\n\n\/\/ The answer text lives in the task's artifacts.\nif (response.PayloadCase == SendMessageResponseCase.Task)\n{\n    var task = response.Task!;\n    var text = string.Join(\"\", task.Artifacts!\n        .SelectMany(a => a.Parts)\n        .Where(p => p.ContentCase == PartContentCase.Text)\n        .Select(p => p.Text));\n    Console.WriteLine(text);\n}<\/textarea><\/pre><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" style=\"width:24px;height:24px\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\"><path class=\"with-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4\"><\/path><path class=\"without-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2\"><\/path><\/svg><\/span><pre class=\"shiki dark-plus\" style=\"background-color: #1E1E1E\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color: #C586C0\">using<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #4EC9B0\">System<\/span><span style=\"color: #D4D4D4\">.<\/span><span style=\"color: #4EC9B0\">Net<\/span><span style=\"color: #D4D4D4\">.<\/span><span style=\"color: #4EC9B0\">Http<\/span><span style=\"color: #D4D4D4\">.<\/span><span style=\"color: #4EC9B0\">Headers<\/span><span style=\"color: #D4D4D4\">;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #C586C0\">using<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #4EC9B0\">System<\/span><span style=\"color: #D4D4D4\">.<\/span><span style=\"color: #4EC9B0\">Text<\/span><span style=\"color: #D4D4D4\">.<\/span><span style=\"color: #4EC9B0\">Json<\/span><span style=\"color: #D4D4D4\">;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #C586C0\">using<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #4EC9B0\">A2A<\/span><span style=\"color: #D4D4D4\">;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #C586C0\">using<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #4EC9B0\">Microsoft<\/span><span style=\"color: #D4D4D4\">.<\/span><span style=\"color: #4EC9B0\">Identity<\/span><span style=\"color: #D4D4D4\">.<\/span><span style=\"color: #4EC9B0\">Client<\/span><span style=\"color: #D4D4D4\">;<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #569CD6\">const<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #569CD6\">string<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">ClientId<\/span><span style=\"color: #D4D4D4\"> = <\/span><span style=\"color: #CE9178\">&quot;&lt;your-app-client-id&gt;&quot;<\/span><span style=\"color: #D4D4D4\">;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #569CD6\">const<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #569CD6\">string<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">TenantId<\/span><span style=\"color: #D4D4D4\"> = <\/span><span style=\"color: #CE9178\">&quot;&lt;your-tenant-id&gt;&quot;<\/span><span style=\"color: #D4D4D4\">;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #569CD6\">const<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #569CD6\">string<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">Scope<\/span><span style=\"color: #D4D4D4\">    = <\/span><span style=\"color: #CE9178\">&quot;api:\/\/workiq.svc.cloud.microsoft\/.default&quot;<\/span><span style=\"color: #D4D4D4\">;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #569CD6\">const<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #569CD6\">string<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">Endpoint<\/span><span style=\"color: #D4D4D4\"> = <\/span><span style=\"color: #CE9178\">&quot;https:\/\/workiq.svc.cloud.microsoft\/a2a\/&quot;<\/span><span style=\"color: #D4D4D4\">;<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #6A9955\">\/\/ --- Auth: identical to Part 1 ---<\/span><\/span>\n<span class=\"line\"><span style=\"color: #569CD6\">var<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">app<\/span><span style=\"color: #D4D4D4\"> = <\/span><span style=\"color: #9CDCFE\">PublicClientApplicationBuilder<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">    .<\/span><span style=\"color: #DCDCAA\">Create<\/span><span style=\"color: #D4D4D4\">(<\/span><span style=\"color: #9CDCFE\">ClientId<\/span><span style=\"color: #D4D4D4\">)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">    .<\/span><span style=\"color: #DCDCAA\">WithAuthority<\/span><span style=\"color: #D4D4D4\">(<\/span><span style=\"color: #CE9178\">$&quot;https:\/\/login.microsoftonline.com\/{<\/span><span style=\"color: #9CDCFE\">TenantId<\/span><span style=\"color: #CE9178\">}&quot;<\/span><span style=\"color: #D4D4D4\">)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">    .<\/span><span style=\"color: #DCDCAA\">WithDefaultRedirectUri<\/span><span style=\"color: #D4D4D4\">()<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">    .<\/span><span style=\"color: #DCDCAA\">Build<\/span><span style=\"color: #D4D4D4\">();<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #569CD6\">var<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">auth<\/span><span style=\"color: #D4D4D4\"> = <\/span><span style=\"color: #569CD6\">await<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">app<\/span><span style=\"color: #D4D4D4\">.<\/span><span style=\"color: #DCDCAA\">AcquireTokenInteractive<\/span><span style=\"color: #D4D4D4\">(<\/span><span style=\"color: #569CD6\">new<\/span><span style=\"color: #D4D4D4\">[] { <\/span><span style=\"color: #9CDCFE\">Scope<\/span><span style=\"color: #D4D4D4\"> }).<\/span><span style=\"color: #DCDCAA\">ExecuteAsync<\/span><span style=\"color: #D4D4D4\">();<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #6A9955\">\/\/ --- Bring your own authed HttpClient, then hand it to the SDK ---<\/span><\/span>\n<span class=\"line\"><span style=\"color: #569CD6\">var<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">http<\/span><span style=\"color: #D4D4D4\"> = <\/span><span style=\"color: #569CD6\">new<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #4EC9B0\">HttpClient<\/span><span style=\"color: #D4D4D4\">();<\/span><\/span>\n<span class=\"line\"><span style=\"color: #9CDCFE\">http<\/span><span style=\"color: #D4D4D4\">.<\/span><span style=\"color: #9CDCFE\">DefaultRequestHeaders<\/span><span style=\"color: #D4D4D4\">.<\/span><span style=\"color: #9CDCFE\">Authorization<\/span><span style=\"color: #D4D4D4\"> =<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">    <\/span><span style=\"color: #569CD6\">new<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #4EC9B0\">AuthenticationHeaderValue<\/span><span style=\"color: #D4D4D4\">(<\/span><span style=\"color: #CE9178\">&quot;Bearer&quot;<\/span><span style=\"color: #D4D4D4\">, <\/span><span style=\"color: #9CDCFE\">auth<\/span><span style=\"color: #D4D4D4\">.<\/span><span style=\"color: #9CDCFE\">AccessToken<\/span><span style=\"color: #D4D4D4\">);<\/span><\/span>\n<span class=\"line\"><span style=\"color: #6A9955\">\/\/ Still required \u2014 opts in to the A2A v1.0 wire format (see Part 1).<\/span><\/span>\n<span class=\"line\"><span style=\"color: #9CDCFE\">http<\/span><span style=\"color: #D4D4D4\">.<\/span><span style=\"color: #9CDCFE\">DefaultRequestHeaders<\/span><span style=\"color: #D4D4D4\">.<\/span><span style=\"color: #DCDCAA\">TryAddWithoutValidation<\/span><span style=\"color: #D4D4D4\">(<\/span><span style=\"color: #CE9178\">&quot;A2A-Version&quot;<\/span><span style=\"color: #D4D4D4\">, <\/span><span style=\"color: #CE9178\">&quot;1.0&quot;<\/span><span style=\"color: #D4D4D4\">);<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #569CD6\">var<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">client<\/span><span style=\"color: #D4D4D4\"> = <\/span><span style=\"color: #569CD6\">new<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #4EC9B0\">A2AClient<\/span><span style=\"color: #D4D4D4\">(<\/span><span style=\"color: #569CD6\">new<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #4EC9B0\">Uri<\/span><span style=\"color: #D4D4D4\">(<\/span><span style=\"color: #9CDCFE\">Endpoint<\/span><span style=\"color: #D4D4D4\">), <\/span><span style=\"color: #9CDCFE\">http<\/span><span style=\"color: #D4D4D4\">);<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #6A9955\">\/\/ --- Build a typed message and send it ---<\/span><\/span>\n<span class=\"line\"><span style=\"color: #569CD6\">var<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">message<\/span><span style=\"color: #D4D4D4\"> = <\/span><span style=\"color: #569CD6\">new<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #4EC9B0\">Message<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">{<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">    <\/span><span style=\"color: #9CDCFE\">Role<\/span><span style=\"color: #D4D4D4\">      = <\/span><span style=\"color: #9CDCFE\">Role<\/span><span style=\"color: #D4D4D4\">.<\/span><span style=\"color: #9CDCFE\">User<\/span><span style=\"color: #D4D4D4\">,<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">    <\/span><span style=\"color: #9CDCFE\">MessageId<\/span><span style=\"color: #D4D4D4\"> = <\/span><span style=\"color: #9CDCFE\">Guid<\/span><span style=\"color: #D4D4D4\">.<\/span><span style=\"color: #DCDCAA\">NewGuid<\/span><span style=\"color: #D4D4D4\">().<\/span><span style=\"color: #DCDCAA\">ToString<\/span><span style=\"color: #D4D4D4\">(),<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">    <\/span><span style=\"color: #9CDCFE\">Parts<\/span><span style=\"color: #D4D4D4\">     = &#91;<\/span><span style=\"color: #9CDCFE\">Part<\/span><span style=\"color: #D4D4D4\">.<\/span><span style=\"color: #DCDCAA\">FromText<\/span><span style=\"color: #D4D4D4\">(<\/span><span style=\"color: #CE9178\">&quot;What meetings do I have today?&quot;<\/span><span style=\"color: #D4D4D4\">)&#93;,<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">    <\/span><span style=\"color: #9CDCFE\">Metadata<\/span><span style=\"color: #D4D4D4\">  = <\/span><span style=\"color: #569CD6\">new<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #4EC9B0\">Dictionary<\/span><span style=\"color: #D4D4D4\">&lt;<\/span><span style=\"color: #569CD6\">string<\/span><span style=\"color: #D4D4D4\">, <\/span><span style=\"color: #4EC9B0\">JsonElement<\/span><span style=\"color: #D4D4D4\">&gt;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">    {<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">        &#91;<\/span><span style=\"color: #CE9178\">&quot;Location&quot;<\/span><span style=\"color: #D4D4D4\">&#93; = <\/span><span style=\"color: #9CDCFE\">JsonSerializer<\/span><span style=\"color: #D4D4D4\">.<\/span><span style=\"color: #DCDCAA\">SerializeToElement<\/span><span style=\"color: #D4D4D4\">(<\/span><span style=\"color: #569CD6\">new<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">        {<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">            <\/span><span style=\"color: #9CDCFE\">timeZoneOffset<\/span><span style=\"color: #D4D4D4\"> = (<\/span><span style=\"color: #569CD6\">int<\/span><span style=\"color: #D4D4D4\">)<\/span><span style=\"color: #9CDCFE\">TimeZoneInfo<\/span><span style=\"color: #D4D4D4\">.<\/span><span style=\"color: #9CDCFE\">Local<\/span><span style=\"color: #D4D4D4\">.<\/span><span style=\"color: #9CDCFE\">BaseUtcOffset<\/span><span style=\"color: #D4D4D4\">.<\/span><span style=\"color: #9CDCFE\">TotalMinutes<\/span><span style=\"color: #D4D4D4\">,<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">            <\/span><span style=\"color: #9CDCFE\">timeZone<\/span><span style=\"color: #D4D4D4\"> = <\/span><span style=\"color: #9CDCFE\">TimeZoneInfo<\/span><span style=\"color: #D4D4D4\">.<\/span><span style=\"color: #9CDCFE\">Local<\/span><span style=\"color: #D4D4D4\">.<\/span><span style=\"color: #9CDCFE\">Id<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">        })<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">    }<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">};<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #569CD6\">var<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">response<\/span><span style=\"color: #D4D4D4\"> = <\/span><span style=\"color: #569CD6\">await<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">client<\/span><span style=\"color: #D4D4D4\">.<\/span><span style=\"color: #DCDCAA\">SendMessageAsync<\/span><span style=\"color: #D4D4D4\">(<\/span><span style=\"color: #569CD6\">new<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #4EC9B0\">SendMessageRequest<\/span><span style=\"color: #D4D4D4\"> { <\/span><span style=\"color: #9CDCFE\">Message<\/span><span style=\"color: #D4D4D4\"> = <\/span><span style=\"color: #9CDCFE\">message<\/span><span style=\"color: #D4D4D4\"> });<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #6A9955\">\/\/ The answer text lives in the task&#39;s artifacts.<\/span><\/span>\n<span class=\"line\"><span style=\"color: #C586C0\">if<\/span><span style=\"color: #D4D4D4\"> (<\/span><span style=\"color: #9CDCFE\">response<\/span><span style=\"color: #D4D4D4\">.<\/span><span style=\"color: #9CDCFE\">PayloadCase<\/span><span style=\"color: #D4D4D4\"> == <\/span><span style=\"color: #9CDCFE\">SendMessageResponseCase<\/span><span style=\"color: #D4D4D4\">.<\/span><span style=\"color: #9CDCFE\">Task<\/span><span style=\"color: #D4D4D4\">)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">{<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">    <\/span><span style=\"color: #569CD6\">var<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">task<\/span><span style=\"color: #D4D4D4\"> = <\/span><span style=\"color: #9CDCFE\">response<\/span><span style=\"color: #D4D4D4\">.<\/span><span style=\"color: #9CDCFE\">Task<\/span><span style=\"color: #D4D4D4\">!;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">    <\/span><span style=\"color: #569CD6\">var<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">text<\/span><span style=\"color: #D4D4D4\"> = <\/span><span style=\"color: #569CD6\">string<\/span><span style=\"color: #D4D4D4\">.<\/span><span style=\"color: #DCDCAA\">Join<\/span><span style=\"color: #D4D4D4\">(<\/span><span style=\"color: #CE9178\">&quot;&quot;<\/span><span style=\"color: #D4D4D4\">, <\/span><span style=\"color: #9CDCFE\">task<\/span><span style=\"color: #D4D4D4\">.<\/span><span style=\"color: #9CDCFE\">Artifacts<\/span><span style=\"color: #D4D4D4\">!<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">        .<\/span><span style=\"color: #DCDCAA\">SelectMany<\/span><span style=\"color: #D4D4D4\">(<\/span><span style=\"color: #9CDCFE\">a<\/span><span style=\"color: #D4D4D4\"> =&gt; <\/span><span style=\"color: #9CDCFE\">a<\/span><span style=\"color: #D4D4D4\">.<\/span><span style=\"color: #9CDCFE\">Parts<\/span><span style=\"color: #D4D4D4\">)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">        .<\/span><span style=\"color: #DCDCAA\">Where<\/span><span style=\"color: #D4D4D4\">(<\/span><span style=\"color: #9CDCFE\">p<\/span><span style=\"color: #D4D4D4\"> =&gt; <\/span><span style=\"color: #9CDCFE\">p<\/span><span style=\"color: #D4D4D4\">.<\/span><span style=\"color: #9CDCFE\">ContentCase<\/span><span style=\"color: #D4D4D4\"> == <\/span><span style=\"color: #9CDCFE\">PartContentCase<\/span><span style=\"color: #D4D4D4\">.<\/span><span style=\"color: #9CDCFE\">Text<\/span><span style=\"color: #D4D4D4\">)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">        .<\/span><span style=\"color: #DCDCAA\">Select<\/span><span style=\"color: #D4D4D4\">(<\/span><span style=\"color: #9CDCFE\">p<\/span><span style=\"color: #D4D4D4\"> =&gt; <\/span><span style=\"color: #9CDCFE\">p<\/span><span style=\"color: #D4D4D4\">.<\/span><span style=\"color: #9CDCFE\">Text<\/span><span style=\"color: #D4D4D4\">));<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">    <\/span><span style=\"color: #9CDCFE\">Console<\/span><span style=\"color: #D4D4D4\">.<\/span><span style=\"color: #DCDCAA\">WriteLine<\/span><span style=\"color: #D4D4D4\">(<\/span><span style=\"color: #9CDCFE\">text<\/span><span style=\"color: #D4D4D4\">);<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">}<\/span><\/span><\/code><\/pre><\/div>\n\n\n\n<p class=\"wp-block-paragraph\">The shape to internalize here, because it carries into everything below:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>A SendMessageResponse has a PayloadCase,  usually <code>Task<\/code> for Work IQ. (The other case, Message, shows up in simpler direct replies.)<\/li>\n\n\n\n<li>The answer is in<strong> Task.Artifacts[].Parts<\/strong>, not in the task&#8217;s status. The status message carries progress and metadata \u2014 status text, citations \u2014 not the final text. Mixing those two up is the single most common parsing mistake, so it&#8217;s worth saying twice: artifacts hold the answer, status holds the commentary.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Step 2: Give it a memory<\/strong><\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Here&#8217;s the part that turns a query tool into a conversation. Every Work IQ response carries a contextId. Pass it back on your next message and Work IQ treats the exchange as one continuous thread \u2014 so &#8220;tell me more about the 2 PM call&#8221; resolves against what it just told you.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">That&#8217;s the whole mechanic: <strong>capture the contextId, send it on the next turn.<\/strong> Let&#8217;s wrap the call in a REPL that does exactly that:<\/p>\n\n\n\n<div class=\"wp-block-kevinbatdorf-code-block-pro\" data-code-block-pro-font-family=\"Code-Pro-JetBrains-Mono\" style=\"font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)\"><span style=\"display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#2b2b2b;color:#c7c7c7\">C#<\/span><span role=\"button\" tabindex=\"0\" style=\"color:#D4D4D4;display:none\" aria-label=\"Copy\" class=\"code-block-pro-copy-button\"><pre class=\"code-block-pro-copy-button-pre\" aria-hidden=\"true\"><textarea class=\"code-block-pro-copy-button-textarea\" tabindex=\"-1\" aria-hidden=\"true\" readonly>string? contextId = null;\nConsole.WriteLine(\"Ask Work IQ something ('quit' to exit).\\n\");\n\nwhile (true)\n{\n    Console.Write(\"You > \");\n    var input = Console.ReadLine();\n    if (string.IsNullOrWhiteSpace(input) ||\n        input.Equals(\"quit\", StringComparison.OrdinalIgnoreCase))\n        break;\n\n    var message = new Message\n    {\n        Role      = Role.User,\n        MessageId = Guid.NewGuid().ToString(),\n        ContextId = contextId,                 \/\/ null on turn 1, reused after\n        Parts     = &#91;Part.FromText(input)&#93;,\n        Metadata  = new Dictionary&lt;string, JsonElement>\n        {\n            &#91;\"Location\"&#93; = JsonSerializer.SerializeToElement(new\n            {\n                timeZoneOffset = (int)TimeZoneInfo.Local.BaseUtcOffset.TotalMinutes,\n                timeZone = TimeZoneInfo.Local.Id\n            })\n        }\n    };\n\n    var response = await client.SendMessageAsync(new SendMessageRequest { Message = message });\n    var task = response.Task!;\n\n    \/\/ Capture the contextId so the *next* turn continues this thread.\n    contextId = task.ContextId ?? contextId;\n\n    var text = string.Join(\"\", task.Artifacts!\n        .SelectMany(a => a.Parts)\n        .Where(p => p.ContentCase == PartContentCase.Text)\n        .Select(p => p.Text));\n    Console.WriteLine(text + \"\\n\");\n}<\/textarea><\/pre><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" style=\"width:24px;height:24px\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\"><path class=\"with-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4\"><\/path><path class=\"without-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2\"><\/path><\/svg><\/span><pre class=\"shiki dark-plus\" style=\"background-color: #1E1E1E\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color: #569CD6\">string<\/span><span style=\"color: #D4D4D4\">? <\/span><span style=\"color: #9CDCFE\">contextId<\/span><span style=\"color: #D4D4D4\"> = <\/span><span style=\"color: #569CD6\">null<\/span><span style=\"color: #D4D4D4\">;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #9CDCFE\">Console<\/span><span style=\"color: #D4D4D4\">.<\/span><span style=\"color: #DCDCAA\">WriteLine<\/span><span style=\"color: #D4D4D4\">(<\/span><span style=\"color: #CE9178\">&quot;Ask Work IQ something (&#39;quit&#39; to exit).<\/span><span style=\"color: #D7BA7D\">\\n<\/span><span style=\"color: #CE9178\">&quot;<\/span><span style=\"color: #D4D4D4\">);<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #C586C0\">while<\/span><span style=\"color: #D4D4D4\"> (<\/span><span style=\"color: #569CD6\">true<\/span><span style=\"color: #D4D4D4\">)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">{<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">    <\/span><span style=\"color: #9CDCFE\">Console<\/span><span style=\"color: #D4D4D4\">.<\/span><span style=\"color: #DCDCAA\">Write<\/span><span style=\"color: #D4D4D4\">(<\/span><span style=\"color: #CE9178\">&quot;You &gt; &quot;<\/span><span style=\"color: #D4D4D4\">);<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">    <\/span><span style=\"color: #569CD6\">var<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">input<\/span><span style=\"color: #D4D4D4\"> = <\/span><span style=\"color: #9CDCFE\">Console<\/span><span style=\"color: #D4D4D4\">.<\/span><span style=\"color: #DCDCAA\">ReadLine<\/span><span style=\"color: #D4D4D4\">();<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">    <\/span><span style=\"color: #C586C0\">if<\/span><span style=\"color: #D4D4D4\"> (<\/span><span style=\"color: #569CD6\">string<\/span><span style=\"color: #D4D4D4\">.<\/span><span style=\"color: #DCDCAA\">IsNullOrWhiteSpace<\/span><span style=\"color: #D4D4D4\">(<\/span><span style=\"color: #9CDCFE\">input<\/span><span style=\"color: #D4D4D4\">) ||<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">        <\/span><span style=\"color: #9CDCFE\">input<\/span><span style=\"color: #D4D4D4\">.<\/span><span style=\"color: #DCDCAA\">Equals<\/span><span style=\"color: #D4D4D4\">(<\/span><span style=\"color: #CE9178\">&quot;quit&quot;<\/span><span style=\"color: #D4D4D4\">, <\/span><span style=\"color: #9CDCFE\">StringComparison<\/span><span style=\"color: #D4D4D4\">.<\/span><span style=\"color: #9CDCFE\">OrdinalIgnoreCase<\/span><span style=\"color: #D4D4D4\">))<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">        <\/span><span style=\"color: #C586C0\">break<\/span><span style=\"color: #D4D4D4\">;<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">    <\/span><span style=\"color: #569CD6\">var<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">message<\/span><span style=\"color: #D4D4D4\"> = <\/span><span style=\"color: #569CD6\">new<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #4EC9B0\">Message<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">    {<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">        <\/span><span style=\"color: #9CDCFE\">Role<\/span><span style=\"color: #D4D4D4\">      = <\/span><span style=\"color: #9CDCFE\">Role<\/span><span style=\"color: #D4D4D4\">.<\/span><span style=\"color: #9CDCFE\">User<\/span><span style=\"color: #D4D4D4\">,<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">        <\/span><span style=\"color: #9CDCFE\">MessageId<\/span><span style=\"color: #D4D4D4\"> = <\/span><span style=\"color: #9CDCFE\">Guid<\/span><span style=\"color: #D4D4D4\">.<\/span><span style=\"color: #DCDCAA\">NewGuid<\/span><span style=\"color: #D4D4D4\">().<\/span><span style=\"color: #DCDCAA\">ToString<\/span><span style=\"color: #D4D4D4\">(),<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">        <\/span><span style=\"color: #9CDCFE\">ContextId<\/span><span style=\"color: #D4D4D4\"> = <\/span><span style=\"color: #9CDCFE\">contextId<\/span><span style=\"color: #D4D4D4\">,                 <\/span><span style=\"color: #6A9955\">\/\/ null on turn 1, reused after<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">        <\/span><span style=\"color: #9CDCFE\">Parts<\/span><span style=\"color: #D4D4D4\">     = &#91;<\/span><span style=\"color: #9CDCFE\">Part<\/span><span style=\"color: #D4D4D4\">.<\/span><span style=\"color: #DCDCAA\">FromText<\/span><span style=\"color: #D4D4D4\">(<\/span><span style=\"color: #9CDCFE\">input<\/span><span style=\"color: #D4D4D4\">)&#93;,<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">        <\/span><span style=\"color: #9CDCFE\">Metadata<\/span><span style=\"color: #D4D4D4\">  = <\/span><span style=\"color: #569CD6\">new<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #4EC9B0\">Dictionary<\/span><span style=\"color: #D4D4D4\">&lt;<\/span><span style=\"color: #569CD6\">string<\/span><span style=\"color: #D4D4D4\">, <\/span><span style=\"color: #4EC9B0\">JsonElement<\/span><span style=\"color: #D4D4D4\">&gt;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">        {<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">            &#91;<\/span><span style=\"color: #CE9178\">&quot;Location&quot;<\/span><span style=\"color: #D4D4D4\">&#93; = <\/span><span style=\"color: #9CDCFE\">JsonSerializer<\/span><span style=\"color: #D4D4D4\">.<\/span><span style=\"color: #DCDCAA\">SerializeToElement<\/span><span style=\"color: #D4D4D4\">(<\/span><span style=\"color: #569CD6\">new<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">            {<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">                <\/span><span style=\"color: #9CDCFE\">timeZoneOffset<\/span><span style=\"color: #D4D4D4\"> = (<\/span><span style=\"color: #569CD6\">int<\/span><span style=\"color: #D4D4D4\">)<\/span><span style=\"color: #9CDCFE\">TimeZoneInfo<\/span><span style=\"color: #D4D4D4\">.<\/span><span style=\"color: #9CDCFE\">Local<\/span><span style=\"color: #D4D4D4\">.<\/span><span style=\"color: #9CDCFE\">BaseUtcOffset<\/span><span style=\"color: #D4D4D4\">.<\/span><span style=\"color: #9CDCFE\">TotalMinutes<\/span><span style=\"color: #D4D4D4\">,<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">                <\/span><span style=\"color: #9CDCFE\">timeZone<\/span><span style=\"color: #D4D4D4\"> = <\/span><span style=\"color: #9CDCFE\">TimeZoneInfo<\/span><span style=\"color: #D4D4D4\">.<\/span><span style=\"color: #9CDCFE\">Local<\/span><span style=\"color: #D4D4D4\">.<\/span><span style=\"color: #9CDCFE\">Id<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">            })<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">        }<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">    };<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">    <\/span><span style=\"color: #569CD6\">var<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">response<\/span><span style=\"color: #D4D4D4\"> = <\/span><span style=\"color: #569CD6\">await<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">client<\/span><span style=\"color: #D4D4D4\">.<\/span><span style=\"color: #DCDCAA\">SendMessageAsync<\/span><span style=\"color: #D4D4D4\">(<\/span><span style=\"color: #569CD6\">new<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #4EC9B0\">SendMessageRequest<\/span><span style=\"color: #D4D4D4\"> { <\/span><span style=\"color: #9CDCFE\">Message<\/span><span style=\"color: #D4D4D4\"> = <\/span><span style=\"color: #9CDCFE\">message<\/span><span style=\"color: #D4D4D4\"> });<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">    <\/span><span style=\"color: #569CD6\">var<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">task<\/span><span style=\"color: #D4D4D4\"> = <\/span><span style=\"color: #9CDCFE\">response<\/span><span style=\"color: #D4D4D4\">.<\/span><span style=\"color: #9CDCFE\">Task<\/span><span style=\"color: #D4D4D4\">!;<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #6A9955\">    \/\/ Capture the contextId so the *next* turn continues this thread.<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">    <\/span><span style=\"color: #9CDCFE\">contextId<\/span><span style=\"color: #D4D4D4\"> = <\/span><span style=\"color: #9CDCFE\">task<\/span><span style=\"color: #D4D4D4\">.<\/span><span style=\"color: #9CDCFE\">ContextId<\/span><span style=\"color: #D4D4D4\"> ?? <\/span><span style=\"color: #9CDCFE\">contextId<\/span><span style=\"color: #D4D4D4\">;<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">    <\/span><span style=\"color: #569CD6\">var<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">text<\/span><span style=\"color: #D4D4D4\"> = <\/span><span style=\"color: #569CD6\">string<\/span><span style=\"color: #D4D4D4\">.<\/span><span style=\"color: #DCDCAA\">Join<\/span><span style=\"color: #D4D4D4\">(<\/span><span style=\"color: #CE9178\">&quot;&quot;<\/span><span style=\"color: #D4D4D4\">, <\/span><span style=\"color: #9CDCFE\">task<\/span><span style=\"color: #D4D4D4\">.<\/span><span style=\"color: #9CDCFE\">Artifacts<\/span><span style=\"color: #D4D4D4\">!<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">        .<\/span><span style=\"color: #DCDCAA\">SelectMany<\/span><span style=\"color: #D4D4D4\">(<\/span><span style=\"color: #9CDCFE\">a<\/span><span style=\"color: #D4D4D4\"> =&gt; <\/span><span style=\"color: #9CDCFE\">a<\/span><span style=\"color: #D4D4D4\">.<\/span><span style=\"color: #9CDCFE\">Parts<\/span><span style=\"color: #D4D4D4\">)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">        .<\/span><span style=\"color: #DCDCAA\">Where<\/span><span style=\"color: #D4D4D4\">(<\/span><span style=\"color: #9CDCFE\">p<\/span><span style=\"color: #D4D4D4\"> =&gt; <\/span><span style=\"color: #9CDCFE\">p<\/span><span style=\"color: #D4D4D4\">.<\/span><span style=\"color: #9CDCFE\">ContentCase<\/span><span style=\"color: #D4D4D4\"> == <\/span><span style=\"color: #9CDCFE\">PartContentCase<\/span><span style=\"color: #D4D4D4\">.<\/span><span style=\"color: #9CDCFE\">Text<\/span><span style=\"color: #D4D4D4\">)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">        .<\/span><span style=\"color: #DCDCAA\">Select<\/span><span style=\"color: #D4D4D4\">(<\/span><span style=\"color: #9CDCFE\">p<\/span><span style=\"color: #D4D4D4\"> =&gt; <\/span><span style=\"color: #9CDCFE\">p<\/span><span style=\"color: #D4D4D4\">.<\/span><span style=\"color: #9CDCFE\">Text<\/span><span style=\"color: #D4D4D4\">));<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">    <\/span><span style=\"color: #9CDCFE\">Console<\/span><span style=\"color: #D4D4D4\">.<\/span><span style=\"color: #DCDCAA\">WriteLine<\/span><span style=\"color: #D4D4D4\">(<\/span><span style=\"color: #9CDCFE\">text<\/span><span style=\"color: #D4D4D4\"> + <\/span><span style=\"color: #CE9178\">&quot;<\/span><span style=\"color: #D7BA7D\">\\n<\/span><span style=\"color: #CE9178\">&quot;<\/span><span style=\"color: #D4D4D4\">);<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">}<\/span><\/span><\/code><\/pre><\/div>\n\n\n\n<p class=\"wp-block-paragraph\">Run it and try a two-turn exchange:<\/p>\n\n\n\n<div class=\"wp-block-kevinbatdorf-code-block-pro\" data-code-block-pro-font-family=\"Code-Pro-JetBrains-Mono\" style=\"font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)\"><span style=\"display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#2b2b2b;color:#c7c7c7\">Bash<\/span><span role=\"button\" tabindex=\"0\" style=\"color:#D4D4D4;display:none\" aria-label=\"Copy\" class=\"code-block-pro-copy-button\"><pre class=\"code-block-pro-copy-button-pre\" aria-hidden=\"true\"><textarea class=\"code-block-pro-copy-button-textarea\" tabindex=\"-1\" aria-hidden=\"true\" readonly>You > What meetings do I have today?\nToday you have: 9 AM standup, 11 AM review with Dana, 2 PM customer call.\n\nYou > Who's organizing the 2 PM one, and what should I read before it?\nThe 2 PM customer call is organized by Priya Nair. Two threads are worth\nreading first: the \"Contoso renewal\" email chain from Tuesday and the draft\nSOW in the shared \"Contoso\" folder.<\/textarea><\/pre><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" style=\"width:24px;height:24px\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\"><path class=\"with-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4\"><\/path><path class=\"without-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2\"><\/path><\/svg><\/span><pre class=\"shiki dark-plus\" style=\"background-color: #1E1E1E\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color: #DCDCAA\">You<\/span><span style=\"color: #D4D4D4\"> &gt; <\/span><span style=\"color: #CE9178\">What<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #CE9178\">meetings<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #CE9178\">do<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #CE9178\">I<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #CE9178\">have<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #CE9178\">today?<\/span><\/span>\n<span class=\"line\"><span style=\"color: #DCDCAA\">Today<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #CE9178\">you<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #CE9178\">have:<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #B5CEA8\">9<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #CE9178\">AM<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #CE9178\">standup,<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #B5CEA8\">11<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #CE9178\">AM<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #CE9178\">review<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #CE9178\">with<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #CE9178\">Dana,<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #B5CEA8\">2<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #CE9178\">PM<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #CE9178\">customer<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #CE9178\">call.<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #DCDCAA\">You<\/span><span style=\"color: #D4D4D4\"> &gt; <\/span><span style=\"color: #CE9178\">Who&#39;s organizing the 2 PM one, and what should I read before it?<\/span><\/span>\n<span class=\"line\"><span style=\"color: #CE9178\">The 2 PM customer call is organized by Priya Nair. Two threads are worth<\/span><\/span>\n<span class=\"line\"><span style=\"color: #CE9178\">reading first: the &quot;Contoso renewal&quot; email chain from Tuesday and the draft<\/span><\/span>\n<span class=\"line\"><span style=\"color: #CE9178\">SOW in the shared &quot;Contoso&quot; folder.<\/span><\/span><\/code><\/pre><\/div>\n\n\n\n<p class=\"wp-block-paragraph\">The second answer only makes sense because Work IQ remembered the first. No history array, no re-sending the prior turns \u2014 contextId does it, server-side. If you&#8217;ve read my <a href=\"https:\/\/codingwithramin.com\/?p=592\">Foundry memory posts<\/a>, notice the contrast: there I wired up long-term memory myself; here the conversation state is just part of the protocol.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Step 3: Stream the response<\/strong><\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Synchronous calls are fine for a script. For anything a person waits on, you want streaming \u2014 partly so tokens appear as they&#8217;re generated, and partly because Work IQ streams <strong>progress updates<\/strong> (the &#8220;looking through your calendar\u2026&#8221; status lines) which make the wait feel like progress instead of a hang. Microsoft&#8217;s own sample code calls these intermediate messages <em>chain-of-thought<\/em>; I&#8217;ll stick to &#8220;progress&#8221; or &#8220;status&#8221; updates here, since that&#8217;s all we can observe from the outside.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Streaming swaps <strong>SendMessageAsync <\/strong>for <strong>SendStreamingMessageAsync<\/strong>, which returns an <strong>IAsyncEnumerable <\/strong>of events. Each event has a <strong>PayloadCase<\/strong>, and there are four you&#8217;ll see:<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><tbody><tr><td><strong>PayloadCase<\/strong><\/td><td><strong>What it is<\/strong><\/td><\/tr><tr><td>Task<\/td><td>The initial &#8220;I&#8217;ve accepted this&#8221; event. Informational \u2014 grab the contextId from it.<\/td><\/tr><tr><td>StatusUpdate<\/td><td>Progress \/ status lines, <strong>and<\/strong> the terminal state. The final one is where citation metadata currently arrives (see below).<\/td><\/tr><tr><td>ArtifactUpdate<\/td><td>The answer itself, streamed in chunks.<\/td><\/tr><tr><td>Message<\/td><td>A direct message reply (rare in this flow).<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\"><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The one subtlety worth slowing down for is how ArtifactUpdate chunks arrive. Each one has an <strong>Append <\/strong>flag:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Append = true<\/strong>: delta semantics. The chunk is the <em>new tail<\/em>; append it to what you&#8217;ve got.<\/li>\n\n\n\n<li><strong>Append = false<\/strong>: replace semantics. The chunk is the <em>full artifact so far<\/em>. Work IQ&#8217;s streaming sends each replacement as a strict extension of the previous one, so the right move is to print only the new suffix rather than re-rendering the whole thing.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Handle both and you get clean, flicker-free streaming. Here&#8217;s the loop:<\/p>\n\n\n\n<div class=\"wp-block-kevinbatdorf-code-block-pro\" data-code-block-pro-font-family=\"Code-Pro-JetBrains-Mono\" style=\"font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)\"><span style=\"display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#2b2b2b;color:#c7c7c7\">C#<\/span><span role=\"button\" tabindex=\"0\" style=\"color:#D4D4D4;display:none\" aria-label=\"Copy\" class=\"code-block-pro-copy-button\"><pre class=\"code-block-pro-copy-button-pre\" aria-hidden=\"true\"><textarea class=\"code-block-pro-copy-button-textarea\" tabindex=\"-1\" aria-hidden=\"true\" readonly>using System.Text;\n\nstatic async Task&lt;string?> StreamAnswer(A2AClient client, Message message)\n{\n    string? contextId = null;\n    var buffers = new Dictionary&lt;string, StringBuilder>();\n    Dictionary&lt;string, JsonElement>? finalMetadata = null;\n\n    await foreach (var evt in client.SendStreamingMessageAsync(\n        new SendMessageRequest { Message = message }))\n    {\n        switch (evt.PayloadCase)\n        {\n            case StreamResponseCase.Task:\n                contextId = evt.Task!.ContextId;\n                break;\n\n            case StreamResponseCase.StatusUpdate:\n                var status = evt.StatusUpdate!;\n                if (status.Status.Message is { } m)\n                {\n                    contextId     = m.ContextId ?? contextId;\n                    finalMetadata = m.Metadata;          \/\/ where citations currently arrive (terminal event)\n\n                    var thought = JoinText(m.Parts);\n                    if (!string.IsNullOrEmpty(thought))\n                        WriteDim($\"\\n  &#91;{thought}&#93;\");     \/\/ progress \/ status line\n                }\n                break;\n\n            case StreamResponseCase.ArtifactUpdate:\n                var au  = evt.ArtifactUpdate!;\n                var id  = au.Artifact.ArtifactId;\n                if (!buffers.TryGetValue(id, out var sb))\n                    buffers&#91;id&#93; = sb = new StringBuilder();\n\n                var chunk = JoinText(au.Artifact.Parts);\n\n                if (au.Append)\n                {\n                    sb.Append(chunk);\n                    Console.Write(chunk);                 \/\/ print the delta\n                }\n                else\n                {\n                    \/\/ Replace: each chunk is a prefix-extension \u2014 print only the new suffix.\n                    var old = sb.ToString();\n                    sb.Clear();\n                    sb.Append(chunk);\n                    Console.Write(chunk.StartsWith(old, StringComparison.Ordinal)\n                        ? chunk&#91;old.Length..&#93;\n                        : chunk);\n                }\n                break;\n        }\n    }\n\n    if (finalMetadata is not null) PrintCitations(finalMetadata);\n    return contextId;\n\n    static string JoinText(IEnumerable&lt;Part> parts) =>\n        string.Join(\"\", parts\n            .Where(p => p.ContentCase == PartContentCase.Text)\n            .Select(p => p.Text));\n}<\/textarea><\/pre><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" style=\"width:24px;height:24px\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\"><path class=\"with-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4\"><\/path><path class=\"without-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2\"><\/path><\/svg><\/span><pre class=\"shiki dark-plus\" style=\"background-color: #1E1E1E\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color: #C586C0\">using<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #4EC9B0\">System<\/span><span style=\"color: #D4D4D4\">.<\/span><span style=\"color: #4EC9B0\">Text<\/span><span style=\"color: #D4D4D4\">;<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #569CD6\">static<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #569CD6\">async<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #4EC9B0\">Task<\/span><span style=\"color: #D4D4D4\">&lt;<\/span><span style=\"color: #569CD6\">string<\/span><span style=\"color: #D4D4D4\">?&gt; <\/span><span style=\"color: #DCDCAA\">StreamAnswer<\/span><span style=\"color: #D4D4D4\">(<\/span><span style=\"color: #4EC9B0\">A2AClient<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">client<\/span><span style=\"color: #D4D4D4\">, <\/span><span style=\"color: #4EC9B0\">Message<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">message<\/span><span style=\"color: #D4D4D4\">)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">{<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">    <\/span><span style=\"color: #569CD6\">string<\/span><span style=\"color: #D4D4D4\">? <\/span><span style=\"color: #9CDCFE\">contextId<\/span><span style=\"color: #D4D4D4\"> = <\/span><span style=\"color: #569CD6\">null<\/span><span style=\"color: #D4D4D4\">;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">    <\/span><span style=\"color: #569CD6\">var<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">buffers<\/span><span style=\"color: #D4D4D4\"> = <\/span><span style=\"color: #569CD6\">new<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #4EC9B0\">Dictionary<\/span><span style=\"color: #D4D4D4\">&lt;<\/span><span style=\"color: #569CD6\">string<\/span><span style=\"color: #D4D4D4\">, <\/span><span style=\"color: #4EC9B0\">StringBuilder<\/span><span style=\"color: #D4D4D4\">&gt;();<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">    <\/span><span style=\"color: #4EC9B0\">Dictionary<\/span><span style=\"color: #D4D4D4\">&lt;<\/span><span style=\"color: #569CD6\">string<\/span><span style=\"color: #D4D4D4\">, <\/span><span style=\"color: #4EC9B0\">JsonElement<\/span><span style=\"color: #D4D4D4\">&gt;? <\/span><span style=\"color: #9CDCFE\">finalMetadata<\/span><span style=\"color: #D4D4D4\"> = <\/span><span style=\"color: #569CD6\">null<\/span><span style=\"color: #D4D4D4\">;<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">    <\/span><span style=\"color: #569CD6\">await<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #C586C0\">foreach<\/span><span style=\"color: #D4D4D4\"> (<\/span><span style=\"color: #569CD6\">var<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">evt<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #C586C0\">in<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">client<\/span><span style=\"color: #D4D4D4\">.<\/span><span style=\"color: #DCDCAA\">SendStreamingMessageAsync<\/span><span style=\"color: #D4D4D4\">(<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">        <\/span><span style=\"color: #569CD6\">new<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #4EC9B0\">SendMessageRequest<\/span><span style=\"color: #D4D4D4\"> { <\/span><span style=\"color: #9CDCFE\">Message<\/span><span style=\"color: #D4D4D4\"> = <\/span><span style=\"color: #9CDCFE\">message<\/span><span style=\"color: #D4D4D4\"> }))<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">    {<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">        <\/span><span style=\"color: #C586C0\">switch<\/span><span style=\"color: #D4D4D4\"> (<\/span><span style=\"color: #9CDCFE\">evt<\/span><span style=\"color: #D4D4D4\">.<\/span><span style=\"color: #9CDCFE\">PayloadCase<\/span><span style=\"color: #D4D4D4\">)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">        {<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">            <\/span><span style=\"color: #C586C0\">case<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #4EC9B0\">StreamResponseCase<\/span><span style=\"color: #D4D4D4\">.<\/span><span style=\"color: #4EC9B0\">Task<\/span><span style=\"color: #D4D4D4\">:<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">                <\/span><span style=\"color: #9CDCFE\">contextId<\/span><span style=\"color: #D4D4D4\"> = <\/span><span style=\"color: #9CDCFE\">evt<\/span><span style=\"color: #D4D4D4\">.<\/span><span style=\"color: #9CDCFE\">Task<\/span><span style=\"color: #D4D4D4\">!.<\/span><span style=\"color: #9CDCFE\">ContextId<\/span><span style=\"color: #D4D4D4\">;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">                <\/span><span style=\"color: #C586C0\">break<\/span><span style=\"color: #D4D4D4\">;<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">            <\/span><span style=\"color: #C586C0\">case<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #4EC9B0\">StreamResponseCase<\/span><span style=\"color: #D4D4D4\">.<\/span><span style=\"color: #4EC9B0\">StatusUpdate<\/span><span style=\"color: #D4D4D4\">:<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">                <\/span><span style=\"color: #569CD6\">var<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">status<\/span><span style=\"color: #D4D4D4\"> = <\/span><span style=\"color: #9CDCFE\">evt<\/span><span style=\"color: #D4D4D4\">.<\/span><span style=\"color: #9CDCFE\">StatusUpdate<\/span><span style=\"color: #D4D4D4\">!;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">                <\/span><span style=\"color: #C586C0\">if<\/span><span style=\"color: #D4D4D4\"> (<\/span><span style=\"color: #9CDCFE\">status<\/span><span style=\"color: #D4D4D4\">.<\/span><span style=\"color: #9CDCFE\">Status<\/span><span style=\"color: #D4D4D4\">.<\/span><span style=\"color: #9CDCFE\">Message<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #569CD6\">is<\/span><span style=\"color: #D4D4D4\"> { } <\/span><span style=\"color: #9CDCFE\">m<\/span><span style=\"color: #D4D4D4\">)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">                {<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">                    <\/span><span style=\"color: #9CDCFE\">contextId<\/span><span style=\"color: #D4D4D4\">     = <\/span><span style=\"color: #9CDCFE\">m<\/span><span style=\"color: #D4D4D4\">.<\/span><span style=\"color: #9CDCFE\">ContextId<\/span><span style=\"color: #D4D4D4\"> ?? <\/span><span style=\"color: #9CDCFE\">contextId<\/span><span style=\"color: #D4D4D4\">;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">                    <\/span><span style=\"color: #9CDCFE\">finalMetadata<\/span><span style=\"color: #D4D4D4\"> = <\/span><span style=\"color: #9CDCFE\">m<\/span><span style=\"color: #D4D4D4\">.<\/span><span style=\"color: #9CDCFE\">Metadata<\/span><span style=\"color: #D4D4D4\">;          <\/span><span style=\"color: #6A9955\">\/\/ where citations currently arrive (terminal event)<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">                    <\/span><span style=\"color: #569CD6\">var<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">thought<\/span><span style=\"color: #D4D4D4\"> = <\/span><span style=\"color: #DCDCAA\">JoinText<\/span><span style=\"color: #D4D4D4\">(<\/span><span style=\"color: #9CDCFE\">m<\/span><span style=\"color: #D4D4D4\">.<\/span><span style=\"color: #9CDCFE\">Parts<\/span><span style=\"color: #D4D4D4\">);<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">                    <\/span><span style=\"color: #C586C0\">if<\/span><span style=\"color: #D4D4D4\"> (!<\/span><span style=\"color: #569CD6\">string<\/span><span style=\"color: #D4D4D4\">.<\/span><span style=\"color: #DCDCAA\">IsNullOrEmpty<\/span><span style=\"color: #D4D4D4\">(<\/span><span style=\"color: #9CDCFE\">thought<\/span><span style=\"color: #D4D4D4\">))<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">                        <\/span><span style=\"color: #DCDCAA\">WriteDim<\/span><span style=\"color: #D4D4D4\">(<\/span><span style=\"color: #CE9178\">$&quot;<\/span><span style=\"color: #D7BA7D\">\\n<\/span><span style=\"color: #CE9178\">  &#91;{<\/span><span style=\"color: #9CDCFE\">thought<\/span><span style=\"color: #CE9178\">}&#93;&quot;<\/span><span style=\"color: #D4D4D4\">);     <\/span><span style=\"color: #6A9955\">\/\/ progress \/ status line<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">                }<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">                <\/span><span style=\"color: #C586C0\">break<\/span><span style=\"color: #D4D4D4\">;<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">            <\/span><span style=\"color: #C586C0\">case<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #4EC9B0\">StreamResponseCase<\/span><span style=\"color: #D4D4D4\">.<\/span><span style=\"color: #4EC9B0\">ArtifactUpdate<\/span><span style=\"color: #D4D4D4\">:<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">                <\/span><span style=\"color: #569CD6\">var<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">au<\/span><span style=\"color: #D4D4D4\">  = <\/span><span style=\"color: #9CDCFE\">evt<\/span><span style=\"color: #D4D4D4\">.<\/span><span style=\"color: #9CDCFE\">ArtifactUpdate<\/span><span style=\"color: #D4D4D4\">!;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">                <\/span><span style=\"color: #569CD6\">var<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">id<\/span><span style=\"color: #D4D4D4\">  = <\/span><span style=\"color: #9CDCFE\">au<\/span><span style=\"color: #D4D4D4\">.<\/span><span style=\"color: #9CDCFE\">Artifact<\/span><span style=\"color: #D4D4D4\">.<\/span><span style=\"color: #9CDCFE\">ArtifactId<\/span><span style=\"color: #D4D4D4\">;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">                <\/span><span style=\"color: #C586C0\">if<\/span><span style=\"color: #D4D4D4\"> (!<\/span><span style=\"color: #9CDCFE\">buffers<\/span><span style=\"color: #D4D4D4\">.<\/span><span style=\"color: #DCDCAA\">TryGetValue<\/span><span style=\"color: #D4D4D4\">(<\/span><span style=\"color: #9CDCFE\">id<\/span><span style=\"color: #D4D4D4\">, <\/span><span style=\"color: #569CD6\">out<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #569CD6\">var<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">sb<\/span><span style=\"color: #D4D4D4\">))<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">                    <\/span><span style=\"color: #9CDCFE\">buffers<\/span><span style=\"color: #D4D4D4\">&#91;<\/span><span style=\"color: #9CDCFE\">id<\/span><span style=\"color: #D4D4D4\">&#93; = <\/span><span style=\"color: #9CDCFE\">sb<\/span><span style=\"color: #D4D4D4\"> = <\/span><span style=\"color: #569CD6\">new<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #4EC9B0\">StringBuilder<\/span><span style=\"color: #D4D4D4\">();<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">                <\/span><span style=\"color: #569CD6\">var<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">chunk<\/span><span style=\"color: #D4D4D4\"> = <\/span><span style=\"color: #DCDCAA\">JoinText<\/span><span style=\"color: #D4D4D4\">(<\/span><span style=\"color: #9CDCFE\">au<\/span><span style=\"color: #D4D4D4\">.<\/span><span style=\"color: #9CDCFE\">Artifact<\/span><span style=\"color: #D4D4D4\">.<\/span><span style=\"color: #9CDCFE\">Parts<\/span><span style=\"color: #D4D4D4\">);<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">                <\/span><span style=\"color: #C586C0\">if<\/span><span style=\"color: #D4D4D4\"> (<\/span><span style=\"color: #9CDCFE\">au<\/span><span style=\"color: #D4D4D4\">.<\/span><span style=\"color: #9CDCFE\">Append<\/span><span style=\"color: #D4D4D4\">)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">                {<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">                    <\/span><span style=\"color: #9CDCFE\">sb<\/span><span style=\"color: #D4D4D4\">.<\/span><span style=\"color: #DCDCAA\">Append<\/span><span style=\"color: #D4D4D4\">(<\/span><span style=\"color: #9CDCFE\">chunk<\/span><span style=\"color: #D4D4D4\">);<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">                    <\/span><span style=\"color: #9CDCFE\">Console<\/span><span style=\"color: #D4D4D4\">.<\/span><span style=\"color: #DCDCAA\">Write<\/span><span style=\"color: #D4D4D4\">(<\/span><span style=\"color: #9CDCFE\">chunk<\/span><span style=\"color: #D4D4D4\">);                 <\/span><span style=\"color: #6A9955\">\/\/ print the delta<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">                }<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">                <\/span><span style=\"color: #C586C0\">else<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">                {<\/span><\/span>\n<span class=\"line\"><span style=\"color: #6A9955\">                    \/\/ Replace: each chunk is a prefix-extension \u2014 print only the new suffix.<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">                    <\/span><span style=\"color: #569CD6\">var<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">old<\/span><span style=\"color: #D4D4D4\"> = <\/span><span style=\"color: #9CDCFE\">sb<\/span><span style=\"color: #D4D4D4\">.<\/span><span style=\"color: #DCDCAA\">ToString<\/span><span style=\"color: #D4D4D4\">();<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">                    <\/span><span style=\"color: #9CDCFE\">sb<\/span><span style=\"color: #D4D4D4\">.<\/span><span style=\"color: #DCDCAA\">Clear<\/span><span style=\"color: #D4D4D4\">();<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">                    <\/span><span style=\"color: #9CDCFE\">sb<\/span><span style=\"color: #D4D4D4\">.<\/span><span style=\"color: #DCDCAA\">Append<\/span><span style=\"color: #D4D4D4\">(<\/span><span style=\"color: #9CDCFE\">chunk<\/span><span style=\"color: #D4D4D4\">);<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">                    <\/span><span style=\"color: #9CDCFE\">Console<\/span><span style=\"color: #D4D4D4\">.<\/span><span style=\"color: #DCDCAA\">Write<\/span><span style=\"color: #D4D4D4\">(<\/span><span style=\"color: #9CDCFE\">chunk<\/span><span style=\"color: #D4D4D4\">.<\/span><span style=\"color: #DCDCAA\">StartsWith<\/span><span style=\"color: #D4D4D4\">(<\/span><span style=\"color: #9CDCFE\">old<\/span><span style=\"color: #D4D4D4\">, <\/span><span style=\"color: #9CDCFE\">StringComparison<\/span><span style=\"color: #D4D4D4\">.<\/span><span style=\"color: #9CDCFE\">Ordinal<\/span><span style=\"color: #D4D4D4\">)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">                        ? <\/span><span style=\"color: #9CDCFE\">chunk<\/span><span style=\"color: #D4D4D4\">&#91;<\/span><span style=\"color: #9CDCFE\">old<\/span><span style=\"color: #D4D4D4\">.<\/span><span style=\"color: #9CDCFE\">Length<\/span><span style=\"color: #D4D4D4\">..&#93;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">                        : <\/span><span style=\"color: #9CDCFE\">chunk<\/span><span style=\"color: #D4D4D4\">);<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">                }<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">                <\/span><span style=\"color: #C586C0\">break<\/span><span style=\"color: #D4D4D4\">;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">        }<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">    }<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">    <\/span><span style=\"color: #C586C0\">if<\/span><span style=\"color: #D4D4D4\"> (<\/span><span style=\"color: #9CDCFE\">finalMetadata<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #569CD6\">is<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #569CD6\">not<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #569CD6\">null<\/span><span style=\"color: #D4D4D4\">) <\/span><span style=\"color: #DCDCAA\">PrintCitations<\/span><span style=\"color: #D4D4D4\">(<\/span><span style=\"color: #9CDCFE\">finalMetadata<\/span><span style=\"color: #D4D4D4\">);<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">    <\/span><span style=\"color: #C586C0\">return<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">contextId<\/span><span style=\"color: #D4D4D4\">;<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">    <\/span><span style=\"color: #569CD6\">static<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #569CD6\">string<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #DCDCAA\">JoinText<\/span><span style=\"color: #D4D4D4\">(<\/span><span style=\"color: #4EC9B0\">IEnumerable<\/span><span style=\"color: #D4D4D4\">&lt;<\/span><span style=\"color: #4EC9B0\">Part<\/span><span style=\"color: #D4D4D4\">&gt; <\/span><span style=\"color: #9CDCFE\">parts<\/span><span style=\"color: #D4D4D4\">) =&gt;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">        <\/span><span style=\"color: #569CD6\">string<\/span><span style=\"color: #D4D4D4\">.<\/span><span style=\"color: #DCDCAA\">Join<\/span><span style=\"color: #D4D4D4\">(<\/span><span style=\"color: #CE9178\">&quot;&quot;<\/span><span style=\"color: #D4D4D4\">, <\/span><span style=\"color: #9CDCFE\">parts<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">            .<\/span><span style=\"color: #DCDCAA\">Where<\/span><span style=\"color: #D4D4D4\">(<\/span><span style=\"color: #9CDCFE\">p<\/span><span style=\"color: #D4D4D4\"> =&gt; <\/span><span style=\"color: #9CDCFE\">p<\/span><span style=\"color: #D4D4D4\">.<\/span><span style=\"color: #9CDCFE\">ContentCase<\/span><span style=\"color: #D4D4D4\"> == <\/span><span style=\"color: #9CDCFE\">PartContentCase<\/span><span style=\"color: #D4D4D4\">.<\/span><span style=\"color: #9CDCFE\">Text<\/span><span style=\"color: #D4D4D4\">)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">            .<\/span><span style=\"color: #DCDCAA\">Select<\/span><span style=\"color: #D4D4D4\">(<\/span><span style=\"color: #9CDCFE\">p<\/span><span style=\"color: #D4D4D4\"> =&gt; <\/span><span style=\"color: #9CDCFE\">p<\/span><span style=\"color: #D4D4D4\">.<\/span><span style=\"color: #9CDCFE\">Text<\/span><span style=\"color: #D4D4D4\">));<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">}<\/span><\/span><\/code><\/pre><\/div>\n\n\n\n<p class=\"wp-block-paragraph\">Drop that into the REPL by replacing the body of the loop with:<\/p>\n\n\n\n<div class=\"wp-block-kevinbatdorf-code-block-pro\" data-code-block-pro-font-family=\"Code-Pro-JetBrains-Mono\" style=\"font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)\"><span style=\"display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#2b2b2b;color:#c7c7c7\">C#<\/span><span role=\"button\" tabindex=\"0\" style=\"color:#D4D4D4;display:none\" aria-label=\"Copy\" class=\"code-block-pro-copy-button\"><pre class=\"code-block-pro-copy-button-pre\" aria-hidden=\"true\"><textarea class=\"code-block-pro-copy-button-textarea\" tabindex=\"-1\" aria-hidden=\"true\" readonly>Console.Write(\"Agent > \");\ncontextId = await StreamAnswer(client, message);\nConsole.WriteLine(\"\\n\");<\/textarea><\/pre><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" style=\"width:24px;height:24px\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\"><path class=\"with-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4\"><\/path><path class=\"without-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2\"><\/path><\/svg><\/span><pre class=\"shiki dark-plus\" style=\"background-color: #1E1E1E\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color: #9CDCFE\">Console<\/span><span style=\"color: #D4D4D4\">.<\/span><span style=\"color: #DCDCAA\">Write<\/span><span style=\"color: #D4D4D4\">(<\/span><span style=\"color: #CE9178\">&quot;Agent &gt; &quot;<\/span><span style=\"color: #D4D4D4\">);<\/span><\/span>\n<span class=\"line\"><span style=\"color: #9CDCFE\">contextId<\/span><span style=\"color: #D4D4D4\"> = <\/span><span style=\"color: #569CD6\">await<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #DCDCAA\">StreamAnswer<\/span><span style=\"color: #D4D4D4\">(<\/span><span style=\"color: #9CDCFE\">client<\/span><span style=\"color: #D4D4D4\">, <\/span><span style=\"color: #9CDCFE\">message<\/span><span style=\"color: #D4D4D4\">);<\/span><\/span>\n<span class=\"line\"><span style=\"color: #9CDCFE\">Console<\/span><span style=\"color: #D4D4D4\">.<\/span><span style=\"color: #DCDCAA\">WriteLine<\/span><span style=\"color: #D4D4D4\">(<\/span><span style=\"color: #CE9178\">&quot;<\/span><span style=\"color: #D7BA7D\">\\n<\/span><span style=\"color: #CE9178\">&quot;<\/span><span style=\"color: #D4D4D4\">);<\/span><\/span><\/code><\/pre><\/div>\n\n\n\n<p class=\"wp-block-paragraph\">Now the experience is what you&#8217;d want: a dim status line or two while Work IQ assembles context, then the answer typing itself out in real time, then the sources.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Step 4: Citations (and a note on where they live)<\/strong><\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">The citation handling is the same idea as Part 1, but now we&#8217;re reading from the metadata we captured off the terminal StatusUpdate:<\/p>\n\n\n\n<div class=\"wp-block-kevinbatdorf-code-block-pro\" data-code-block-pro-font-family=\"Code-Pro-JetBrains-Mono\" style=\"font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)\"><span style=\"display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#2b2b2b;color:#c7c7c7\">C#<\/span><span role=\"button\" tabindex=\"0\" style=\"color:#D4D4D4;display:none\" aria-label=\"Copy\" class=\"code-block-pro-copy-button\"><pre class=\"code-block-pro-copy-button-pre\" aria-hidden=\"true\"><textarea class=\"code-block-pro-copy-button-textarea\" tabindex=\"-1\" aria-hidden=\"true\" readonly>static void PrintCitations(Dictionary&lt;string, JsonElement> metadata)\n{\n    if (!metadata.TryGetValue(\"attributions\", out var attrs) ||\n        attrs.ValueKind != JsonValueKind.Array)\n        return;\n\n    Console.WriteLine(\"\\n\\nSources:\");\n    foreach (var a in attrs.EnumerateArray())\n    {\n        var name = a.TryGetProperty(\"providerDisplayName\", out var n) ? n.GetString() : \"(source)\";\n        var url  = a.TryGetProperty(\"seeMoreWebUrl\", out var u) ? u.GetString() : \"\";\n        Console.WriteLine($\"  \u2022 {name}  {url}\");\n    }\n}\n\nstatic void WriteDim(string s)\n{\n    Console.ForegroundColor = ConsoleColor.DarkGray;\n    Console.WriteLine(s);\n    Console.ResetColor();\n}<\/textarea><\/pre><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" style=\"width:24px;height:24px\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\"><path class=\"with-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4\"><\/path><path class=\"without-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2\"><\/path><\/svg><\/span><pre class=\"shiki dark-plus\" style=\"background-color: #1E1E1E\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color: #569CD6\">static<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #569CD6\">void<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #DCDCAA\">PrintCitations<\/span><span style=\"color: #D4D4D4\">(<\/span><span style=\"color: #4EC9B0\">Dictionary<\/span><span style=\"color: #D4D4D4\">&lt;<\/span><span style=\"color: #569CD6\">string<\/span><span style=\"color: #D4D4D4\">, <\/span><span style=\"color: #4EC9B0\">JsonElement<\/span><span style=\"color: #D4D4D4\">&gt; <\/span><span style=\"color: #9CDCFE\">metadata<\/span><span style=\"color: #D4D4D4\">)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">{<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">    <\/span><span style=\"color: #C586C0\">if<\/span><span style=\"color: #D4D4D4\"> (!<\/span><span style=\"color: #9CDCFE\">metadata<\/span><span style=\"color: #D4D4D4\">.<\/span><span style=\"color: #DCDCAA\">TryGetValue<\/span><span style=\"color: #D4D4D4\">(<\/span><span style=\"color: #CE9178\">&quot;attributions&quot;<\/span><span style=\"color: #D4D4D4\">, <\/span><span style=\"color: #569CD6\">out<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #569CD6\">var<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">attrs<\/span><span style=\"color: #D4D4D4\">) ||<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">        <\/span><span style=\"color: #9CDCFE\">attrs<\/span><span style=\"color: #D4D4D4\">.<\/span><span style=\"color: #9CDCFE\">ValueKind<\/span><span style=\"color: #D4D4D4\"> != <\/span><span style=\"color: #9CDCFE\">JsonValueKind<\/span><span style=\"color: #D4D4D4\">.<\/span><span style=\"color: #9CDCFE\">Array<\/span><span style=\"color: #D4D4D4\">)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">        <\/span><span style=\"color: #C586C0\">return<\/span><span style=\"color: #D4D4D4\">;<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">    <\/span><span style=\"color: #9CDCFE\">Console<\/span><span style=\"color: #D4D4D4\">.<\/span><span style=\"color: #DCDCAA\">WriteLine<\/span><span style=\"color: #D4D4D4\">(<\/span><span style=\"color: #CE9178\">&quot;<\/span><span style=\"color: #D7BA7D\">\\n\\n<\/span><span style=\"color: #CE9178\">Sources:&quot;<\/span><span style=\"color: #D4D4D4\">);<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">    <\/span><span style=\"color: #C586C0\">foreach<\/span><span style=\"color: #D4D4D4\"> (<\/span><span style=\"color: #569CD6\">var<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">a<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #C586C0\">in<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">attrs<\/span><span style=\"color: #D4D4D4\">.<\/span><span style=\"color: #DCDCAA\">EnumerateArray<\/span><span style=\"color: #D4D4D4\">())<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">    {<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">        <\/span><span style=\"color: #569CD6\">var<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">name<\/span><span style=\"color: #D4D4D4\"> = <\/span><span style=\"color: #9CDCFE\">a<\/span><span style=\"color: #D4D4D4\">.<\/span><span style=\"color: #DCDCAA\">TryGetProperty<\/span><span style=\"color: #D4D4D4\">(<\/span><span style=\"color: #CE9178\">&quot;providerDisplayName&quot;<\/span><span style=\"color: #D4D4D4\">, <\/span><span style=\"color: #569CD6\">out<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #569CD6\">var<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">n<\/span><span style=\"color: #D4D4D4\">) ? <\/span><span style=\"color: #9CDCFE\">n<\/span><span style=\"color: #D4D4D4\">.<\/span><span style=\"color: #DCDCAA\">GetString<\/span><span style=\"color: #D4D4D4\">() : <\/span><span style=\"color: #CE9178\">&quot;(source)&quot;<\/span><span style=\"color: #D4D4D4\">;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">        <\/span><span style=\"color: #569CD6\">var<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">url<\/span><span style=\"color: #D4D4D4\">  = <\/span><span style=\"color: #9CDCFE\">a<\/span><span style=\"color: #D4D4D4\">.<\/span><span style=\"color: #DCDCAA\">TryGetProperty<\/span><span style=\"color: #D4D4D4\">(<\/span><span style=\"color: #CE9178\">&quot;seeMoreWebUrl&quot;<\/span><span style=\"color: #D4D4D4\">, <\/span><span style=\"color: #569CD6\">out<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #569CD6\">var<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">u<\/span><span style=\"color: #D4D4D4\">) ? <\/span><span style=\"color: #9CDCFE\">u<\/span><span style=\"color: #D4D4D4\">.<\/span><span style=\"color: #DCDCAA\">GetString<\/span><span style=\"color: #D4D4D4\">() : <\/span><span style=\"color: #CE9178\">&quot;&quot;<\/span><span style=\"color: #D4D4D4\">;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">        <\/span><span style=\"color: #9CDCFE\">Console<\/span><span style=\"color: #D4D4D4\">.<\/span><span style=\"color: #DCDCAA\">WriteLine<\/span><span style=\"color: #D4D4D4\">(<\/span><span style=\"color: #CE9178\">$&quot;  \u2022 {<\/span><span style=\"color: #9CDCFE\">name<\/span><span style=\"color: #CE9178\">}  {<\/span><span style=\"color: #9CDCFE\">url<\/span><span style=\"color: #CE9178\">}&quot;<\/span><span style=\"color: #D4D4D4\">);<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">    }<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">}<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #569CD6\">static<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #569CD6\">void<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #DCDCAA\">WriteDim<\/span><span style=\"color: #D4D4D4\">(<\/span><span style=\"color: #569CD6\">string<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">s<\/span><span style=\"color: #D4D4D4\">)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">{<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">    <\/span><span style=\"color: #9CDCFE\">Console<\/span><span style=\"color: #D4D4D4\">.<\/span><span style=\"color: #9CDCFE\">ForegroundColor<\/span><span style=\"color: #D4D4D4\"> = <\/span><span style=\"color: #9CDCFE\">ConsoleColor<\/span><span style=\"color: #D4D4D4\">.<\/span><span style=\"color: #9CDCFE\">DarkGray<\/span><span style=\"color: #D4D4D4\">;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">    <\/span><span style=\"color: #9CDCFE\">Console<\/span><span style=\"color: #D4D4D4\">.<\/span><span style=\"color: #DCDCAA\">WriteLine<\/span><span style=\"color: #D4D4D4\">(<\/span><span style=\"color: #9CDCFE\">s<\/span><span style=\"color: #D4D4D4\">);<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">    <\/span><span style=\"color: #9CDCFE\">Console<\/span><span style=\"color: #D4D4D4\">.<\/span><span style=\"color: #DCDCAA\">ResetColor<\/span><span style=\"color: #D4D4D4\">();<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">}<\/span><\/span><\/code><\/pre><\/div>\n\n\n\n<p class=\"wp-block-paragraph\">One forward-looking caveat, and I&#8217;m flagging it because it&#8217;ll save you a debugging session later: as of now, citations ride along in Status.Message.Metadata under attributions. I&#8217;m not just going off my own testing here \u2014 Microsoft&#8217;s <a href=\"https:\/\/github.com\/microsoft\/work-iq-samples\">upstream Work IQ samples<\/a> say so in a code comment: the attributions are <em>&#8220;still in Status.Message.Metadata until DataPart migration ships.&#8221;<\/em> Once that migration lands, you&#8217;ll read them off a structured DataPart instead of fishing them out of metadata. The shape of <em>what<\/em> you get (provider, URL, type) shouldn&#8217;t change; <em>where<\/em> you read it from will. So if a future SDK bump makes this come back null, that migration is your most likely culprit \u2014 check the artifact parts.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Chat vs Context (a quick aside)<\/strong><\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Everything we&#8217;ve done lives in Work IQ&#8217;s <strong>Chat<\/strong> domain: you ask, Work IQ reasons, you get a synthesized, cited answer. There&#8217;s a sibling worth knowing about \u2014 <strong>Context<\/strong> \u2014 which returns the <em>raw grounding<\/em> Work IQ would have used, packaged for your agent to reason over itself, rather than a finished answer. Chat is &#8220;let Copilot answer this&#8221;; Context is &#8220;give my agent the relevant material and let it decide.&#8221; For a lot of agent designs you&#8217;ll mix them: Context to gather, your own model to synthesize. I&#8217;ll come back to this when it earns its own demo.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Where this is going<\/strong><\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">We now have a real conversational agent: typed SDK calls, memory across turns via contextId, streaming output with visible progress updates, and citations \u2014 all still permission-trimmed to the signed-in user, still with zero retrieval code on my side.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">In <strong>Part 3<\/strong> we leave A2A behind and pick up the <strong>Work IQ MCP<\/strong> \u2014 the redesigned remote server that collapses hundreds of Microsoft 365 operations into about ten generic tool verbs (fetch, create, update, and friends), with a getSchema call that lets an agent discover how data is shaped at runtime. That&#8217;s a different and, in some ways, more powerful way to give an agent access to M365 \u2014 and it&#8217;s the on-ramp if you&#8217;re wiring Work IQ into Copilot Studio or a Foundry agent rather than writing the client yourself.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Code for this part is in <a href=\"https:\/\/github.com\/AhmadiRamin\/work-iq-samples\">Part2.ConversationalAgent<\/a> in the series repo. Next up: Work IQ as MCP tools.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>In Part 1 we proved the concept: register an Entra app, POST a JSON-RPC envelope to the Work IQ gateway, and get back a grounded, permission-trimmed answer with citations. About forty lines of code, no RAG pipeline, no permission logic. It worked \u2014 but it isn&#8217;t an agent. It was one synchronous question, with me hand-building the JSON-RPC envelope and hand-parsing the response, and the user staring at nothing until the whole answer was computed&hellip;<\/p>\n","protected":false},"author":1,"featured_media":653,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[55,72,65,5,77],"tags":[49,9,59,78],"class_list":["post-644","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-ai","category-ai-agent","category-azure-ai-foundry","category-microsoft-azure","category-work-iq","tag-ai","tag-azure","tag-azure-ai-agent","tag-work-iq"],"_links":{"self":[{"href":"https:\/\/codingwithramin.com\/index.php?rest_route=\/wp\/v2\/posts\/644","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/codingwithramin.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/codingwithramin.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/codingwithramin.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/codingwithramin.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=644"}],"version-history":[{"count":8,"href":"https:\/\/codingwithramin.com\/index.php?rest_route=\/wp\/v2\/posts\/644\/revisions"}],"predecessor-version":[{"id":652,"href":"https:\/\/codingwithramin.com\/index.php?rest_route=\/wp\/v2\/posts\/644\/revisions\/652"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/codingwithramin.com\/index.php?rest_route=\/wp\/v2\/media\/653"}],"wp:attachment":[{"href":"https:\/\/codingwithramin.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=644"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/codingwithramin.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=644"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/codingwithramin.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=644"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}