@standardagents/builder 0.17.0-next.a4b7340 → 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.js CHANGED
@@ -195,6 +195,16 @@ function getGoogleFallbackPricing(modelId) {
195
195
  if (prefixMatch) return { ...prefixMatch.pricing, source: "google-static" };
196
196
  return null;
197
197
  }
198
+ function getAnthropicFallbackPricing(modelId) {
199
+ const normalized = normalizeModelId(modelId);
200
+ let best = null;
201
+ for (const entry of ANTHROPIC_MODEL_PRICING_PREFIXES) {
202
+ if (normalized.startsWith(entry.prefix) && (!best || entry.prefix.length > best.prefix.length)) {
203
+ best = entry;
204
+ }
205
+ }
206
+ return best ? { ...best.pricing, source: "anthropic-static" } : null;
207
+ }
198
208
  function getGroqFallbackPricing(modelId) {
199
209
  const pricing = GROQ_MODEL_PRICING[normalizeModelId(modelId)];
200
210
  return pricing ? { ...pricing, source: "groq-static" } : null;
@@ -225,6 +235,9 @@ function resolveModelPricing(modelDef, providerName) {
225
235
  source: "model"
226
236
  };
227
237
  }
238
+ if (providerName === "anthropic") {
239
+ return getAnthropicFallbackPricing(modelDef.model);
240
+ }
228
241
  if (providerName === "cerebras") {
229
242
  return getCerebrasFallbackPricing(modelDef.model);
230
243
  }
@@ -260,10 +273,29 @@ function calculateUsageCost(usage, pricing) {
260
273
  costTotal: roundCost(costInput + costOutput)
261
274
  };
262
275
  }
