@mono-agent/agent-runtime 0.18.0 → 0.18.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -18,10 +18,28 @@ import {
18
18
  claudeSandboxCapabilityMismatchResult,
19
19
  claudeSandboxPolicyProblem,
20
20
  } from "./claude-sandbox.js";
21
+ import { createClaudeSubagentActivityNormalizer } from "./claude-subagent-activity.js";
21
22
 
22
23
  const CLAUDE_EFFORT_LEVELS = new Set(["low", "medium", "high", "xhigh", "max"]);
24
+ const CLAUDE_SETTING_SOURCES = new Set(["user", "project", "local"]);
23
25
  const MAX_CLAUDE_ERROR_CHARS = 2_000;
24
26
 
27
+ /**
28
+ * Filesystem settings the Agent SDK may load for this run. The SDK's own default
29
+ * is "load nothing", and mono-agent keeps that default so a hosted run stays
30
+ * reproducible: no host CLAUDE.md, hooks, or plugins leak in unless the caller
31
+ * asks for them. A host that *wants* on-disk discovery — typically to reach
32
+ * `.claude/agents` so the native `Task` tool has profiles to deploy — opts in per
33
+ * run. Unrecognized entries are dropped rather than forwarded, so a typo cannot
34
+ * silently widen what the SDK reads off disk.
35
+ * @param {unknown} value
36
+ * @returns {Array<"user" | "project" | "local">}
37
+ */
38
+ function normalizeClaudeSettingSources(value) {
39
+ if (!Array.isArray(value)) return [];
40
+ return Array.from(new Set(value.filter((entry) => CLAUDE_SETTING_SOURCES.has(entry))));
41
+ }
42
+
25
43
  /**
26
44
  * Preserve the provider default when effort is omitted. The current Agent SDK
27
45
  * public effort contract accepts the five values below. Its shipped JavaScript
@@ -652,6 +670,14 @@ export async function generateClaudeResponse(systemPrompt, options) {
652
670
  onEvent(event);
653
671
  }
654
672
 
673
+ const subagentNormalizer = createClaudeSubagentActivityNormalizer();
674
+ function emitSubagentEvents(activityEvents) {
675
+ for (const activity of activityEvents) emitEvent(activity);
676
+ }
677
+ function drainSubagents(reason) {
678
+ emitSubagentEvents(subagentNormalizer.drain(reason));
679
+ }
680
+
655
681
  // Deprecated `settings` fallback for the tool_result byte cap was consumed;
656
682
  // surface the one-per-run deprecation warning (the typed `toolLimits` object
657
683
  // is the supported path). mono-agent never passes `settings`, so this never
@@ -716,7 +742,11 @@ export async function generateClaudeResponse(systemPrompt, options) {
716
742
  disallowedTools,
717
743
  mcpServers: mcpServers || {},
718
744
  strictMcpConfig: true,
719
- settingSources: [],
745
+ settingSources: normalizeClaudeSettingSources(options.settingSources),
746
+ // The SDK otherwise forwards child tool frames but suppresses child prose.
747
+ // Request it explicitly so the live normalizer can preserve the complete
748
+ // nested transcript without ever merging it into the parent answer.
749
+ forwardSubagentText: true,
720
750
  env: createClaudeSdkEnvironment(options.env, options.providerEnv),
721
751
  abortController: internalAbortController,
722
752
  ...(disposableSession ? { persistSession: false } : options.persistSession === true ? { persistSession: true } : {}),
@@ -797,6 +827,7 @@ export async function generateClaudeResponse(systemPrompt, options) {
797
827
 
798
828
  const abortHandler = () => {
799
829
  cancelled = true;
830
+ drainSubagents("subagent cancelled with the parent run");
800
831
  internalAbortController.abort();
801
832
  try { stream?.close?.(); } catch { /* best effort; finally closes again */ }
802
833
  };
@@ -807,9 +838,21 @@ export async function generateClaudeResponse(systemPrompt, options) {
807
838
 
808
839
  try {
809
840
  stream = claudeAgentQuery({ prompt: /** @type {any} */ (prompt), options: queryOptions });
810
- for await (const event of stream) {
811
- const nextSessionId = sessionIdFromEvent(event);
841
+ for await (const rawEvent of stream) {
842
+ const nextSessionId = sessionIdFromEvent(rawEvent);
812
843
  if (nextSessionId) providerSessionId = nextSessionId;
844
+ const observation = subagentNormalizer.observe(rawEvent);
845
+ emitSubagentEvents(observation.events);
846
+ // Child records are represented exclusively as subagent_activity. In
847
+ // particular, their text, errors, tools, usage, and structured output
848
+ // must never mutate the parent's result state below.
849
+ if (observation.consumed) {
850
+ if (cancelled) break;
851
+ continue;
852
+ }
853
+ // Preserve unrelated blocks when a root user message batches them with a
854
+ // background Agent launch acknowledgement.
855
+ const event = /** @type {any} */ (observation.forwarded ?? rawEvent);
813
856
  emitEvent(event);
814
857
  if (event?.type === "tool_progress" && event.tool_name) noteToolUse(event.tool_name);
815
858
  for (const toolUse of structuredOutputToolUses(event)) {
@@ -962,6 +1005,11 @@ export async function generateClaudeResponse(systemPrompt, options) {
962
1005
  }
963
1006
  }
964
1007
  } finally {
1008
+ drainSubagents(cancelled
1009
+ ? "subagent cancelled with the parent run"
1010
+ : errorMessage || structuredTerminalFailure
1011
+ ? "subagent stopped because the Claude SDK stream failed"
1012
+ : "subagent stream closed before completion");
965
1013
  try { stream?.close?.(); } catch { /* best effort after every terminal path */ }
966
1014
  if (abortSignal) abortSignal.removeEventListener?.("abort", abortHandler);
967
1015
  }
@@ -1024,19 +1072,14 @@ export async function generateClaudeResponse(systemPrompt, options) {
1024
1072
  },
1025
1073
  });
1026
1074
 
1027
- const configuredSubagents = Array.isArray(options.nativeSubagents?.teammates)
1028
- ? options.nativeSubagents.teammates.map((entry) => entry?.name).filter(Boolean)
1029
- : [];
1075
+ const observedSubagents = subagentNormalizer.nativeSubagentsUsed();
1030
1076
  const capabilitiesUsed = buildCapabilitiesUsed({
1031
1077
  promptCacheActive: cachedTokens > 0 || cacheCreationTokens > 0,
1032
1078
  thinkingEnabled: thinkingObserved ? true : null,
1033
1079
  structuredOutputEnforced: !!options.outputSchema,
1034
- // Claude SDK doesn't surface a per-call "subagent was invoked" signal,
1035
- // so we report null when subagents were configured (unknown) and false
1036
- // when none were configured (definitely not).
1037
- subagentInvoked: configuredSubagents.length > 0 ? null : false,
1080
+ subagentInvoked: subagentNormalizer.subagentInvoked(),
1038
1081
  mcpServersUsed: Object.keys(mcpServers || {}),
1039
- nativeSubagentsUsed: configuredSubagents,
1082
+ nativeSubagentsUsed: observedSubagents,
1040
1083
  toolCompactionApplied: toolCompactionAppliedFromWarnings(runtimeWarnings),
1041
1084
  contextCompactionApplied: null, // Claude SDK doesn't use the runtime compaction layer
1042
1085
  });