@standardagents/builder 0.17.0 → 0.17.1

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.
package/dist/runtime.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { e as ThreadEnv, h as ThreadMetadata, M as Message, l as FileRecord, F as FlowState, n as FileStats, G as GrepResult, m as AttachmentRef, r as MessageContent, E as Env, g as ThreadInstance } from './index-BnrKzXpJ.js';
2
- export { A as Agent, f as BuilderThreadEndpointHandler, B as Controller, a as ControllerContext, j as FlowResult, p as ImageContentPart, I as ImageMetadata, L as LLMResponse, q as MultimodalContent, R as RequestContext, S as StorageBackend, k as TelemetryEvent, o as TextContentPart, b as ThreadEndpointContext, T as ToolCall, i as ToolResult, c as createThreadEndpointHandler, d as defineController } from './index-BnrKzXpJ.js';
1
+ import { e as ThreadEnv, h as ThreadMetadata, M as Message, l as FileRecord, F as FlowState, n as FileStats, G as GrepResult, m as AttachmentRef, r as MessageContent, E as Env, g as ThreadInstance } from './index-Dx1js-H1.js';
2
+ export { A as Agent, f as BuilderThreadEndpointHandler, B as Controller, a as ControllerContext, j as FlowResult, p as ImageContentPart, I as ImageMetadata, L as LLMResponse, q as MultimodalContent, R as RequestContext, S as StorageBackend, k as TelemetryEvent, o as TextContentPart, b as ThreadEndpointContext, T as ToolCall, i as ToolResult, c as createThreadEndpointHandler, d as defineController } from './index-Dx1js-H1.js';
3
3
  import { CodeExecutionOptions, CodeExecutionResult, SubagentRegistryEntry, InjectMessageInput, QueueMessageInput, ModelDefinition as ModelDefinition$1, ToolArgs, PromptTextPart, PromptEnvPart, VariableDefinition, SubpromptConfig as SubpromptConfig$1, PromptToolConfig as PromptToolConfig$1, SubagentToolConfig as SubagentToolConfig$1, ReasoningConfig, SideConfig as SideConfig$1, LLMProviderInterface, ContentPart, TextPart, ImagePart, FilePart, NamespaceContext, DefinitionLoader } from '@standardagents/spec';
4
4
  export { AgentType, DefinitionLoader, GlobalNamespaceContext, HookSignatures, ImageContent, ModelCapabilities, ModelProvider, NamespaceContext, PackageSignature, PackedExports, PackedMeta, PackedMetadata, PackedNamespaceContext, PromptInput, PromptTextPart, ProviderAssistantMessage, ProviderError, ProviderErrorCode, ProviderFactory, ProviderFactoryConfig, ProviderFinishReason, ProviderGeneratedImage, ProviderMessage, ProviderMessageContent, ModelCapabilities as ProviderModelCapabilities, ProviderReasoningDetail, ProviderRequest, ProviderResponse, ProviderStreamChunk, ProviderSystemMessage, ProviderTool, ProviderToolCallPart, ProviderToolMessage, ProviderToolResultContent, ProviderUsage, ProviderUserMessage, ReasoningConfig, StructuredPrompt, TextContent, Tool, ToolArgs, ToolArgsNode, ToolArgsRawShape, ToolContent, belongsToPackage, defineAgent, defineHook, defineModel, definePrompt, defineTool, isPacked, isVisibleInNamespace, mapReasoningLevel } from '@standardagents/spec';
5
5
  import { DurableObject, WorkerEntrypoint } from 'cloudflare:workers';
package/dist/runtime.js CHANGED
@@ -1445,6 +1445,23 @@ function convertUsage(usage) {
1445
1445
  provider: usage.provider
1446
1446
  };
1447
1447
  }
1448
+ function inferProviderFromModelId(model) {
1449
+ const trimmed = model?.trim();
1450
+ if (!trimmed) return null;
1451
+ const slashIndex = trimmed.indexOf("/");
1452
+ if (slashIndex <= 0) return null;
1453
+ return trimmed.slice(0, slashIndex);
1454
+ }
1455
+ function inferInitialActualProvider(providerName, modelDef) {
1456
+ if (providerName !== "openrouter") return null;
1457
+ return inferProviderFromModelId(modelDef.model);
1458
+ }
1459
+ function normalizeProviderMetadataValue(value) {
1460
+ if (typeof value !== "string") return void 0;
1461
+ const trimmed = value.trim();
1462
+ if (!trimmed || trimmed.toLowerCase() === "unknown") return void 0;
1463
+ return trimmed;
1464
+ }
1448
1465
  var NON_VISION_PLACEHOLDER_TEXT, LLMRequest;
