You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
fix(tools): never drop the sub-agent 🔑 notice — wrap every return through one helper
The "🔑 Your agent_id is: ..." notice is the only signal a freshly
allocated sub-agent has for learning its own ID. It used to be emitted
inline by:
- GetCurrentLocation success path (line ~192)
- InvokeExpression case "success" branch (only when startupNotice was set)
- StartConsole new + reuse paths
Every other return path forgot to add it. Sub-agents could lose their
allocated ID forever if their first call landed on:
- GetCurrentLocation with no ready pipe — delegated to StartConsole
with the already-allocated agent_id, so the inner ResolveAgentId
saw isNewlyAllocated=false and the inner success path stayed silent.
- InvokeExpression cases "timeout", "Completed" (cached), "error" —
branches built their own response and never appended the notice.
- InvokeExpression busy auto-route — recurses with is_subagent:false,
inner stays silent, outer''s busyResponse never adds the notice.
- Any drift bail / var1 enforcement / pipe-error early return.
Fix: add a single PrependAgentIdNoticeIfNew helper and a per-method
local Wrap function, then route every return through Wrap. The helper
is a no-op when isNewlyAllocated=false, so the common path
(is_subagent=false or already-known agent_id) is unchanged. Inline
🔑 emissions removed from the three legacy sites so we don''t
double-up.
Trade-off: the notice now appears at the very top of the response
instead of being interleaved with the startupNotice block. AI parses
the same text either way; emit-from-one-place is what makes future
branches automatically correct.
4 unit tests added: success-path notice (regression guard),
error-path notice (the previously-broken case), default-agent silence,
provided-agent-id silence.
Build clean. xunit: net8 162/162, net9 275/275 (+4 from this change).
Note: Unit/Proxy/** is net9-only by csproj convention because the
Proxy project itself is net9-only.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
return$"🔑 Your agent_id is: {agentId} — pass this in all subsequent tool calls.\n\n{body}";
125
+
}
126
+
113
127
[McpServerTool]
114
128
[Description("Retrieves the current location and all available drives (providers) from the PowerShell session. Returns current_location and other_drive_locations array. Call this when you need to understand the current PowerShell context, as users may change location during the session. When executing multiple invoke_expression commands in succession, calling once at the beginning is sufficient.")]
115
129
publicstaticasyncTask<string>GetCurrentLocation(
@@ -122,17 +136,24 @@ public static async Task<string> GetCurrentLocation(
// Cwd drift detection: if the user typed `cd` in the visible console
@@ -360,7 +379,7 @@ public static async Task<string> InvokeExpression(
360
379
}
361
380
bailResponse.AppendLine($"ℹ️ User changed cwd in console {driftConsoleName} from '{drift.Value.AiCwd}' to '{drift.Value.LiveCwd}'.");
362
381
bailResponse.AppendLine($"Pipeline NOT executed. Re-issue to run at '{drift.Value.LiveCwd}', or prepend `Set-Location -LiteralPath '{drift.Value.AiCwd.Replace("'","''")}';` to revert.");
363
-
returnbailResponse.ToString();
382
+
returnWrap(bailResponse.ToString());
364
383
}
365
384
366
385
// Execute the command
@@ -418,7 +437,7 @@ public static async Task<string> InvokeExpression(
@@ -465,7 +484,7 @@ public static async Task<string> InvokeExpression(
465
484
busyResponse.AppendLine($"ℹ️ Auto-routed to {newConsoleName} at {startLoc} (source console busy with {jsonResponse.Reason}). Pipeline executed automatically — no re-send needed.");
466
485
busyResponse.AppendLine();
467
486
busyResponse.Append(retryResult);
468
-
returnbusyResponse.ToString();
487
+
returnWrap(busyResponse.ToString());
469
488
}
470
489
break;
471
490
@@ -522,7 +541,7 @@ public static async Task<string> InvokeExpression(
522
541
timeoutResponse.AppendLine();
523
542
timeoutResponse.AppendLine(scopeWarning);
524
543
}
525
-
returntimeoutResponse.ToString();
544
+
returnWrap(timeoutResponse.ToString());
526
545
527
546
casePipeStatus.Completed:
528
547
// Snapshot AI-intended cwd from the cached completion.
@@ -589,10 +608,10 @@ public static async Task<string> InvokeExpression(
589
608
{
590
609
cachedResponse.Append("Result cached. Will be returned on next tool call.");
591
610
}
592
-
returncachedResponse.ToString();
611
+
returnWrap(cachedResponse.ToString());
593
612
594
613
case"error":
595
-
returnjsonResponse.Message??$"Error from PowerShell.MCP module: {jsonResponse.Error}";
614
+
returnWrap(jsonResponse.Message??$"Error from PowerShell.MCP module: {jsonResponse.Error}");
596
615
597
616
case"success":
598
617
// Snapshot AI-intended cwd at successful completion. This
@@ -632,10 +651,6 @@ public static async Task<string> InvokeExpression(
632
651
if(!string.IsNullOrEmpty(startupNotice))
633
652
{
634
653
successResponse.AppendLine(startupNotice);
635
-
if(isNewlyAllocated)
636
-
{
637
-
successResponse.AppendLine($"🔑 Your agent_id is: {agentId} — pass this in all subsequent tool calls.");
638
-
}
639
654
successResponse.AppendLine();
640
655
}
641
656
if(completedOutput.Length>0)
@@ -670,7 +685,7 @@ public static async Task<string> InvokeExpression(
670
685
// successResponse.AppendLine();
671
686
// successResponse.AppendLine(jsonHint);
672
687
// }
673
-
returnsuccessResponse.ToString();
688
+
returnWrap(successResponse.ToString());
674
689
}
675
690
}
676
691
}
@@ -681,7 +696,7 @@ public static async Task<string> InvokeExpression(
681
696
}
682
697
683
698
// Fallback: return result as-is (shouldn't happen with new DLL)
684
-
returnresult;
699
+
returnWrap(result);
685
700
}
686
701
catch(Exceptionex)
687
702
{
@@ -692,7 +707,7 @@ public static async Task<string> InvokeExpression(
varclosedMessages=newList<string>{$" - ⚠ Console {consoleName} was closed"};
694
709
closedMessages.AddRange(otherClosed);
695
-
return$"Command execution failed: {ex.Message}\n{string.Join("\n",closedMessages)}\nPlease try again. A new console will be started automatically if needed.";
710
+
returnWrap($"Command execution failed: {ex.Message}\n{string.Join("\n",closedMessages)}\nPlease try again. A new console will be started automatically if needed.");
696
711
}
697
712
}
698
713
@@ -909,8 +924,11 @@ public static async Task<string> StartConsole(
reuseResponse.AppendLine("ℹ️ Did not launch a new console. An existing standby console is available and will be reused. To force a new console, provide the reason parameter.");
972
-
if(isNewlyAllocated)
973
-
{
974
-
reuseResponse.AppendLine($"🔑 Your agent_id is: {agentId} — pass this in all subsequent tool calls.");
975
-
}
976
990
reuseResponse.AppendLine();
977
991
reuseResponse.Append(reuseLocationResult);
978
-
returnreuseResponse.ToString();
992
+
returnWrap(reuseResponse.ToString());
979
993
}
980
994
// No standby console found, fall through to create a new one
0 commit comments