263
- var TOKENS_PER_MILLION, CEREBRAS_MODEL_PRICING, GOOGLE_MODEL_PRICING, GOOGLE_MODEL_PRICING_PREFIXES, GROQ_MODEL_PRICING, XAI_MODEL_PRICING;
276
+ var TOKENS_PER_MILLION, ANTHROPIC_MODEL_PRICING_PREFIXES, CEREBRAS_MODEL_PRICING, GOOGLE_MODEL_PRICING, GOOGLE_MODEL_PRICING_PREFIXES, GROQ_MODEL_PRICING, XAI_MODEL_PRICING;
264
277
  var init_pricing = __esm({
265
278
  "src/agents/pricing.ts"() {
266
279
  TOKENS_PER_MILLION = 1e6;
280
+ ANTHROPIC_MODEL_PRICING_PREFIXES = [
281
+ { prefix: "claude-fable-5", pricing: { inputPrice: 10, outputPrice: 50, cachedPrice: 1 } },
282
+ { prefix: "claude-mythos", pricing: { inputPrice: 10, outputPrice: 50, cachedPrice: 1 } },
283
+ { prefix: "claude-opus-4-8", pricing: { inputPrice: 5, outputPrice: 25, cachedPrice: 0.5 } },
284
+ { prefix: "claude-opus-4-7", pricing: { inputPrice: 5, outputPrice: 25, cachedPrice: 0.5 } },
285
+ { prefix: "claude-opus-4-6", pricing: { inputPrice: 5, outputPrice: 25, cachedPrice: 0.5 } },
286
+ { prefix: "claude-opus-4-5", pricing: { inputPrice: 5, outputPrice: 25, cachedPrice: 0.5 } },
287
+ { prefix: "claude-opus-4-1", pricing: { inputPrice: 15, outputPrice: 75, cachedPrice: 1.5 } },
288
+ { prefix: "claude-opus-4", pricing: { inputPrice: 15, outputPrice: 75, cachedPrice: 1.5 } },
289
+ { prefix: "claude-sonnet-4-6", pricing: { inputPrice: 3, outputPrice: 15, cachedPrice: 0.3 } },
290
+ { prefix: "claude-sonnet-4-5", pricing: { inputPrice: 3, outputPrice: 15, cachedPrice: 0.3 } },
291
+ { prefix: "claude-sonnet-4", pricing: { inputPrice: 3, outputPrice: 15, cachedPrice: 0.3 } },
292
+ { prefix: "claude-haiku-4-5", pricing: { inputPrice: 1, outputPrice: 5, cachedPrice: 0.1 } },
293
+ { prefix: "claude-3-7-sonnet", pricing: { inputPrice: 3, outputPrice: 15, cachedPrice: 0.3 } },
294
+ { prefix: "claude-3-5-sonnet", pricing: { inputPrice: 3, outputPrice: 15, cachedPrice: 0.3 } },
295
+ { prefix: "claude-3-5-haiku", pricing: { inputPrice: 0.8, outputPrice: 4, cachedPrice: 0.08 } },
296
+ { prefix: "claude-3-opus", pricing: { inputPrice: 15, outputPrice: 75, cachedPrice: 1.5 } },
297
+ { prefix: "claude-3-haiku", pricing: { inputPrice: 0.25, outputPrice: 1.25, cachedPrice: 0.03 } }
298
+ ];
267
299
  CEREBRAS_MODEL_PRICING = {
268
300
  "zai-glm-4.7": {
269
301
  inputPrice: 2.25,
@@ -926,9 +958,9 @@ var init_ProviderRegistry = __esm({
926
958
  const fingerprint = this.getProviderFingerprint(providerName, modelDef);
927
959
  const cached = this.providerCache.get(modelName);
928
960
  if (cached?.fingerprint === fingerprint) {
929
- return this.buildResult(cached.provider, modelDef);
961
+ return this.buildResult(cached.provider, modelDef, false);
930
962
  }
931
- return this.cacheAndReturn(modelName, createTestProvider(), modelDef, fingerprint);
963
+ return this.cacheAndReturn(modelName, createTestProvider(), modelDef, fingerprint, false);
932
964
  }
933
965
  const apiKey = this.getApiKeyForProvider(providerName, env);
934
966
  if (apiKey) {
@@ -945,19 +977,19 @@ var init_ProviderRegistry = __esm({
945
977
  const fingerprint = this.getProviderFingerprint(providerName, modelDef, config);
946
978
  const cached = this.providerCache.get(modelName);
947
979
  if (cached?.fingerprint === fingerprint) {
948
- return this.buildResult(cached.provider, modelDef);
980
+ return this.buildResult(cached.provider, modelDef, false);
949
981
  }
950
- return this.cacheAndReturn(modelName, providerFactory(config), modelDef, fingerprint);
982
+ return this.cacheAndReturn(modelName, providerFactory(config), modelDef, fingerprint, false);
951
983
  }
952
984
  if (this.providerOverrideFn) {
953
985
  const overrideFingerprint = this.getProviderFingerprint(`override:${this.providerOverrideRevision}`, modelDef);
954
986
  const overrideCached = this.providerCache.get(modelName);
955
987
  if (overrideCached?.fingerprint === overrideFingerprint) {
956
- return this.buildResult(overrideCached.provider, modelDef);
988
+ return this.buildResult(overrideCached.provider, modelDef, true);
957
989
  }
958
990
  const overrideProvider = this.providerOverrideFn(modelName, modelDef, env);
959
991
  if (overrideProvider) {
960
- return this.cacheAndReturn(modelName, overrideProvider, modelDef, overrideFingerprint);
992
+ return this.cacheAndReturn(modelName, overrideProvider, modelDef, overrideFingerprint, true);
961
993
  }
962
994
  }
963
995
  const routing = resolvePlatformRouting(providerName, env);
@@ -969,18 +1001,18 @@ var init_ProviderRegistry = __esm({
969
1001
  const fingerprint = this.getProviderFingerprint(`platform:${providerName}`, modelDef, config);
970
1002
  const cached = this.providerCache.get(modelName);
971
1003
  if (cached?.fingerprint === fingerprint) {
972
- return this.buildResult(cached.provider, modelDef);
1004
+ return this.buildResult(cached.provider, modelDef, true);
973
1005
  }
974
- return this.cacheAndReturn(modelName, providerFactory(config), modelDef, fingerprint);
1006
+ return this.cacheAndReturn(modelName, providerFactory(config), modelDef, fingerprint, true);
975
1007
  }
976
1008
  throw new Error(`No API key found for provider: ${providerName}. Set the provider key or STANDARD_AGENTS_API_KEY.`);
977
1009
  }
978
- buildResult(provider, modelDef) {
979
- return { provider, modelName: modelDef.model, modelDef };
1010
+ buildResult(provider, modelDef, viaPlatform) {
1011
+ return { provider, modelName: modelDef.model, modelDef, viaPlatform };
980
1012
  }
981
- cacheAndReturn(modelName, provider, modelDef, fingerprint) {
982
- this.providerCache.set(modelName, { provider, fingerprint });
983
- return this.buildResult(provider, modelDef);
1013
+ cacheAndReturn(modelName, provider, modelDef, fingerprint, viaPlatform) {
1014
+ this.providerCache.set(modelName, { provider, fingerprint, viaPlatform });
1015
+ return this.buildResult(provider, modelDef, viaPlatform);
984
1016
  }
985
1017
  getProviderFingerprint(providerName, modelDef, config) {
986
1018
  return JSON.stringify({
@@ -1413,6 +1445,23 @@ function convertUsage(usage) {
1413
1445
  provider: usage.provider
1414
1446
  };
1415
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
+ }
1416
1465
  var NON_VISION_PLACEHOLDER_TEXT, LLMRequest;
1417
1466
  var init_LLMRequest = __esm({
1418
1467
  "src/agents/LLMRequest.ts"() {
@@ -1581,6 +1630,7 @@ var init_LLMRequest = __esm({
1581
1630
  console.error("Failed to get provider name:", err);
1582
1631
  }
1583
1632
  const { requestBody, visionFiltering } = buildRequestBody(context, modelDef);
1633
+ const initialActualProvider = inferInitialActualProvider(providerName, modelDef);
1584
1634
  const requestBodyForLog = {
1585
1635
  ...requestBody,
1586
1636
  messages: stripBase64FromMessages(
@@ -1599,6 +1649,7 @@ var init_LLMRequest = __esm({
1599
1649
  id,
1600
1650
  message_id: state.rootMessageId || crypto.randomUUID(),
1601
1651
  provider: providerName || this.getProviderFromModel(modelId),
1652
+ actual_provider: initialActualProvider ?? void 0,
1602
1653
  model: modelId,
1603
1654
  model_name: modelDef.model,
1604
1655
  endpoint: "chat.completions",
@@ -1619,14 +1670,15 @@ var init_LLMRequest = __esm({
1619
1670
  await state.storage.sql.exec(
1620
1671
  `
1621
1672
  INSERT INTO logs (
1622
- id, message_id, provider, model, model_name, endpoint,
1673
+ id, message_id, provider, actual_provider, model, model_name, endpoint,
1623
1674
  request_body, tools_available, message_history_length, prompt_name,
1624
1675
  parent_log_id, retry_of_log_id, tools_schema, system_prompt, is_complete, created_at
1625
- ) 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)
1626
1677
  `,
1627
1678
  logData.id,
1628
1679
  logData.message_id,
1629
1680
  logData.provider,
1681
+ logData.actual_provider,
1630
1682
  logData.model,
1631
1683
  logData.model_name,
1632
1684
  logData.endpoint,
@@ -1661,7 +1713,7 @@ var init_LLMRequest = __esm({
1661
1713
  */
1662
1714
  static async callModel(modelId, context, state, logId) {
1663
1715
  const { ProviderRegistry: ProviderRegistry2 } = await Promise.resolve().then(() => (init_providers(), providers_exports));
1664
- const { provider, modelName, modelDef } = await ProviderRegistry2.getProvider(
1716
+ const { provider, modelName, modelDef, viaPlatform } = await ProviderRegistry2.getProvider(
1665
1717
  modelId,
1666
1718
  state.env,
1667
1719
  state.thread.instance
@@ -1795,6 +1847,7 @@ var init_LLMRequest = __esm({
1795
1847
  }
1796
1848
  };
1797
1849
  response._provider = provider;
1850
+ response._viaPlatform = viaPlatform;
1798
1851
  response._providerResponseId = providerResponseId;
1799
1852
  response._aggregate_response = {
1800
1853
  id: responseId,
@@ -1872,12 +1925,12 @@ var init_LLMRequest = __esm({
1872
1925
  try {
1873
1926
  const toolsCalled = response.tool_calls ? JSON.stringify(response.tool_calls.map((tc) => tc.function.name)) : null;
1874
1927
  const providerName = response._provider?.name;
1875
- const standardAgentsRouterUsed = providerName === "platform";
1928
+ const standardAgentsRouterUsed = response._viaPlatform === true || providerName === "platform";
1876
1929
  const resolvedPricing = resolveModelPricing(modelDef, providerName);
1877
1930
  const calculatedCost = calculateUsageCost(response.usage, resolvedPricing);
1878
1931
  const providerReportedCost = typeof response.usage.cost === "number" ? response.usage.cost : typeof response.usage.cost === "string" ? parseFloat(response.usage.cost) : null;
1879
1932
  const cost_total = providerReportedCost != null && !Number.isNaN(providerReportedCost) ? providerReportedCost : calculatedCost?.costTotal ?? null;
1880
- const actualProvider = response.usage.provider;
1933
+ const actualProvider = normalizeProviderMetadataValue(response.usage.provider) ?? null;
1881
1934
  const aggregateResponse = response._aggregate_response;
1882
1935
  const responseBody = aggregateResponse ? JSON.stringify(aggregateResponse) : JSON.stringify(response);
1883
1936
  const providerTools = [];
@@ -2305,8 +2358,13 @@ ${errorStack}` : ""}${errorDetails}`;
2305
2358
  }).catch((err) => {
2306
2359
  console.error("Async metadata fetch failed (non-fatal):", err);
2307
2360
  });
2308
- state.rootState.pendingMetadataPromises = state.rootState.pendingMetadataPromises || [];
2309
- 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
+ }
2310
2368
  }
2311
2369
  /**
2312
2370
  * Update log record with async metadata from provider.
@@ -2316,9 +2374,12 @@ ${errorStack}` : ""}${errorDetails}`;
2316
2374
  const updates = [];
2317
2375
  const values = [];
2318
2376
  let paramIndex = 1;
2319
- if (metadata.actual_provider !== void 0) {
2377
+ const actualProvider = normalizeProviderMetadataValue(
2378
+ metadata.actual_provider ?? metadata.actualProvider ?? metadata.providerName
2379
+ );
2380
+ if (actualProvider !== void 0) {
2320
2381
  updates.push(`actual_provider = ?${paramIndex++}`);
2321
- values.push(metadata.actual_provider);
2382
+ values.push(actualProvider);
2322
2383
  }
2323
2384
  if (metadata.generation_cost !== void 0) {
2324
2385
  updates.push(`cost_total = ?${paramIndex++}`);
@@ -2336,7 +2397,7 @@ ${errorStack}` : ""}${errorDetails}`;
2336
2397
  log_id: logId,
2337
2398
  data: {
2338
2399
  id: logId,
2339
- ...metadata.actual_provider !== void 0 && { actual_provider: metadata.actual_provider },
2400
+ ...actualProvider !== void 0 && { actual_provider: actualProvider },
2340
2401
  ...metadata.generation_cost !== void 0 && { cost_total: metadata.generation_cost }
2341
2402
  }
2342
2403
  });
@@ -3062,6 +3123,11 @@ var init_ToolExecutor = __esm({
3062
3123
  state.sequence.queue.push(...toolCalls);
3063
3124
  state.sequence.isHandling = true;
3064
3125
  while (state.sequence.queue.length > 0) {
3126
+ if (state.stopped) {
3127
+ state.sequence.queue = [];
3128
+ state.sequence.isHandling = false;
3129
+ break;
3130
+ }
3065
3131
  if (await state.thread.instance.shouldStop() || state.abortController?.signal.aborted) {
3066
3132
  state.sequence.queue = [];
3067
3133
  state.sequence.isHandling = false;
@@ -3090,7 +3156,17 @@ var init_ToolExecutor = __esm({
3090
3156
  });
3091
3157
  continue;
3092
3158
  }
3093
- 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
+ }
3094
3170
  if (result.status === "error") {
3095
3171
  const error = new Error(result.error || "Tool execution failed");
3096
3172
  if (result.stack) {
@@ -4959,6 +5035,11 @@ ${attachmentPaths}`;
4959
5035
  }
4960
5036
  }
4961
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;
4962
5043
  let message = {
4963
5044
  id: messageId,
4964
5045
  role: "tool",
@@ -4972,15 +5053,20 @@ ${attachmentPaths}`;
4972
5053
  parent_id: state.parentMessageId || null,
4973
5054
  depth: state.depth,
4974
5055
  attachments: attachmentRefs && attachmentRefs.length > 0 ? JSON.stringify(attachmentRefs) : null,
4975
- 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
4976
5062
  };
4977
5063
  message = await FlowEngine2.runBeforeCreateMessageHook(state, message);
4978
5064
  message.created_at = Date.now() * TIMESTAMP_MULTIPLIER;
4979
5065
  const currentPrompt = state.promptPath.length > 0 ? state.promptPath[state.promptPath.length - 1] : null;
4980
5066
  await state.storage.sql.exec(
4981
5067
  `
4982
- INSERT INTO messages (id, role, content, name, tool_call_id, created_at, tool_status, parent_id, depth, attachments, subagent_id, prompt)
4983
- 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)
4984
5070
  `,
4985
5071
  message.id,
4986
5072
  message.role,
@@ -4993,7 +5079,8 @@ ${attachmentPaths}`;
4993
5079
  message.depth,
4994
5080
  message.attachments,
4995
5081
  message.subagent_id ?? null,
4996
- currentPrompt
5082
+ currentPrompt,
5083
+ message.metadata ? JSON.stringify(message.metadata) : null
4997
5084
  );
4998
5085
  await FlowEngine2.runAfterCreateMessageHook(state, message);
4999
5086
  const parentLogId = state.currentLogId || null;
@@ -5058,6 +5145,23 @@ ${attachmentPaths}`;
5058
5145
  await this.logError(state, error, `storeToolResult:${call.function.name}`);
5059
5146
  }
5060
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
+ }
5061
5165
  static async createSilentGeneratedAssetPathMessagesFromToolResult(state, sourceToolMessage, refs, enabled) {
5062
5166
  if (!enabled || !refs || refs.length === 0) {
5063
5167
  return;
@@ -5519,6 +5623,8 @@ var init_FlowEngine = __esm({
5519
5623
  stoppedBy: state.stoppedBy,
5520
5624
  stopReason: state.stopReason,
5521
5625
  stopReasonCode: state.stopReasonCode,
5626
+ sessionStopped: state.sessionStopped,
5627
+ sessionStopResult: state.sessionStopResult,
5522
5628
  stepCount: state.stepCount,
5523
5629
  stream: state.stream.httpStream
5524
5630
  };
@@ -5718,6 +5824,8 @@ var init_FlowEngine = __esm({
5718
5824
  stoppedBy: stateInput.stoppedBy,
5719
5825
  stopReason: stateInput.stopReason,
5720
5826
  stopReasonCode: stateInput.stopReasonCode,
5827
+ sessionStopped: stateInput.sessionStopped,
5828
+ sessionStopResult: stateInput.sessionStopResult,
5721
5829
  messageHistory: [],
5722
5830
  sequence: stateInput.sequence || {
5723
5831
  queue: [],
@@ -5977,7 +6085,7 @@ var init_FlowEngine = __esm({
5977
6085
  max_session_turns: newAgentDef.maxSessionTurns ?? null,
5978
6086
  side_a_label: newAgentDef.sideA?.label ?? null,
5979
6087
  side_a_agent_prompt: newAgentDef.sideA?.prompt ?? null,
5980
- side_a_stop_on_response: newAgentDef.sideA?.stopOnResponse ?? false,
6088
+ side_a_stop_on_response: newAgentDef.sideA?.stopOnResponse ?? true,
5981
6089
  side_a_stop_tool: newAgentDef.sideA?.stopTool ?? null,
5982
6090
  side_a_stop_tool_response_property: newAgentDef.sideA?.stopToolResponseProperty ?? null,
5983
6091
  side_a_max_steps: newAgentDef.sideA?.maxSteps ?? null,
@@ -5992,7 +6100,7 @@ var init_FlowEngine = __esm({
5992
6100
  side_a_session_status_attachments_property: sideASession.status.attachmentsProperty,
5993
6101
  side_b_label: newAgentDef.sideB?.label ?? null,
5994
6102
  side_b_agent_prompt: newAgentDef.sideB?.prompt ?? null,
5995
- side_b_stop_on_response: newAgentDef.sideB?.stopOnResponse ?? false,
6103
+ side_b_stop_on_response: newAgentDef.sideB?.stopOnResponse ?? true,
5996
6104
  side_b_stop_tool: newAgentDef.sideB?.stopTool ?? null,
5997
6105
  side_b_stop_tool_response_property: newAgentDef.sideB?.stopToolResponseProperty ?? null,
5998
6106
  side_b_max_steps: newAgentDef.sideB?.maxSteps ?? null,
@@ -11687,34 +11795,79 @@ var init_ThreadStateImpl = __esm({
11687
11795
  this._metadata.tenvs = nextEnv;
11688
11796
  }
11689
11797
  async notifyParent(content) {
11690
- if (!content || !content.trim()) {
11798
+ const trimmedContent = content?.trim();
11799
+ if (!trimmedContent) {
11691
11800
  throw new Error("notifyParent requires non-empty content");
11692
11801
  }
11693
- const { parentThreadId, parentStub } = await this._getParentThreadStub();
11694
- if (typeof parentStub.queueMessage !== "function") {
11695
- 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
+ });
11696
11823
  }
11697
- await parentStub.queueMessage(parentThreadId, {
11698
- role: "user",
11699
- content: content.trim(),
11700
- silent: true,
11701
- metadata: { subagent_id: this.threadId }
11702
- });
11703
11824
  }
11704
11825
  async setStatus(status) {
11705
11826
  if (!status || !status.trim()) {
11706
11827
  throw new Error("setStatus requires non-empty status");
11707
11828
  }
11708
- const { parentThreadId, parentStub } = await this._getParentThreadStub();
11709
- if (typeof parentStub.updateChildRegistryStatus !== "function") {
11710
- 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;
11711
11832
  }
11712
- await parentStub.updateChildRegistryStatus(
11713
- parentThreadId,
11833
+ await parentTarget.parentStub.updateChildRegistryStatus(
11834
+ parentTarget.parentThreadId,
11714
11835
  this.threadId,
11715
11836
  status.trim()
11716
11837
  );
11717
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
+ }
11718
11871
  async getChildThread(referenceId) {
11719
11872
  const agentBuilder = this._getAgentBuilderStub();
11720
11873
  const child = await agentBuilder.getThread(referenceId);
@@ -12127,19 +12280,47 @@ var init_ThreadStateImpl = __esm({
12127
12280
  return Object.keys(normalized).length > 0 ? normalized : null;
12128
12281
  }
12129
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() {
12130
12290
  const metadataParent = typeof this._metadata.parent === "string" && this._metadata.parent.trim().length > 0 ? this._metadata.parent.trim() : null;
12131
12291
  if (metadataParent) {
12132
12292
  return metadataParent;
12133
12293
  }
12134
- const agentBuilder = this._getAgentBuilderStub();
12135
- 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
+ }
12136
12301
  const parent = thread && typeof thread.parent === "string" && thread.parent.trim().length > 0 ? thread.parent.trim() : null;
12137
- if (!parent) {
12138
- throw new Error(`Thread ${this.threadId} has no parent thread`);
12302
+ if (parent) {
12303
+ this._metadata.parent = parent;
12139
12304
  }
12140
- this._metadata.parent = parent;
12141
12305
  return parent;
12142
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
+ }
12143
12324
  async _getParentThreadStub() {
12144
12325
  const parentThreadId = await this._getParentThreadId();
12145
12326
  const durableId = this._threadInstance.env.AGENT_BUILDER_THREAD.idFromName(parentThreadId);
@@ -14208,6 +14389,7 @@ function createEndpointThreadStateBridge(state) {
14208
14389
  setEnv: (propertyName, value, options) => state.setEnv(propertyName, value, options),
14209
14390
  notifyParent: (content) => state.notifyParent(content),
14210
14391
  setStatus: (status) => state.setStatus(status),
14392
+ stopSession: (options) => state.stopSession(options),
14211
14393
  queueTool: (toolName, args) => state.queueTool(toolName, args),
14212
14394
  invokeTool: (toolName, args) => state.invokeTool(toolName, args),
14213
14395
  scheduleEffect: (name, args, delay) => state.scheduleEffect(name, args, delay),
@@ -15427,6 +15609,15 @@ var DurableThread = class extends DurableObject {
15427
15609
  const terminalToolNames = await this.getTerminalSessionToolNames(agentId);
15428
15610
  const sessionFailToolNames = terminalToolNames.fail;
15429
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
+ }
15430
15621
  const latestInRunCursor = await this.ctx.storage.sql.exec(
15431
15622
  `
15432
15623
  SELECT role, name, content, tool_status, attachments
@@ -16006,27 +16197,30 @@ ${paths.join("\n")}`;
16006
16197
  if (!parentThreadId || !flowResult.stopped) {
16007
16198
  return;
16008
16199
  }
16200
+ const sessionStopped = flowResult.sessionStopped === true;
16009
16201
  const terminalToolNames = await this.getTerminalSessionToolNames(agentName);
16010
16202
  const sessionFailToolNames = terminalToolNames.fail;
16011
16203
  const hasTerminalSessionTools = terminalToolNames.all.size > 0;
16012
- const latestTerminalMessage = hasTerminalSessionTools ? await this.getLatestTerminalSessionMessageAfter(
16204
+ const latestTerminalMessage = !sessionStopped && hasTerminalSessionTools ? await this.getLatestTerminalSessionMessageAfter(
16013
16205
  previousTopLevelMessageId,
16014
16206
  terminalToolNames.all
16015
16207
  ) : null;
16016
- const latest = latestTerminalMessage ?? await this.getLatestTopLevelMessage();
16017
- if (!latest && !hasTerminalSessionTools) {
16208
+ const latest = sessionStopped ? null : latestTerminalMessage ?? await this.getLatestTopLevelMessage();
16209
+ if (!sessionStopped && !latest && !hasTerminalSessionTools) {
16018
16210
  return;
16019
16211
  }
16020
- if (latest && latest.id === previousTopLevelMessageId && !hasTerminalSessionTools) {
16212
+ if (!sessionStopped && latest && latest.id === previousTopLevelMessageId && !hasTerminalSessionTools) {
16021
16213
  return;
16022
16214
  }
16023
16215
  const isTerminalSessionMessage = !!latest && latest.role === "tool" && !!latest.name && terminalToolNames.all.has(latest.name);
16024
16216
  const isFailSessionTerminal = isTerminalSessionMessage && !!latest.name && sessionFailToolNames.has(latest.name);
16025
16217
  const isSuccessfulTerminalSessionMessage = isTerminalSessionMessage && latest?.tool_status !== "error";
16026
- const isFailureWithoutTerminalMessage = hasTerminalSessionTools && !isSuccessfulTerminalSessionMessage;
16218
+ const isFailureWithoutTerminalMessage = !sessionStopped && hasTerminalSessionTools && !isSuccessfulTerminalSessionMessage;
16027
16219
  const isErrorTerminal = latest?.tool_status === "error" || isFailSessionTerminal || isFailureWithoutTerminalMessage;
16028
16220
  let refs = [];
16029
- if (hasTerminalSessionTools) {
16221
+ if (sessionStopped) {
16222
+ refs = [];
16223
+ } else if (hasTerminalSessionTools) {
16030
16224
  refs = this.parseAttachmentRefsJson(latestTerminalMessage?.attachments ?? null) ?? [];
16031
16225
  } else if (latest?.attachments) {
16032
16226
  refs = this.parseAttachmentRefsJson(latest.attachments) ?? [];
@@ -16044,7 +16238,7 @@ ${paths.join("\n")}`;
16044
16238
  parentStub,
16045
16239
  `from-subagent-${threadId}`
16046
16240
  );
16047
- 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.");
16048
16242
  const attachmentSummary = this.formatSubagentAttachmentPathSummary(copiedRefs);
16049
16243
  const messageContent = isErrorTerminal ? `Subagent (reference: ${threadId}) has reported a failure:
16050
16244
 
@@ -16060,7 +16254,7 @@ ${resultContent}${attachmentSummary}`;
16060
16254
  metadata: { subagent_id: threadId }
16061
16255
  });
16062
16256
  }
16063
- const terminalChildRun = hasTerminalSessionTools;
16257
+ const terminalChildRun = sessionStopped || hasTerminalSessionTools;
16064
16258
  await parentStub.updateChildRegistryStatus(
16065
16259
  parentThreadId,
16066
16260
  threadId,
@@ -17473,7 +17667,7 @@ ${resultContent}${attachmentSummary}`;
17473
17667
  max_session_turns: agentDef.maxSessionTurns,
17474
17668
  side_a_label: agentDef.sideA?.label,
17475
17669
  side_a_agent_prompt: qualifyPromptName(agentDef.sideA?.prompt),
17476
- side_a_stop_on_response: agentDef.sideA?.stopOnResponse ?? false,
17670
+ side_a_stop_on_response: agentDef.sideA?.stopOnResponse ?? true,
17477
17671
  side_a_stop_tool: agentDef.sideA?.stopTool,
17478
17672
  side_a_stop_tool_response_property: agentDef.sideA?.stopToolResponseProperty,
17479
17673
  side_a_max_steps: agentDef.sideA?.maxSteps,
@@ -17488,7 +17682,7 @@ ${resultContent}${attachmentSummary}`;
17488
17682
  side_a_session_status_attachments_property: sideASession.status.attachmentsProperty,
17489
17683
  side_b_label: agentDef.sideB?.label,
17490
17684
  side_b_agent_prompt: qualifyPromptName(agentDef.sideB?.prompt),
17491
- side_b_stop_on_response: agentDef.sideB?.stopOnResponse ?? false,
17685
+ side_b_stop_on_response: agentDef.sideB?.stopOnResponse ?? true,
17492
17686
  side_b_stop_tool: agentDef.sideB?.stopTool,
17493
17687
  side_b_stop_tool_response_property: agentDef.sideB?.stopToolResponseProperty,
17494
17688
  side_b_max_steps: agentDef.sideB?.maxSteps,
@@ -17666,7 +17860,7 @@ ${resultContent}${attachmentSummary}`;
17666
17860
  max_session_turns: agentDef.maxSessionTurns,
17667
17861
  side_a_label: agentDef.sideA?.label,
17668
17862
  side_a_agent_prompt: qualifyPromptName(agentDef.sideA?.prompt),
17669
- side_a_stop_on_response: agentDef.sideA?.stopOnResponse ?? false,
17863
+ side_a_stop_on_response: agentDef.sideA?.stopOnResponse ?? true,
17670
17864
  side_a_stop_tool: agentDef.sideA?.stopTool,
17671
17865
  side_a_stop_tool_response_property: agentDef.sideA?.stopToolResponseProperty,
17672
17866
  side_a_max_steps: agentDef.sideA?.maxSteps,
@@ -17681,7 +17875,7 @@ ${resultContent}${attachmentSummary}`;
17681
17875
  side_a_session_status_attachments_property: sideASession.status.attachmentsProperty,
17682
17876
  side_b_label: agentDef.sideB?.label,
17683
17877
  side_b_agent_prompt: qualifyPromptName(agentDef.sideB?.prompt),
17684
- side_b_stop_on_response: agentDef.sideB?.stopOnResponse ?? false,
17878
+ side_b_stop_on_response: agentDef.sideB?.stopOnResponse ?? true,
17685
17879
  side_b_stop_tool: agentDef.sideB?.stopTool,
17686
17880
  side_b_stop_tool_response_property: agentDef.sideB?.stopToolResponseProperty,
17687
17881
  side_b_max_steps: agentDef.sideB?.maxSteps,
@@ -17812,7 +18006,7 @@ ${resultContent}${attachmentSummary}`;
17812
18006
  max_session_turns: agentDef.maxSessionTurns,
17813
18007
  side_a_label: agentDef.sideA?.label,
17814
18008
  side_a_agent_prompt: qualifyPromptName(agentDef.sideA?.prompt),
17815
- side_a_stop_on_response: agentDef.sideA?.stopOnResponse ?? false,
18009
+ side_a_stop_on_response: agentDef.sideA?.stopOnResponse ?? true,
17816
18010
  side_a_stop_tool: agentDef.sideA?.stopTool,
17817
18011
  side_a_stop_tool_response_property: agentDef.sideA?.stopToolResponseProperty,
17818
18012
  side_a_max_steps: agentDef.sideA?.maxSteps,
@@ -17827,7 +18021,7 @@ ${resultContent}${attachmentSummary}`;
17827
18021
  side_a_session_status_attachments_property: sideASession.status.attachmentsProperty,
17828
18022
  side_b_label: agentDef.sideB?.label,
17829
18023
  side_b_agent_prompt: qualifyPromptName(agentDef.sideB?.prompt),
17830
- side_b_stop_on_response: agentDef.sideB?.stopOnResponse ?? false,
18024
+ side_b_stop_on_response: agentDef.sideB?.stopOnResponse ?? true,
17831
18025
  side_b_stop_tool: agentDef.sideB?.stopTool,
17832
18026
  side_b_stop_tool_response_property: agentDef.sideB?.stopToolResponseProperty,
17833
18027
  side_b_max_steps: agentDef.sideB?.maxSteps,