1449
1466
  var init_LLMRequest = __esm({
1450
1467
  "src/agents/LLMRequest.ts"() {
@@ -1613,6 +1630,7 @@ var init_LLMRequest = __esm({
1613
1630
  console.error("Failed to get provider name:", err);
1614
1631
  }
1615
1632
  const { requestBody, visionFiltering } = buildRequestBody(context, modelDef);
1633
+ const initialActualProvider = inferInitialActualProvider(providerName, modelDef);
1616
1634
  const requestBodyForLog = {
1617
1635
  ...requestBody,
1618
1636
  messages: stripBase64FromMessages(
@@ -1631,6 +1649,7 @@ var init_LLMRequest = __esm({
1631
1649
  id,
1632
1650
  message_id: state.rootMessageId || crypto.randomUUID(),
1633
1651
  provider: providerName || this.getProviderFromModel(modelId),
1652
+ actual_provider: initialActualProvider ?? void 0,
1634
1653
  model: modelId,
1635
1654
  model_name: modelDef.model,
1636
1655
  endpoint: "chat.completions",
@@ -1651,14 +1670,15 @@ var init_LLMRequest = __esm({
1651
1670
  await state.storage.sql.exec(
1652
1671
  `
1653
1672
  INSERT INTO logs (
1654
- id, message_id, provider, model, model_name, endpoint,
1673
+ id, message_id, provider, actual_provider, model, model_name, endpoint,
1655
1674
  request_body, tools_available, message_history_length, prompt_name,
1656
1675
  parent_log_id, retry_of_log_id, tools_schema, system_prompt, is_complete, created_at
1657
- ) VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, ?11, ?12, ?13, ?14, ?15, ?16)
1676
+ ) VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, ?11, ?12, ?13, ?14, ?15, ?16, ?17)
1658
1677
  `,
1659
1678
  logData.id,
1660
1679
  logData.message_id,
1661
1680
  logData.provider,
1681
+ logData.actual_provider,
1662
1682
  logData.model,
1663
1683
  logData.model_name,
1664
1684
  logData.endpoint,
@@ -1910,7 +1930,7 @@ var init_LLMRequest = __esm({
1910
1930
  const calculatedCost = calculateUsageCost(response.usage, resolvedPricing);
1911
1931
  const providerReportedCost = typeof response.usage.cost === "number" ? response.usage.cost : typeof response.usage.cost === "string" ? parseFloat(response.usage.cost) : null;
1912
1932
  const cost_total = providerReportedCost != null && !Number.isNaN(providerReportedCost) ? providerReportedCost : calculatedCost?.costTotal ?? null;
1913
- const actualProvider = response.usage.provider;
1933
+ const actualProvider = normalizeProviderMetadataValue(response.usage.provider) ?? null;
1914
1934
  const aggregateResponse = response._aggregate_response;
1915
1935
  const responseBody = aggregateResponse ? JSON.stringify(aggregateResponse) : JSON.stringify(response);
1916
1936
  const providerTools = [];
@@ -2338,8 +2358,13 @@ ${errorStack}` : ""}${errorDetails}`;
2338
2358
  }).catch((err) => {
2339
2359
  console.error("Async metadata fetch failed (non-fatal):", err);
2340
2360
  });
2341
- state.rootState.pendingMetadataPromises = state.rootState.pendingMetadataPromises || [];
2342
- state.rootState.pendingMetadataPromises.push(metadataPromise);
2361
+ const rootState = state.rootState || state;
2362
+ rootState.pendingMetadataPromises = rootState.pendingMetadataPromises || [];
2363
+ rootState.pendingMetadataPromises.push(metadataPromise);
2364
+ if (rootState !== state) {
2365
+ state.pendingMetadataPromises = state.pendingMetadataPromises || [];
2366
+ state.pendingMetadataPromises.push(metadataPromise);
2367
+ }
2343
2368
  }
2344
2369
  /**
2345
2370
  * Update log record with async metadata from provider.
@@ -2349,9 +2374,12 @@ ${errorStack}` : ""}${errorDetails}`;
2349
2374
  const updates = [];
2350
2375
  const values = [];
2351
2376
  let paramIndex = 1;
2352
- if (metadata.actual_provider !== void 0) {
2377
+ const actualProvider = normalizeProviderMetadataValue(
2378
+ metadata.actual_provider ?? metadata.actualProvider ?? metadata.providerName
2379
+ );
2380
+ if (actualProvider !== void 0) {
2353
2381
  updates.push(`actual_provider = ?${paramIndex++}`);
2354
- values.push(metadata.actual_provider);
2382
+ values.push(actualProvider);
2355
2383
  }
2356
2384
  if (metadata.generation_cost !== void 0) {
2357
2385
  updates.push(`cost_total = ?${paramIndex++}`);
@@ -2369,7 +2397,7 @@ ${errorStack}` : ""}${errorDetails}`;
2369
2397
  log_id: logId,
2370
2398
  data: {
2371
2399
  id: logId,
2372
- ...metadata.actual_provider !== void 0 && { actual_provider: metadata.actual_provider },
2400
+ ...actualProvider !== void 0 && { actual_provider: actualProvider },
2373
2401
  ...metadata.generation_cost !== void 0 && { cost_total: metadata.generation_cost }
2374
2402
  }
2375
2403
  });
@@ -3095,6 +3123,11 @@ var init_ToolExecutor = __esm({
3095
3123
  state.sequence.queue.push(...toolCalls);
3096
3124
  state.sequence.isHandling = true;
3097
3125
  while (state.sequence.queue.length > 0) {
3126
+ if (state.stopped) {
3127
+ state.sequence.queue = [];
3128
+ state.sequence.isHandling = false;
3129
+ break;
3130
+ }
3098
3131
  if (await state.thread.instance.shouldStop() || state.abortController?.signal.aborted) {
3099
3132
  state.sequence.queue = [];
3100
3133
  state.sequence.isHandling = false;
@@ -3123,7 +3156,17 @@ var init_ToolExecutor = __esm({
3123
3156
  });
3124
3157
  continue;
3125
3158
  }
3126
- const result = await this.executeToolCall(call, state, toolMessageId);
3159
+ const previousToolCall = state.currentToolCall;
3160
+ state.currentToolCall = {
3161
+ id: call.id,
3162
+ name: call.function.name
3163
+ };
3164
+ let result;
3165
+ try {
3166
+ result = await this.executeToolCall(call, state, toolMessageId);
3167
+ } finally {
3168
+ state.currentToolCall = previousToolCall;
3169
+ }
3127
3170
  if (result.status === "error") {
3128
3171
  const error = new Error(result.error || "Tool execution failed");
3129
3172
  if (result.stack) {
@@ -4992,6 +5035,11 @@ ${attachmentPaths}`;
4992
5035
  }
4993
5036
  }
4994
5037
  const toolStatus = processedResult.status === "success" ? "success" : "error";
5038
+ const parentNotifications = this.consumeParentNotificationsForToolCall(
5039
+ state,
5040
+ call
5041
+ );
5042
+ const notificationContent = parentNotifications[parentNotifications.length - 1]?.content;
4995
5043
  let message = {
4996
5044
  id: messageId,
4997
5045
  role: "tool",
@@ -5005,15 +5053,20 @@ ${attachmentPaths}`;
5005
5053
  parent_id: state.parentMessageId || null,
5006
5054
  depth: state.depth,
5007
5055
  attachments: attachmentRefs && attachmentRefs.length > 0 ? JSON.stringify(attachmentRefs) : null,
5008
- subagent_id: typeof processedResult.subagent_id === "string" && processedResult.subagent_id.trim().length > 0 ? processedResult.subagent_id.trim() : null
5056
+ subagent_id: typeof processedResult.subagent_id === "string" && processedResult.subagent_id.trim().length > 0 ? processedResult.subagent_id.trim() : null,
5057
+ metadata: parentNotifications.length > 0 ? {
5058
+ parent_notification: true,
5059
+ parent_notifications: parentNotifications,
5060
+ parent_notification_content: notificationContent
5061
+ } : void 0
5009
5062
  };
5010
5063
  message = await FlowEngine2.runBeforeCreateMessageHook(state, message);
5011
5064
  message.created_at = Date.now() * TIMESTAMP_MULTIPLIER;
5012
5065
  const currentPrompt = state.promptPath.length > 0 ? state.promptPath[state.promptPath.length - 1] : null;
5013
5066
  await state.storage.sql.exec(
5014
5067
  `
5015
- INSERT INTO messages (id, role, content, name, tool_call_id, created_at, tool_status, parent_id, depth, attachments, subagent_id, prompt)
5016
- VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, ?11, ?12)
5068
+ INSERT INTO messages (id, role, content, name, tool_call_id, created_at, tool_status, parent_id, depth, attachments, subagent_id, prompt, metadata)
5069
+ VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, ?11, ?12, ?13)
5017
5070
  `,
5018
5071
  message.id,
5019
5072
  message.role,
@@ -5026,7 +5079,8 @@ ${attachmentPaths}`;
5026
5079
  message.depth,
5027
5080
  message.attachments,
5028
5081
  message.subagent_id ?? null,
5029
- currentPrompt
5082
+ currentPrompt,
5083
+ message.metadata ? JSON.stringify(message.metadata) : null
5030
5084
  );
5031
5085
  await FlowEngine2.runAfterCreateMessageHook(state, message);
5032
5086
  const parentLogId = state.currentLogId || null;
@@ -5091,6 +5145,23 @@ ${attachmentPaths}`;
5091
5145
  await this.logError(state, error, `storeToolResult:${call.function.name}`);
5092
5146
  }
5093
5147
  }
5148
+ static consumeParentNotificationsForToolCall(state, call) {
5149
+ const pending = state.parentNotifications ?? [];
5150
+ if (pending.length === 0) return [];
5151
+ const matched = [];
5152
+ const remaining = [];
5153
+ for (const notification of pending) {
5154
+ const matchesCallId = notification.toolCallId === call.id;
5155
+ const matchesToolName = !notification.toolCallId && notification.toolName === call.function.name;
5156
+ if (matchesCallId || matchesToolName) {
5157
+ matched.push(notification);
5158
+ } else {
5159
+ remaining.push(notification);
5160
+ }
5161
+ }
5162
+ state.parentNotifications = remaining;
5163
+ return matched;
5164
+ }
5094
5165
  static async createSilentGeneratedAssetPathMessagesFromToolResult(state, sourceToolMessage, refs, enabled) {
5095
5166
  if (!enabled || !refs || refs.length === 0) {
5096
5167
  return;
@@ -5552,6 +5623,8 @@ var init_FlowEngine = __esm({
5552
5623
  stoppedBy: state.stoppedBy,
5553
5624
  stopReason: state.stopReason,
5554
5625
  stopReasonCode: state.stopReasonCode,
5626
+ sessionStopped: state.sessionStopped,
5627
+ sessionStopResult: state.sessionStopResult,
5555
5628
  stepCount: state.stepCount,
5556
5629
  stream: state.stream.httpStream
5557
5630
  };
@@ -5751,6 +5824,8 @@ var init_FlowEngine = __esm({
5751
5824
  stoppedBy: stateInput.stoppedBy,
5752
5825
  stopReason: stateInput.stopReason,
5753
5826
  stopReasonCode: stateInput.stopReasonCode,
5827
+ sessionStopped: stateInput.sessionStopped,
5828
+ sessionStopResult: stateInput.sessionStopResult,
5754
5829
  messageHistory: [],
5755
5830
  sequence: stateInput.sequence || {
5756
5831
  queue: [],
@@ -11720,34 +11795,79 @@ var init_ThreadStateImpl = __esm({
11720
11795
  this._metadata.tenvs = nextEnv;
11721
11796
  }
11722
11797
  async notifyParent(content) {
11723
- if (!content || !content.trim()) {
11798
+ const trimmedContent = content?.trim();
11799
+ if (!trimmedContent) {
11724
11800
  throw new Error("notifyParent requires non-empty content");
11725
11801
  }
11726
- const { parentThreadId, parentStub } = await this._getParentThreadStub();
11727
- if (typeof parentStub.queueMessage !== "function") {
11728
- throw new Error("notifyParent is not supported by this thread runtime");
11802
+ if (this._flowState) {
11803
+ const currentToolCall = this._flowState.currentToolCall;
11804
+ const notification = {
11805
+ content: trimmedContent,
11806
+ toolCallId: currentToolCall?.id ?? null,
11807
+ toolName: currentToolCall?.name ?? this._flowState.active?.tool ?? null,
11808
+ created_at: Date.now() * 1e3
11809
+ };
11810
+ this._flowState.parentNotifications = [
11811
+ ...this._flowState.parentNotifications ?? [],
11812
+ notification
11813
+ ];
11814
+ }
11815
+ const parentTarget = await this._getOptionalParentThreadStub();
11816
+ if (parentTarget && typeof parentTarget.parentStub.queueMessage === "function") {
11817
+ await parentTarget.parentStub.queueMessage(parentTarget.parentThreadId, {
11818
+ role: "user",
11819
+ content: trimmedContent,
11820
+ silent: true,
11821
+ metadata: { subagent_id: this.threadId }
11822
+ });
11729
11823
  }
11730
- await parentStub.queueMessage(parentThreadId, {
11731
- role: "user",
11732
- content: content.trim(),
11733
- silent: true,
11734
- metadata: { subagent_id: this.threadId }
11735
- });
11736
11824
  }
11737
11825
  async setStatus(status) {
11738
11826
  if (!status || !status.trim()) {
11739
11827
  throw new Error("setStatus requires non-empty status");
11740
11828
  }
11741
- const { parentThreadId, parentStub } = await this._getParentThreadStub();
11742
- if (typeof parentStub.updateChildRegistryStatus !== "function") {
11743
- throw new Error("setStatus is not supported by this thread runtime");
11829
+ const parentTarget = await this._getOptionalParentThreadStub();
11830
+ if (!parentTarget || typeof parentTarget.parentStub.updateChildRegistryStatus !== "function") {
11831
+ return;
11744
11832
  }
11745
- await parentStub.updateChildRegistryStatus(
11746
- parentThreadId,
11833
+ await parentTarget.parentStub.updateChildRegistryStatus(
11834
+ parentTarget.parentThreadId,
11747
11835
  this.threadId,
11748
11836
  status.trim()
11749
11837
  );
11750
11838
  }
11839
+ async stopSession(options = {}) {
11840
+ if (!this._flowState) {
11841
+ throw new Error("stopSession requires active execution");
11842
+ }
11843
+ const notifyParent = options.notifyParent?.trim();
11844
+ if (options.notifyParent !== void 0 && !notifyParent) {
11845
+ throw new Error("stopSession notifyParent requires non-empty content");
11846
+ }
11847
+ const status = options.status?.trim();
11848
+ if (options.status !== void 0 && !status) {
11849
+ throw new Error("stopSession status requires non-empty status");
11850
+ }
11851
+ if (notifyParent) {
11852
+ await this.notifyParent(notifyParent);
11853
+ }
11854
+ if (status) {
11855
+ await this.setStatus(status);
11856
+ }
11857
+ const result = options.result?.trim() || "Session stopped.";
11858
+ this._flowState.sessionStopped = true;
11859
+ this._flowState.sessionStopResult = result;
11860
+ this._flowState.stopped = true;
11861
+ this._flowState.stoppedBy = this._flowState.currentSide;
11862
+ this._flowState.stopReason = "Session stopped by state.stopSession()";
11863
+ this._flowState.stopReasonCode = "state_stop_session";
11864
+ this._flowState.emitTelemetry?.({
11865
+ type: "stopped",
11866
+ reason: this._flowState.stopReason,
11867
+ side: this._flowState.currentSide,
11868
+ timestamp: Date.now()
11869
+ });
11870
+ }
11751
11871
  async getChildThread(referenceId) {
11752
11872
  const agentBuilder = this._getAgentBuilderStub();
11753
11873
  const child = await agentBuilder.getThread(referenceId);
@@ -12160,19 +12280,47 @@ var init_ThreadStateImpl = __esm({
12160
12280
  return Object.keys(normalized).length > 0 ? normalized : null;
12161
12281
  }
12162
12282
  async _getParentThreadId() {
12283
+ const parent = await this._getOptionalParentThreadId();
12284
+ if (!parent) {
12285
+ throw new Error(`Thread ${this.threadId} has no parent thread`);
12286
+ }
12287
+ return parent;
12288
+ }
12289
+ async _getOptionalParentThreadId() {
12163
12290
  const metadataParent = typeof this._metadata.parent === "string" && this._metadata.parent.trim().length > 0 ? this._metadata.parent.trim() : null;
12164
12291
  if (metadataParent) {
12165
12292
  return metadataParent;
12166
12293
  }
12167
- const agentBuilder = this._getAgentBuilderStub();
12168
- const thread = await agentBuilder.getThread(this.threadId);
12294
+ let thread = null;
12295
+ try {
12296
+ const agentBuilder = this._getAgentBuilderStub();
12297
+ thread = await agentBuilder.getThread(this.threadId);
12298
+ } catch {
12299
+ return null;
12300
+ }
12169
12301
  const parent = thread && typeof thread.parent === "string" && thread.parent.trim().length > 0 ? thread.parent.trim() : null;
12170
- if (!parent) {
12171
- throw new Error(`Thread ${this.threadId} has no parent thread`);
12302
+ if (parent) {
12303
+ this._metadata.parent = parent;
12172
12304
  }
12173
- this._metadata.parent = parent;
12174
12305
  return parent;
12175
12306
  }
12307
+ async _getOptionalParentThreadStub() {
12308
+ const parentThreadId = await this._getOptionalParentThreadId();
12309
+ if (!parentThreadId) {
12310
+ return null;
12311
+ }
12312
+ try {
12313
+ const threadNamespace = this._threadInstance.env.AGENT_BUILDER_THREAD;
12314
+ if (!threadNamespace || typeof threadNamespace.idFromName !== "function" || typeof threadNamespace.get !== "function") {
12315
+ return null;
12316
+ }
12317
+ const durableId = threadNamespace.idFromName(parentThreadId);
12318
+ const parentStub = threadNamespace.get(durableId);
12319
+ return { parentThreadId, parentStub };
12320
+ } catch {
12321
+ return null;
12322
+ }
12323
+ }
12176
12324
  async _getParentThreadStub() {
12177
12325
  const parentThreadId = await this._getParentThreadId();
12178
12326
  const durableId = this._threadInstance.env.AGENT_BUILDER_THREAD.idFromName(parentThreadId);
@@ -14241,6 +14389,7 @@ function createEndpointThreadStateBridge(state) {
14241
14389
  setEnv: (propertyName, value, options) => state.setEnv(propertyName, value, options),
14242
14390
  notifyParent: (content) => state.notifyParent(content),
14243
14391
  setStatus: (status) => state.setStatus(status),
14392
+ stopSession: (options) => state.stopSession(options),
14244
14393
  queueTool: (toolName, args) => state.queueTool(toolName, args),
14245
14394
  invokeTool: (toolName, args) => state.invokeTool(toolName, args),
14246
14395
  scheduleEffect: (name, args, delay) => state.scheduleEffect(name, args, delay),
@@ -15460,6 +15609,15 @@ var DurableThread = class extends DurableObject {
15460
15609
  const terminalToolNames = await this.getTerminalSessionToolNames(agentId);
15461
15610
  const sessionFailToolNames = terminalToolNames.fail;
15462
15611
  const hasTerminalSessionTools = terminalToolNames.all.size > 0;
15612
+ if (flowResult.sessionStopped) {
15613
+ await emitParentHandoffStatus("result");
15614
+ return {
15615
+ status: "success",
15616
+ result: flowResult.sessionStopResult?.trim() || "Session stopped.",
15617
+ attachments: void 0,
15618
+ terminal: true
15619
+ };
15620
+ }
15463
15621
  const latestInRunCursor = await this.ctx.storage.sql.exec(
15464
15622
  `
15465
15623
  SELECT role, name, content, tool_status, attachments
@@ -16039,27 +16197,30 @@ ${paths.join("\n")}`;
16039
16197
  if (!parentThreadId || !flowResult.stopped) {
16040
16198
  return;
16041
16199
  }
16200
+ const sessionStopped = flowResult.sessionStopped === true;
16042
16201
  const terminalToolNames = await this.getTerminalSessionToolNames(agentName);
16043
16202
  const sessionFailToolNames = terminalToolNames.fail;
16044
16203
  const hasTerminalSessionTools = terminalToolNames.all.size > 0;
16045
- const latestTerminalMessage = hasTerminalSessionTools ? await this.getLatestTerminalSessionMessageAfter(
16204
+ const latestTerminalMessage = !sessionStopped && hasTerminalSessionTools ? await this.getLatestTerminalSessionMessageAfter(
16046
16205
  previousTopLevelMessageId,
16047
16206
  terminalToolNames.all
16048
16207
  ) : null;
16049
- const latest = latestTerminalMessage ?? await this.getLatestTopLevelMessage();
16050
- if (!latest && !hasTerminalSessionTools) {
16208
+ const latest = sessionStopped ? null : latestTerminalMessage ?? await this.getLatestTopLevelMessage();
16209
+ if (!sessionStopped && !latest && !hasTerminalSessionTools) {
16051
16210
  return;
16052
16211
  }
16053
- if (latest && latest.id === previousTopLevelMessageId && !hasTerminalSessionTools) {
16212
+ if (!sessionStopped && latest && latest.id === previousTopLevelMessageId && !hasTerminalSessionTools) {
16054
16213
  return;
16055
16214
  }
16056
16215
  const isTerminalSessionMessage = !!latest && latest.role === "tool" && !!latest.name && terminalToolNames.all.has(latest.name);
16057
16216
  const isFailSessionTerminal = isTerminalSessionMessage && !!latest.name && sessionFailToolNames.has(latest.name);
16058
16217
  const isSuccessfulTerminalSessionMessage = isTerminalSessionMessage && latest?.tool_status !== "error";
16059
- const isFailureWithoutTerminalMessage = hasTerminalSessionTools && !isSuccessfulTerminalSessionMessage;
16218
+ const isFailureWithoutTerminalMessage = !sessionStopped && hasTerminalSessionTools && !isSuccessfulTerminalSessionMessage;
16060
16219
  const isErrorTerminal = latest?.tool_status === "error" || isFailSessionTerminal || isFailureWithoutTerminalMessage;
16061
16220
  let refs = [];
16062
- if (hasTerminalSessionTools) {
16221
+ if (sessionStopped) {
16222
+ refs = [];
16223
+ } else if (hasTerminalSessionTools) {
16063
16224
  refs = this.parseAttachmentRefsJson(latestTerminalMessage?.attachments ?? null) ?? [];
16064
16225
  } else if (latest?.attachments) {
16065
16226
  refs = this.parseAttachmentRefsJson(latest.attachments) ?? [];
@@ -16077,7 +16238,7 @@ ${paths.join("\n")}`;
16077
16238
  parentStub,
16078
16239
  `from-subagent-${threadId}`
16079
16240
  );
16080
- const resultContent = isFailureWithoutTerminalMessage ? this.buildMissingTerminalSessionFailure(flowResult) : latest?.content || (isErrorTerminal ? "Subagent execution failed." : "Subagent completed.");
16241
+ const resultContent = sessionStopped ? flowResult.sessionStopResult?.trim() || "Session stopped." : isFailureWithoutTerminalMessage ? this.buildMissingTerminalSessionFailure(flowResult) : latest?.content || (isErrorTerminal ? "Subagent execution failed." : "Subagent completed.");
16081
16242
  const attachmentSummary = this.formatSubagentAttachmentPathSummary(copiedRefs);
16082
16243
  const messageContent = isErrorTerminal ? `Subagent (reference: ${threadId}) has reported a failure:
16083
16244
 
@@ -16093,7 +16254,7 @@ ${resultContent}${attachmentSummary}`;
16093
16254
  metadata: { subagent_id: threadId }
16094
16255
  });
16095
16256
  }
16096
- const terminalChildRun = hasTerminalSessionTools;
16257
+ const terminalChildRun = sessionStopped || hasTerminalSessionTools;
16097
16258
  await parentStub.updateChildRegistryStatus(
16098
16259
  parentThreadId,
16099
16260
  threadId,